【Linux 16】进程间通信的方式 - 共享内存

文章目录

  • 🌈 一、共享内存概述
    • ⭐ 1. 什么是共享内存
    • ⭐ 2. 如何实现共享内存
    • ⭐ 3. 操作系统允许存在多个共享内存
    • ⭐ 4. 操作系统如何管理共享内存
    • ⭐ 5. 获取共享内存的唯一标识符 key
    • ⭐ 6. 为什么要由用户提供 key
  • 🌈 二、查看共享内存
    • ⭐ 1. 使用 ipcs -m 查看
    • ⭐ 2. key 与 shmid 的区别
  • 🌈 三、共享内存函数
    • ⭐ 1. shmget 创建共享内存
    • ⭐ 2. shmat 将共享内存段连接到进程地址空间
    • ⭐ 3. shmdt 将共享内存段与当前进程脱离
    • ⭐ 4. shmctl 控制共享内存
  • 🌈 四、使用共享内存
  • 🌈 五、释放共享内存
  • 🌈 六、共享内存特点

🌈 一、共享内存概述

⭐ 1. 什么是共享内存

  • 共享内存是 OS 内部单独设计出来专门用于进程间通信的模块,是最快的进程间通信的形式。
  • 它允许两个或多个进程访问同一块物理内存区域,从而实现数据的快速交换。
  • 共享内存减少了数据拷贝的开销,并且数据的传输速度非常快,因为数据直接在内存中传输,而不需要经过内核的干预。

⭐ 2. 如何实现共享内存

  1. 申请空间:在物理内存当中申请一块内存空间。
  2. 建立映射:将这块内存通过页表映射到想要进行通信的进程的进程地址空间共享区中,然后返回共享区的起始地址即可。
    • 至此这些进程便看到了同一份物理内存,这块物理内存就叫做共享内存,多个进程就可以通过共享内存间接的进行通信。

在这里插入图片描述

⭐ 3. 操作系统允许存在多个共享内存

  • 不是所有的进程都是使用同一块内存进行通信的,还有其他多组进程可能要使用不同的共享内存进行通信。
  • 如果 OS 不允许存在多个共享内存的话,那么就意味着同一时刻只有一组进程能够通信,这显然不合理。
  • 因此,OS 允许多个共享内存被创建,即允许同时存在多个共享内存,那么 OS 就要管理这些共享内存

⭐ 4. 操作系统如何管理共享内存

  • 只知道在内存中开辟空间然后映射到地址空间可没法管理共享内存。
  • OS 需要知道一块共享内存的使用情况,才能将共享内存管理起来。
    • 如:OS 需要知道共享内存的大小、什么时候开辟的、谁申请开辟的、有多少进程直接和该共享内存关联等信息。
  • 这些 OS 需要知道的关于共享内存的信息都存储在 shmid_ds 结构体当中。

1. 共享内存的数据结构

  • 共享内存除了在内存当中真正开辟空间之外,OS 还要为共享内存维护相关的内核数据结构。
  • 往后对共享内存的管理就变成了对该数据结构对象的管理。
struct shmid_ds
{struct ipc_perm shm_perm;    /* operation perms */int shm_segsz;               /* size of segment (bytes) */__kernel_time_t shm_atime;   /* last attach time */__kernel_time_t shm_dtime;   /* last detach time */__kernel_time_t shm_ctime;   /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch;   /* no. of current attaches */unsigned short shm_unused;   /* compatibility */void *shm_unused2;           /* ditto - used by DIPC */void *shm_unused3;           /* unused */
};

2. 用 key 值标识唯一的共享内存

  • 当申请了一块共享内存后,为了让要实现通信的进程能够看到同一块共享内存,因此每一个共享内存被申请时都有一个 key 值,这个 key 值用于标识系统中唯一的一块共享内存。
  • 每个共享内存的 key 值存储在 ipc_perm 结构体中,其中 ipc_perm 结构体的定义如下:
struct ipc_perm
{__kernel_key_t key;__kernel_uid_t uid;__kernel_gid_t gid;__kernel_uid_t cuid;__kernel_gid_t cgid;__kernel_mode_t mode;unsigned short seq;
};

⭐ 5. 获取共享内存的唯一标识符 key

  • 理论上 key 这玩意可以随便写一个,但这样容易和系统中已有的发生冲突。
  • 因此就需要使用系统提供的 ftok 算法函数来形成唯一的 key 值
    • 使用 ftok 函数形成的 key 值大概率不会发生冲突,但依旧有可能,此时再执行一次 ftok 即可 (更换参数)。

1. ftok 函数原型

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
  • 功能 : 将提供的 pathname 和 proj_id 参数通过函数内部的算法转换成 key。
  • 参数 : pathname - 路径名;proj_id - 项目 id,这玩意可以随便写。
  • 返回 : 如果转换成功则返回转换后的唯一的 key 值,转换失败则返回 -1。

2. ftok 函数用例

  • 如果给 ftok 提供的参数相同,那么通过同一个算法获取到的 key 肯定相同。
  • 多个进程只要提交同一份参数给 ftok,那么即可根据获取的同一个 key 值看到同一个共享内存空间。
    • 当前有 server.cpp 和 client.cpp 两个文件,让这两个文件提供给 ftok 函数的参数一致,看看所形成的 key 是否相同。
// 文件: server.cpp client.cpp
// 下面的代码除了打印部分不一样,其余部分两个文件都一致
#include <string>
#include <cassert>
#include <iostream>
#include <sys/ipc.h>
#include <sys/types.h>using std::cout;
using std::endl;
using std::string;const string pathname = "/home/yxc/Linux/2024_04_05_进程间通信/3.共享内存";
const int proj_id = 0x11223344;int main()
{key_t key = ftok(pathname.c_str(), proj_id);assert(key >= 0);cout << "server.cpp key : " << key << endl;// 这行代码只存在 server.cpp 文件中cout << "client.cpp key : " << key << endl;// 这行代码只存在 client.cpp 文件中return 0;
}

在这里插入图片描述

⭐ 6. 为什么要由用户提供 key

  • OS 明明就可以自己生成一个随机数作为唯一的 key 值,然后作为共享内存结构体中的 key 值,为什么还要由用户自己提供 key 呢?

为了让通信双方都能看到唯一的 key 值

  • 在系统层面上,key 值只有 OS 和创建共享内存的进程知道。
  • 如果 key 是由操作系统形成的,此时创建共享内存的进程还能通过 shmid 知道具体是哪个共享内存。但是其他使用共享内存的进程不知道 key 值,就没办法知道 shmid 值,就无法访和目标进程获取同一块共享内存了。

🌈 二、查看共享内存

⭐ 1. 使用 ipcs -m 查看

  • 可在命令行中输入 ipcs -m 指令查看当前的共享内存信息。

在这里插入图片描述

参数说明
key共享内存段的键值 key
shmid共享内存段的应用层标识符 id
owner该贡献内存的拥有者
perms共享内存所持有的权限
bytes共享内存段的大小,以字节为单位
nattch当前与该共享内存关联的进程数
status共享内存的状态

⭐ 2. key 与 shmid 的区别

  • key:不在应用层使用,只用来在内核中标识共享内存的唯一性。在创建共享内存的时候会被设置进共享内存的数据结构中。
  • shmid:使用 shmid 来操作共享内存,是方便我们进行编程而存在的。通过这个标识符,进程可以访问和操作共享内存段。

🌈 三、共享内存函数

  • 前提知识:共享内存 (Shared Memory) 的简写是 shm,以下所有的共享内存函数都是基于 shm 命名的。

⭐ 1. shmget 创建共享内存

1. 函数原型

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
  • 功能
    • 创建一块共享内存并将 key 填充进共享内存的结构体中。
  • 参数
    • key:用来唯一标识准备开辟的一块共享内存;
    • size:想开辟的共享内存的大小;
    • shmflg:创建共享内存的方式。
  • 返回
    • 如果共享内存创建成功,返回一个共享内存标识符;失败则返回 - 1,并设置错误码。

2. shmflg 参数的可选项

选项说明
IPC_CREAT如果共享内存不存在,就创建共享内存;如果存在,则获取共享内存。主要用于获取共享内存。
IPC_CREAT | IPC_EXCL如果共享内存不存在则创建,如果存在则出错返回。主要用于创建全新的共享内存。

3. 函数用例

// 文件: server.cpp
#include <string>
#include <cstring>
#include <cassert>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using std::cout;
using std::endl;
using std::string;const string pathname = "/home/yxc/Linux/2024_04_05_进程间通信/3.共享内存";
const int proj_id = 0x11223344;
const int size = 4096;// 将创建 key 和判断是否创建成功封装在一起
key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);assert(key >= 0);return key;
}int main()
{// 创建共享内存的键值 keykey_t key = get_key();// 创建共享内存      int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666);assert(shmid >= 0);cout << "shmid:" << shmid << endl;return 0;
}

在这里插入图片描述

⭐ 2. shmat 将共享内存段连接到进程地址空间

1. 函数原型

#include <sys/shm.h>
#include <sys/types.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 功能
    • 哪个进程调用该函数,就会将指定的共享内存链接到该进程的地址空间。
  • 参数
    • shmid:共享内存标识符;
    • shmaddr:将共享内存连接到进程地址空间中指定的一个起始地址,通常设置为空 (NULL / nullptr),表示让 OS 自己决定一个合适的地址位置;
    • shmflg:进程连接内存时的可选参数,通常设置为 0。
  • 返回
    • 成功则返回共享内存映射到进程地址空间中的起始地址;失败则返回 (void*)-1 并设置错误码。

2. shmflg 参数的可选项

选项说明
SHM_RDONLY关联共享内存后只进行读取操作
SHM_RND若 shmaddr 不为空,则连接的地址会自动向下调整为 SHMLBA 的整数倍。
0默认为读写权限

3. 函数用例

#include <string>
#include <cassert>
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using std::cout;
using std::endl;
using std::string;const string pathname = "/home/yxc/Linux/2024_04_05_进程间通信/3.共享内存";
const int proj_id = 0x11223344;
const int size = 4096;// 将创建 key 和判断是否创建成功封装在一起
key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);assert(key >= 0);return key;
}// 将创建共享内存和判断是否创建成功封装在一起
int create_shm(key_t key)
{int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666);assert(shmid >= 0);return shmid;
}int main()
{key_t key = get_key();                      // 算出共享内存的唯一标识符int shmid = create_shm(key);                // 创建共享内存cout << "连接开始" << endl;char *s = (char *)shmat(shmid, nullptr, 0); // 将共享内存与当前进程连接sleep(5);cout << "连接结束" << endl;return 0;
}
  • nattch 表示当前有多少个进程与该共享内存关联,观察进程在启动和退出时 nattch 的变化。
    • 注:在进程终止时,会自动将共享内存与当前进程脱离。

在这里插入图片描述

⭐ 3. shmdt 将共享内存段与当前进程脱离

1. 函数原型

#include <sys/shm.h>
#include <sys/types.h>int shmdt(const void *shmaddr);
  • 功能:手动取消共享内存与进程的虚拟地址空间的映射关系。
  • 参数:shmaddr 关联共享内存时获得的共享内存映射到地址空间的起始地址。
  • 返回:脱离成功则返回 0,失败则返回 -1 并设置错误码。

2. 函数用例

#include <string>
#include <cassert>
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using std::cout;
using std::endl;
using std::string;const string pathname = "/home/yxc/Linux/2024_04_05_进程间通信/3.共享内存";
const int proj_id = 0x11223344;
const int size = 4096;// 将创建 key 和判断是否创建成功封装在一起
key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);assert(key >= 0);return key;
}// 将创建共享内存和判断是否创建成功封装在一起
int create_shm(key_t key)
{int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666);assert(shmid >= 0);return shmid;
}int main()
{key_t key = get_key();                      // 算出共享内存的唯一标识符int shmid = create_shm(key);                // 创建共享内存cout << "连接开始" << endl;char *s = (char *)shmat(shmid, nullptr, 0); // 将共享内存与当前进程连接sleep(2);shmdt(s);                                   // 取消关联cout << "连接结束" << endl;sleep(5);return 0;
}

在这里插入图片描述

⭐ 4. shmctl 控制共享内存

1. 函数原型

#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 功能
    • 实现对共享内存的删改查。
  • 参数
    • shmid:由 shmget 函数返回的共享内存标识符,指定控制的是哪个共享内存。
    • cmd:准备采取什么方式控制共享内存。
    • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构,如果没需求可设置为空。
  • 返回
    • 控制成功则返回 0,失败则返回 -1 并设置错误码。

2. cmd 参数的可选项

选项说明
查:IPC_STAT获取共享内存的当前关联值,此时参数 buf 作为输出型参数
改:IPC_SET在进程有足够权限的前提下,将共享内存的当前关联值设置为 buf 所指的数据结构中的值
删:IPC_RMID删除共享内存

3. 函数用例

  • 这里只演示删除 IPC_RMID 操作。
#include <string>
#include <cassert>
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using std::cout;
using std::endl;
using std::string;const string pathname = "/home/yxc/Linux/2024_04_05_进程间通信/3.共享内存";
const int proj_id = 0x11223344;
const int size = 4096;// 将创建 key 和判断是否创建成功封装在一起
key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);assert(key >= 0);return key;
}// 将创建共享内存和判断是否创建成功封装在一起
int create_shm(key_t key)
{int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666);assert(shmid >= 0);return shmid;
}int main()
{key_t key = get_key();                      // 算出共享内存的唯一标识符int shmid = create_shm(key);                // 创建共享内存cout << "连接开始" << endl;char *s = (char *)shmat(shmid, nullptr, 0); // 将共享内存与当前进程连接sleep(2);shmdt(s);                                   // 取消关联cout << "连接结束" << endl;sleep(5);shmctl(shmid, IPC_RMID, nullptr);       	// 删除共享内存cout << "共享内存已被删除" << endl;return 0;
}

在这里插入图片描述

🌈 四、使用共享内存

  • 实现客户端向共享内存中依次写入 a ~ z 的 26 个字母,然后服务端每次都读取共享内存中的所有数据。

1. 代码展示

  1. command.hpp 公共文件:包含客户端和服务端两个进程所需要的公共代码。
#pragma once#include <string>
#include <cassert>
#include <unistd.h>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using std::cout;
using std::endl;
using std::string;const string pathname = "/home/yxc/Linux/2024_04_05_进程间通信/3.共享内存";
const int proj_id = 0x11223344;
const int size = 4096; // 准备创建的共享内存的大小// 获取唯一的 key
key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);assert(key >= 0);return key;
}int create_shm_helper(key_t key, int flag)
{int shmid = shmget(key, size, flag);assert(shmid >= 0);return shmid;
}// 创建共享内存
int create_shm(key_t key)
{return create_shm_helper(key, IPC_CREAT | IPC_EXCL | 0644);
}// 获取共享内存
int get_shm(key_t key)
{return create_shm_helper(key, IPC_CREAT);
}
  1. server.cpp 服务端文件:用来创建共享内存,从客户端获取数据并打印。
#include "command.hpp"int main()
{cout << "开始获取键值 key" << endl;key_t key = get_key();                      // 获取键值 keycout << "key: " << key << endl;cout << "开始创建共享内存" <<endl;int shmid = create_shm(key);                // 创建共享内存cout << "shmid: " << shmid << endl;cout << "开始将共享内存映射到进程地址空间" << endl;char* s = (char*)shmat(shmid, nullptr, 0);  // 关联共享内存// 通信代码while (true){// 直接读取cout << "共享内存的内容: " << s << endl;sleep(1);}cout << "开始将共享内存从进程地址空间分离" << endl;shmdt(s);                                   // 脱离共享内存cout << "开始删除共享内存" << endl;shmctl(shmid, IPC_RMID, nullptr);           // 释放共享内存return 0;
}
  1. client.cpp 客户端文件:用来获取共享内存,往共享内存输入数据。
#include "command.hpp"int main()
{key_t key = get_key();                      // 获取键值 keyint shmid = get_shm(key);                   // 获取共享内存char *s = (char *)shmat(shmid, nullptr, 0); // 关联共享内存cout << "关联共享内存成功" << endl;// 通信代码for (char ch = 'a'; ch <= 'z'; ch++){s[ch - 'a'] = ch;                       // 往共享内存写入cout << "write: " << ch << " done" << endl;sleep(1);                               // 每隔一秒向共享内存写入一次}shmdt(s);                                   // 分离共享内存cout << "分离共享内存成功" << endl;return 0;
}

2. 结果展示

在这里插入图片描述

🌈 五、释放共享内存

1. 释放共享内存的步骤

  1. 取消映射:取消进程的虚拟地址空间和物理内存的映射关系,本质就是清空页表的问题。
  2. 释放内存:共享内存的生命周期是随内核的,曾经创建的共享内存不会随着进程的退出而释放,需要手动释放共享内存。

2. 释放共享内存的方法

  1. 在命令行使用指令释放共享内存:使用 ipcrm -m shmid 指令删除指定 shmid 的共享内存。

在这里插入图片描述

  1. 在代码中使用函数释放共享内存:可以使用 shmctl 函数释放共享内存,前面已经实现过,就不过多赘述。

🌈 六、共享内存特点

  1. 共享内存不提供同步机制,共享内存是直接裸露给所有的使用者的,需要注意共享内存的使用安全问题。
  2. 共享内存是所有进程间通信方式中最快的一种。
    • A / B 进程写进共享内存中的内容另一个进程立马就能看到,可有效减少拷贝 (至少减少了 2 次,写进程不用将数据拷贝到缓冲区,读进程不用从缓冲区拷贝)。
  3. 共享内存可以提供较大的空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/876801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP 协议的 time_wait 超时时间

优质博文&#xff1a;IT-BLOG-CN 灵感来源 Time_Wait 产生的时机 TCP四次挥手的流程 如上所知&#xff1a;客户端在收到服务端第三次FIN挥手后&#xff0c;就会进入TIME_WAIT状态&#xff0c;开启时长为2MSL的定时器。 【1】MSL是Maximum Segment Lifetime报文最大生存时间…

root 用户和权限

目录 1. 超级管理员 root 2. 切换用户 Switch User 2.1 普通用户切换到 root 用户 2.2 root 用户切换到普通用户 3. sudo 命令 3.1 配置认证 无论是 Windows&#xff0c;MacOS&#xff0c;Linux 均采用多用户的管理模式管理权限&#xff1b; 1. 超级管理员 root 在 Li…

2年社招冲击字节,一天三面斩获offer

在工作满两年的时间选择了求变&#xff0c;带着运气和实力以社招身份重新看今天的互联网环境&#xff0c;从结果看还是复合预期的。 整个面试的流程还挺快的。周中让招聘专员给投递了简历。问什么时候面试&#xff0c;申请了一个周日&#xff0c;直接安排三面。下周周中就开启…

C#中的wpf基础

在WPF中&#xff0c;Grid 是一种非常强大的布局控件&#xff0c;用于创建网格布局。它允许你将界面划分为行和列&#xff0c;并将控件放置在这些行和列中。 以下是一些关键点和示例&#xff0c;帮助你理解 WPF 中的 Grid&#xff1a; 基本属性 RowDefinitions&#xff1a;定义…

[MIT6.5840]MapReduce

MapReduce Lab 地址 https://pdos.csail.mit.edu/6.824/labs/lab-mr.html 论文地址 https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/mapreduce-osdi04.pdf 工作原理 简单来讲&#xff0c;MapReduce是一种分布式框架&#xff0c;可以用来处理…

windows 安装docker桌面版

下载 下载两个&#xff1a; git桌面版 docker desktop 启动docker 执行安装文件&#xff0c;启动 更新wsl2 假如报错&#xff0c;会提示失败原因。 win10会提示跳转到&#xff1a; https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#step-4—download-the-l…

从0到1,AI我来了- (4)AI图片识别的理论知识-II

上篇文章&#xff0c;我们理解了我们程序的神经网络设计&#xff0c;这篇我们继续&#xff0c;把训练迭代过程分析一下&#xff0c;完成这两篇文章&#xff0c;下面问题&#xff0c;应该能回答了。 一张图片&#xff0c;如何被计算机读懂&#xff1f;pytorch 封装的网络&#…

DP 整数拆分不同的二叉搜索树 DAY21

整数拆分&#xff1f; 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。示例 2: 输入: n 10 输…

全国区块链职业技能大赛样题第9套前端源码

后端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746050 前端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746216 智能合约+数据库表设计:https://blog.csdn.net/Qhx20040819/article/details/140746646 登录 ​ 用户管理

又一成就,Pencils Protocol单链 TVL 突破 3 亿美元

Pencils Protocol 是 Scroll 生态的原生项目&#xff0c;该项目以一站式收益聚合器和拍卖平台作为主要定位&#xff0c;在功能上&#xff0c;其集 Launchpad、资产统一聚合和分发、杠杆收益等功能于一体&#xff0c;旨在最大化用户的资产利用率。近日&#xff0c;Pencils Proto…

利用python自动化运维i脚本实现远程连接服务器并实现相应命令

目录 前言&#xff1a; 一.调用的python库介绍 二.在主机上安装好相应的库 2.1激活虚拟环境 三.代码实现以及解析 四.效果的实现 五.致谢 前言&#xff1a; 在当今快速发展的技术环境中&#xff0c;自动化运维已成为 IT 基础设施管理的关键组成部分。它不仅可以显著提…

Lua编程

文章目录 概述lua数据类型元表注意 闭包表现 实现 lua/c 接口编程skynet中调用层次虚拟栈C闭包注册表userdatalightuserdata 小结 概述 这次是skynet&#xff0c;需要一些lua/c相关的。写一篇博客&#xff0c;记录下。希望有所收获。 lua数据类型 boolean , number , string…

大模型算法面试题(十五)

本系列收纳各种大模型面试题及答案。 1、大模型LLM进行SFT如何对样本进行优化 大模型LLM&#xff08;Language Model&#xff0c;语言模型&#xff09;进行SFT&#xff08;Structured Fine-Tuning&#xff0c;结构化微调&#xff09;时&#xff0c;对样本的优化是提升模型性能…

Linux源码阅读笔记16-文件系统关联及字符设备操作

文件系统关联 设备文件都是由标准函数处理&#xff0c;类似普通文件。设备文件也是通过虚拟文件系统来管理的&#xff0c;和普通文件都是通过完全相同的接口访问的。 inode中设备文件的成员数据 虚拟文件系统每个文件都关联到一个inode&#xff0c;用于管理文件的属性。源码如…

【Go - context 速览,场景与用法】

作用 context字面意思上下文&#xff0c;用于关联管理上下文&#xff0c;具体有如下几个作用 取消信号传递&#xff1a;可以用来传递取消信号&#xff0c;让一个正在执行的函数知道它应该提前终止。超时控制&#xff1a;可以设定一个超时时间&#xff0c;自动取消超过执行时间…

Swift学习入门,新手小白看过来

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

(十三)Spring教程——依赖注入之工厂方法注入

1.工厂方法注入 工厂方法是在应用中被经常使用的设计模式&#xff0c;它也是控制反转和单例设计思想的主要实现方法。由于Spring IoC容器以框架的方式提供工厂方法的功能&#xff0c;并以透明的方式开放给开发者&#xff0c;所以很少需要手工编写基于工厂方法的类。正是因为工厂…

如何从网站获取表格数据

1.手动复制粘贴 最简单的方法是直接在网页上手动选择表格内容&#xff0c;然后复制粘贴到Excel或其他表格处理软件中。这种方法适用于表格较小且不经常更新的情况。 2.使用浏览器插件 有许多浏览器插件可以帮助从网页中提取表格数据&#xff0c;例如&#xff1a; -TableCapt…

SSRF过滤攻击

SSRF绕过&#xff1a; 靶场地址&#xff1a;重庆橙子科技SSRF靶场 这个是毫无过滤的直接读取&#xff0c;但是一般网站会设置有对SSRF的过滤&#xff0c;比如将IP地址过滤。 下面是常用的绕过方式&#xff1a; 1.环回地址绕过 http://127.0.0.1/flag.php http://017700…

相机怎么选(不推荐,只分析)

title: 相机怎么选 tags: [相机, 单反相机] categories: [其他, 相机] 最近准备购买&#xff0c;相机怎么选&#xff0c;我去搜索了许多文章&#xff0c;整理了一篇小白挑选技术篇&#xff0c;供大家参考。 分类 胶片相机 需要装入胶卷才能使用的相机&#xff0c;拍照后可直…