做网站软件图标是一个箭头的,网站虚拟视频主持人,网站建设哪家好采用苏州久远网络,wengdo网站开发创意设计Linux C语言 41-进程间通信IPC之共享内存
本节关键字#xff1a;C语言 进程间通信 共享内存 shared memory 相关库函数#xff1a;shmget、shmat、shmdt、shmctl
什么是共享内存#xff1f;
共享内存#xff08;Shared Memory#xff09;指两个或多个进程共享一个给定的…Linux C语言 41-进程间通信IPC之共享内存
本节关键字C语言 进程间通信 共享内存 shared memory 相关库函数shmget、shmat、shmdt、shmctl
什么是共享内存
共享内存Shared Memory指两个或多个进程共享一个给定的存储区。
共享内存的特点
共享内存是最快的只用System V IPC因为进程是直接对内存进行读写因为多个进程可以同时操作所以需要进程同步信号量共享内存通常结合在一起使用信号量用来同步对共享内存的访问共享内存被创建成功后需要手动释放否则将持续到内核重新引导。
共享内存相关库函数
创建或链接一个共享内存
当创建新的共享内存段时其内容被初始化为零并且其相关的数据结构shmid_ds参见shmctl(2)被初始化如下
shm_perm.uid 和shm_perm.uid 被设置为调用进程的有效用户ID。shm_perm.cgid 和shm_perm_gid 被设置为调用进程的有效组ID。shm_perm.mode 的最低有效9位被设置为shmflg 的最低有效的9位。shm_setsz 设置为size 的值。shm_lpid、shm_natch、shm_time和shm_dtime 设置为0。shm_time 设置为当前时间。
如果共享内存段已经存在则会验证权限并检查它的销毁标记是否有效。
#include sys/ipc.h
#include sys/shm.h
#include sys/types.h
key_t ftok(const char *pathname, int proj_id); // 前文消息队列中已经介绍过这里就不展开介绍了
int shmget(key_t key, size_t size, int shmflg);
/**
brief 分配共享内存段。shmget()返回与参数key的值相关联的共享内存段的标识符。如果key的值为IPC_PRIVATE或key不是IPC_PRIVATE不存在与该密钥对应的共享内存段并且在shmflg中指定了IPC_CREAT则会创建一个新的共享内存分段其大小等于四舍五入到PAGE_SIZE的倍数。如果shmflg同时指定了IPC_CREAT和IPC_EXCL并且key的共享内存段已经存在那么shmget()将失败errno设置为EEXIST。这类似于组合O_CREAT | O_EXCL对open(2)的效果。
param key 进程间通信的键值密钥是ftok()的返回值
param size 该共享内存的字节长度
param shmflg 表示函数的行为及共享内存的权限取值如下IPC_CREAT 如果不存在就创建IPC_EXCL 如果存在则返回失败mode_flags最低有效9位指定授予所有者、组和世界的权限。这些位具有与open(2)的模式自变量相同的格式和含义。目前系统未使用执行权限。SHM_HUGETLB 自Linux 2.6起使用“巨大的页面”分配段。有关更多信息请参阅内核源文件Documentation/vm/hugetlbpage.txt。SHM_NORESERVE 自Linux 2.6.15起此标志的作用与mmap2MAP_NORESERVE标志相同。不要为该段保留交换空间。当保留交换空间时可以保证可以修改段。当交换空间没有保留时如果没有可用的物理内存则可能会在写入时获得SIGSEGV。另请参阅proc(5)中文件/proc/sys/vm/overcommit_memory的讨论。
return 成功返回有效的贡献内存标识符shmid失败返回-1并设置错误码errno错误码errno的类型
EACCES 用户没有访问共享内存段的权限也没有CAP_IPC_OWNER功能。
EEXIST 指定了IPC_CREAT | IPC_EXCL并且该段存在。
EINVAL 要创建一个新的段其大小SHMMIN或sizeSHMMAX或者不创建新的段、存在一个具有给定键的段但大小大于该段的大小。
ENFILE 已达到打开文件总数的系统限制。
ENOENT 给定key不存在分段并且未指定IPC_CREAT。
ENOMEM 无法为段开销分配内存。
ENOSPC 已获取所有可能的共享内存IDSHMMNI或者分配所请求大小的段将导致系统超过共享内存的系统范围限制SHMALL。
EPERM 指定了SHM_HUGETLB标志但调用方没有特权不具有CAP_IPC_LOCK功能。NOTES
IPC_PRIVATE不是标志字段而是key_t类型。如果这个特殊值用于key则系统调用将忽略shmflg的除最低有效9位之外的所有内容并创建一个新的共享内存段。
以下对共享内存段资源的限制会影响shmget调用
SHMALL 系统范围内共享内存页的最大值在Linux上可以通过/proc/sys/kernel/SHMALL读取和修改此限制。
SHMMAX 共享内存段的最大大小以字节为单位取决于策略在Linux上可以通过/proc/sys/kernel/SHMMAX读取和修改此限制。
SHMMIN 共享内存段的最小大小以字节为单位取决于实现当前为1字节但PAGE_size是有效的最小大小。
SHMMNI 系统范围内共享内存段的最大数量取决于实现目前为4096个在Linux 2.3.99之前为128个在Linux上可以通过/proc/sys/kernel/SHMMNI读取和修改此限制。
该实现对每个进程的共享内存段的最大数量SHMSEG没有特定限制。
*/将共享内存映射到进程内存
一个成功的shmat()调用将更新与共享内存段相关联的shmid_ds结构的成员请参见shmctl(2)如下所示
shm_time 设置为当前时间。shm_lpid 设置为调用进程的进程ID。shm_natch 增加1。
#include sys/types.h
#include sys/shm.h
void *shmat(int shmid, const void *shmaddr, int shmflg);
/**
brief shmat()将shmid标识的共享内存段附加到调用进程的地址空间。附加地址由shmaddr根据以下条件之一指定如果shaddr为NULL系统会选择一个合适的未使用的地址来附加段。如果shaddr不为NULL并且在shmflg中指定了SHM_RND则附加发生在等于shaddr的地址处四舍五入到SHMLBA的最近倍数。否则shaddr必须是发生附加的页面对齐地址。如果在shmflg中指定了SHM_RDONLY则附加该段进行读取并且进程必须具有该段的读取权限。否则将附加段进行读写操作并且进程必须具有该段的读写权限。不存在只写共享内存段的概念。
param shmid 共享内存的标识符是shmget()的成功返回值
param shmaddr 共享内存映射地址为NULL则系统自动指定一般使用NULL
param shmflg 共享内存端的访问权限和映射条件一般设置为0具体取值如下0 共享内存具有可读可写权限SHM_RDONLY 只读SHM_RND shmaddr非空时才有效
return 成功返回附加的共享内存段的地址失败返回-1并设置错误码errno错误码errno的类型
EACCES 调用进程不具有所请求的附加类型所需的权限也不具有CAP_IPC_OWNER功能。
EIDRM shmid指向一个已删除的标识符。
EINVAL 无效的shmid值、未对齐即未对齐页面且未指定SHM_RND或无效的shaddr值或者无法在shaddr处附加段或者指定了SHM_REMAP且shaddr为NULL。
ENOMEM 无法为描述符或页表分配内存。
*/#### 解除共享内存的映射
#include sys/types.h
#include sys/shm.h
int shmdt(const void *shmaddr);
/**
brief shmdt()从调用进程的地址空间中分离位于shaddr指定地址的共享内存段。
要分离的段当前必须附加等于附加shmat()调用返回值的shaddr。在成功调用shmdt()时系统会更新与共享内存段相关联的shmid_ds结构的成员如下所示shm_dtime 设置为当前时间。shm_lpid 设置为调用进程的进程ID。shm_natch 递减一。如果它变为0并且该段被标记为删除则该段被删除。
param shmaddr 共享内存的参数地址
return 成功返回0失败返回-1并设置错误码errno错误码errno的类型
EINVAL shaddr上没有附加共享内存段或者shaddr没有在页面边界上对齐。
*/控制共享内存
#include sys/ipc.h
#include sys/shm.hstruct ipc_perm
{key_t __key; // shmget(2)提供的密钥uid_t uid; // 所有者的有效UIDgid_t gid; // 所有者的有效GIDuid_t cuid; // 创建者的有效UIDgid_t cgid; // 创建者的有效GIDunsigned short mode; // 权限SHM_DEST和SHM_LOCKED标志unsigned short __seq; // 序列号码
};struct shmid_ds
{struct ipc_perm shm_perm; // 所有权和权限size_t shm_segsz; // 段大小字节time_t shm_atime; // 上次连接时间time_t shm_dtime; // 上次分离时间time_t shm_ctime; // 上次修改时间pid_t shm_cpid; // 创建者进程PIDpid_t shm_lpid; // 上次执行shmat(2)或shmdt(2)的进程PIDshmatt_t shm_nattch; // 当前附件数量...
};struct shminfo
{unsigned long shmmax; // 共享内存最大分段长度unsigned long shmmin; // 共享内存最小分段长度总为1unsigned long shmmni; // 共享内存最大分段数unsigned long shmseg; // 进程可以附加的最大段数内核中未使用unsigned long shmall; // 系统范围内共享内存的最大页数// shmmni、shmmax和shmall设置可以通过相同名称的/proc文件进行更改有关详细信息请参见proc(5)。
};struct shm_info
{int used_ids; // # of currently existing segmentsunsigned long shm_tot; // Total number of shared memory pagesunsigned long shm_rss; // # of resident shared memory pagesunsigned long shm_swp; // # of swapped shared memory pagesunsigned long swap_attempts; // Unused since Linux 2.4unsigned long swap_successes; // Unused since Linux 2.4
};int shmctl(int shmid, int cmd, struct shmid_ds *buf);
/**
brief 获取和设置消息队列的属性
param shmid 共享内存的标识符
param cmd 函数功能的控制取值如下IPC_STAT 将信息从与shmid关联的内核数据结构复制到buf指向的shmid_ds结构中。调用方必须具有对共享内存段的读取权限。IPC_SET 将buf指向的shmid_ds结构的一些成员的值写入与该共享内存段相关联的内核数据结构同时更新其shm_time成员。可以更改以下字段shm_perm.uid、shm_perm_gid和shm_per.mode的最低有效9位。调用进程的有效uid必须与共享内存段的所有者shm_erm.uid或创建者shm_term.cuid匹配或者调用方必须具有特权。IPC_RMID 标记要销毁的段。只有在最后一个进程将该段分离后即当关联结构shmid_ds的shm_natch成员为零时该段才会被实际销毁。调用方必须是所有者或创建者或者具有特权。如果某个段已标记为要销毁则将设置IPC_STAT检索到的相关数据结构中的SHM_perm.mode字段的非标准SHM_DEST标志。调用方必须确保一个段最终被销毁否则其在中出现故障的页面将保留在内存或交换中。IPC_INFO 特定于Linux返回有关buf指向的结构中的系统范围共享内存限制和参数的信息。如果定义了_GNU_SOURCE功能测试宏则该结构的类型为shminfo因此需要强制转换在sys/shm.h中定义 struct shminfo;结构内容及注释在上方展示SHM_INFO 特定于Linux返回一个shm_info结构其字段包含有关共享内存所消耗的系统资源的信息。如果定义了_GNU_SOURCE功能测试宏则在sys/shm.h中定义此结构struct shm_info;结构内容及注释在上方展示SHM_STAT 特定于Linux返回与IPC_STAT相同的shmid_ds结构。但是shmid参数不是段标识符而是内核内部数组的索引该数组维护系统上所有共享内存段的信息。调用方可以阻止或允许使用以下cmd值交换共享内存段SHM_LOCK 特定于Linux防止交换共享内存段。调用方必须在启用锁定后所需的任何页面中出错。如果某个段已被锁定则将设置IPC_STAT检索到的相关数据结构中的SHM_perm.mode字段的非标准SHM_locked标志。SHM_UNLOCK 特定于Linux解锁分段允许将其交换出去。在2.6.10之前的内核中只有特权进程可以使用SHM_LOCK和SHM_UNLOCK。由于内核2.6.10如果非特权进程的有效UID与段的所有者或创建者UID匹配并且对于SHM_LOCK要锁定的内存量在RLIMIT_MEMLOCK资源限制范围内则该进程可以使用这些操作请参见setrlimit(2)。param buf shmid_ds 数据类型的地址用来存放或修改共享内存的属性。
return 成功的IPC_INFO或SHM_INFO操作返回内核内部数组中使用次数最多的条目的索引该数组记录了有关所有共享内存段的信息。此信息可与重复的SHM_STAT操作一起使用以获得有关系统上所有共享内存段的信息。成功的SHM_TAT操作将返回共享内存段其索引在shmid中给出的标识符。其他操作成功后返回0。失败返回-1并设置错误码errno错误码errno的类型
EACCES IPC_STAT或SHM_STAT被请求并且SHM_perm.mode不允许对shmid进行读取访问并且调用进程不具有CAP_IPC_OWNER功能。
EFAULT 参数cmd的值为IPC_SET或IPC_STAT但buf指向的地址不可访问。
EIDRM shmid指向一个已删除的标识符。
EINVAL shmid不是有效的标识符或者cmd不是有效命令。或者对于SHM_STAT操作shmid中指定的索引值引用了当前未使用的数组槽。
ENOMEM 在2.6.9之后的内核中指定了SHM_LOCK并且要锁定的段的大小意味着锁定的共享内存段中的总字节数将超过调用进程的真实用户ID的限制。此限制由RLIMIT_MEMLOCK软资源限制定义请参阅setrlimit(2)。
EOVERFLOW 尝试IPC_STAT但GID或UID值太大无法存储在buf指向的结构中。
EPERM 尝试IPC_SET或IPC_RMID并且调用进程的有效用户ID不是创建者在shm_perm.cuid中找到或所有者在shr_perm.uid中找到的ID并且进程没有特权Linux不具有CAP_SYS_ADMIN功能。或者在2.6.9之前的内核中指定了SHM_LOCK或SHM_UNLOCK但进程没有特权Linux没有CAP_IPC_LOCK功能。从Linux 2.6.9开始如果RLIMIT_MEMLOCK为0并且调用方没有特权也可能发生此错误。NOTES
ipcs(8)程序使用IPC_INFO、SHM_STAT和SHM_INFO操作来提供有关所分配资源的信息。将来这些文件可能会被修改或移动到/proc文件系统接口。Linux允许进程使用shmctl(IPC_RMID)附加shmat(2)已标记为删除的共享内存段。此功能在其他Unix实现中不可用可移植应用程序应避免依赖它。
结构shmid_ds中的各种字段在Linux 2.2下被键入为短字段而在Linux 2.4下则变为长字段。为了利用这一点在glibc-2.1.91或更高版本下重新编译就足够了。内核通过cmd中的IPC_64标志来区分新旧调用。
*/共享内存例程
/*** 共享内存使用例程创建、连接、写、读、断开连接、删除* 目前未增加线程安全措施后期可以增加互斥锁等*/#include sys/ipc.h
#include sys/shm.h
#include stdio.h
#include stdlib.h
#include string.h#define SHM_FULL 1
#define SHM_LINES_MAX 10
#define SHM_LINE_LEN_MAX 128typedef struct
{int w_idx, full;char msg[SHM_LINES_MAX][SHM_LINE_LEN_MAX];int len[SHM_LINES_MAX];
} SHM;void shmwrite(SHM *shm, const char *msg, size_t len);
char *shmread(SHM *shm);int main()
{key_t shmid;SHM *shmaddr NULL;char message[SHM_LINE_LEN_MAX];// 创建一个新的共享内存段也可自行指定key值将IPC_PRIVARE换成其它值例如6565656shmid shmget(IPC_PRIVATE, sizeof(SHM), IPC_CREAT | 0666);if (shmid -1) {perror(shmget);exit(EXIT_FAILURE);}// 连接到共享内存段shmaddr shmat(shmid, NULL, 0);if (shmaddr (void *)-1) {perror(shmat);exit(EXIT_FAILURE);}printf(shared memory: shmid[%d], address[%p]\n, shmid, shmaddr);// 对共享内存进行读写等操作...while (shmaddr-full ! SHM_FULL){printf(please input your message: );bzero(message, sizeof(message));scanf(\n%[^\n], message); //注意尝试思考和直接使用 %s的区别哦^!^shmwrite(shmaddr, message, strlen(message));printf(reading shared memory data...\n);shmread(shmaddr);// 此过程中可以在新开命令窗口中使用 ipcs -m 查看新建共享内存的相关信息}printf(shared memory will be deleted in 5 seconds\n);sleep(5);// 从共享内存分离if (shmdt(shmaddr) ! 0) {perror(shmdt);exit(EXIT_FAILURE);}// 删除共享内存段if (shmctl(shmid, IPC_RMID, NULL) ! 0) {perror(shmctl);exit(EXIT_FAILURE);}// 此时再使用 ipcs -m 查看创建的共享内存已经不存在了printf(shared memory deleted: shmid[%d], address[%p]\n, shmid, shmaddr);return 0;
}void shmwrite(SHM *shm, const char *msg, size_t len)
{if (shm-full SHM_FULL)return;strncpy(shm-msg[shm-w_idx], msg, len);shm-len[shm-w_idx] len;shm-w_idx;if (shm-w_idx SHM_LINES_MAX)shm-full SHM_FULL;
}char *shmread(SHM *shm)
{int i, count;if (shm-w_idx 0){printf(share memory is empty\n);return;}count shm-w_idx;for (i0; icount; i){printf([%d]: %s\n, i, shm-msg[i]);}return shm-msg[count];
}提示先做内容框架梳理后期进行完善补充