多进程编程

您所在的位置:网站首页 sockaddr_in头文件 多进程编程

多进程编程

2023-01-12 17:35| 来源: 网络整理| 查看: 265

共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题是,我们必须用i其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。因此,共享内存通常和其他进程间通信方式一起使用。

linux共享内存的API都定义在sys/shm.h头文件中,包括4个系统调用:shmget、shmat、shmdt和shmctl。

shmget系统调用

shmget系统调用创建一段新的共享内存,或者获取一段已经存在的共享内存。定义如下:

#include int shmget(key_t key, size_t size, int shmflg);

和semget系统调用一样,key参数是一个键值,用来标识一段全局唯一的共享内存。size参数指定共享内存的大小,单位是字节。如果是创建新的共享内存,则size值必须指定。如果是获取已经存在的共享内存,则可以把size设置为0。

shmflg参数的使用和含义与semget系统调用的sem_flags参数相同。不过shmget支持两个额外的标志:

SHM_HUGETLB:类似于mmap的MAP_HUGETLB标志,系统将使用“大页面”来为共享内存分配空间 SHM_NORESERVE:类似于mmap的MAP_NORESERVE,不为共享内存保留交换分区(swap空间)。这样,当物理内存不足的时候,对该共享内存执行写操作将触发SIGSEGV信号。

shmget成功时返回一个正整数值,它是共享内存的标识符。shmget失败时返回-1,并设置errno。

如果shmget用于创建共享内存,则这段共享内存的所有自己都被初始化为0,与之关联的内核数据结构shmid_ds将被创建并初始化。shmid_ds结构体的定义如下:

struct shmid_ds { struct ipc_prem shm_prem; /*共享内存的操作权限*/ size_t shm_segsz; /*共享内存大小,单位是字节*/ __time_t shm_atime; /*对这段内存最后一次调用shmat的时间*/ __time_t shm_dtime; /*对这段内存最后一次调用shmdt的时间*/ __time_t shm_ctime; /*对这段内存最后一次调用shmctl的时间*/ __pid_t shm_cpid; /*创建者PID*/ __pid_t shm_lpid; /*最后一次执行shmat或shmdt操作的进程PID*/ shmatt_t shm_nattach; /*目前关联到此共享内存的进程数量*/ /*省略一下填充字段*/ }

shmget对shmid_ds结构体的初始化包括:

将shm_perm.cuid和shm_perm.uid设置为调用进程的有效用户ID 将shm_perm.cgid和shm_perm.gid设置为调用进程的有效组ID 将shm_perm.mode的最低9位设置为shmflg参数的最低9位 将shm_segsz设置为size 将shm_lpid、shm_nattach、shm_atime、shm_dtime设置为0 将shm_ctime设置为当前的时间 shmat和shmdt调用

共享内存被创建/获取之后,我们不能立即访问它,而是需要先将它关联到进程的地址空间中。使用完共享内存之后,我们也需要将它从进程地址空间中分离。这两项任务分别由两个系统调用实现:

#include

void* shmat(int shm_id, const void *shm_addr, int shmflg);

int shmdt(const void* shm_addr);

其中,shm_id参数是由shmget调用返回的共享内存标识符。

shm_addr参数指定将共享内存关联到进程的哪块地址空间,最终的效果还受到shmflg参数的可选标志SHM_RND影响:

如果shm_addr为NULL,则被关联的地址由操作系统选择。这是推荐的做法,以确保代码的可移植性。 如果shm_addr非空,并且SHM_RND标志未被设置,则共享内存被关联到addr指定的地址处。 如果shm_addr非空,并且设置了SHM_RND标志,则被关联的地址是[shm_addr - (shm_addr % SHMLBA)]。SHMLBA的含义是“段低端边界地址倍数”(Segment Low Boundary Address Multiple),它必须是内存页面大小PAGE_SIZE的整数倍。现在的Linux内核中,它等于一个内存页大小。SHM_RND的含义是圆整(round),即将共享内存被关联的地址向下圆整到离shm_addr最近的SHMLBA的整数被地址处。

除了SHM_RND标志外,shmflg参数还支持如下标志:

SHM_RDONLY:进程仅能读取共享内存中的内容。若没有指定该标志,则进程可同时对共享内存进行读写操作(当然,这需要在创建共享内存的时候指定其读写权限) SHM_REMAP:如果地址shmaddr已经被关联到一段共享内存上,则重新关联 SHM_EXEC:它指定对共享内存段的执行权限。对共享内存而言,执行权限实际上和读权限是一样的。

shmat成功时返回共享内存被关联到的地址,失败则返回(void*)-1并设置errno。shmat成功时,将修改内核数据结构shmid_ds的部分字段:

将shm_nattach加1 将shm_lpid设置为调用进程PID 将shm_atime设置为当前的时间

shmdt函数将关联到的shm_addr处的共享内存从进程中分离。它成功时返回0,失败则返回-1并设置errno。shmdt在成功调用时将修改内核数据结构shmid_ds的部分字段:

将shm_nattach减1 将shm_lpid设置为调用进程的PID 将shm_dtime设置为当前的时间 shmctl系统调用

shmctl系统调用控制共享内存的某些属性。定义如下:

#include int shmctl(int shm_id, int command, struct shmid_ds* buf);

其中,shm_id参数是由shmget调用返回的共享内存标识符。command参数指定要执行的命令。shm_ctl支持的所有命令如下表:

多进程编程 - 共享内存

 

shmctl成功时的返回值取决于command参数,如上表,失败是返回-1,并设置errno。

共享内存的POSIX方法

mmap函数利用它的MAP_ANONYMOUS标志可以实现父、子进程之间的匿名内存共享。通过打开同一个文件按,mmap可以实现无关进程之间的内存共享。Linux提供了另外一种利用mmap在无关进程之间共享内存的方式。这种方式无需任何文件的支持,但它需要先使用如下函数创建或打开一个POSIX可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为POSIX)共享内存对象:

#include #include #include int shm_open(const char* name, int oflag, mode_t mode);

shm_open的使用方法与open系统调用完全相同。

name参数指定要创建/打开的内存。从可移植性的角度考虑,该参数应该使用“/somename”的格式:

以“/”开始,后接多个字符,且这些字符都不是“/”;

以“



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3