【服务器编程】Linux多进程编程(二)

本文接这上一篇【服务器编程】Linux多进程编程(一)的内容介绍Linux多进程编程中常用的两种方式:共享内存和消息队列。
本文的内容是阅读总结游双的《Linux高性能服务器编程》第13章“多进程编程“而来

共享内存

共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题是,我们必须用其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。Linux共享内存的API都定义在sys/shm.h头文件中,包括4个系统调用:shmget、shmat、shmdt和shmctl。

shmget系统调用

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

#include <sys/shm.h>
int shmget(key_t key,size_t size, int shmflg);
  • key:用来标识一段全局唯一的共享内存;
  • size:指定共享内存的大小,如果是获取已经存在的共享内存,可以把size设置为0;
  • shmflg:权限标志符;

shmat和shmdt系统调用

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

#include <sys/shm.h>
void* shmat(int shm_id,const void* shm_addr, int shmflg);
int shmdt(const void* shm_addr);
  • shm_id:有shmget调用返回的共享内存标识符
  • shm_addr:指定共享内存关联到进程的哪块地址空间,推荐设置为NULL,表示被关联的地址由操作系统选择
  • shmflg:常见的有SHM_RDONLY(进程仅能读取共享内存中的内容),SHM_REMAP(如果地址shmaddr已经被关联到一段共享内存上,则重新关联),SHM_EXEC(指定对共享内存段的执行权限)

shmat成功时返回共享内存被关联到的地址,失败则返回(void*)-1并设置errno

shmdt函数将关联到shm_addr处的共享内存从进程中分离。它成功时返回0,失败则返回-1并设置errno

shmctl系统调用

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

#include <sys/shm.h>
int shmctl(int shm_id, int command, struct shmid_ds* buf);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCfL9voM-1593871396806)(C:\Users\11754\Desktop\技术\总结\img\shmctl支持的命令.png)]

共享内存的POSIX方法

Linux提供了另外一种利用mmap在无关进程之间共享内存的方式。这种方式无须任何文件的支持,但它需要先使用如下函数来创建或打开一个POSIX共享内存对象:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_open(const char* name, int oflag, mode_t mode);
  • name参数指定要创建/打开的共享内存,格式为“/somename”
  • oflag参数指定创建方式。它可以是下列标志中的一个或者多个的按位或:
    • O_RDONLY:以只读方式打开共享内存对象
    • O_RDWR:以可读、可写方式打开共享内存对象
    • O_CREAT:如果共享内存对象不存在,则创建它
    • O_EXCL:和O_CREAT一起使用,如果由name指定的共享内存对象已经存在,则shm_open调用返回错误,否则就创建一个新的共享内存对象。

shm_open创建的共享内存对象使用完之后也需要被删除。这个过程是通过如下函数实现的:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_unlink(const char* name);

该函数将name参数指定的共享内存对象标记为等待删除。当所有使用该共享内存对象的进程都使用ummap将它从进程中分离之后,系统将销毁这个共享内存对象所占据的资源。

如果代码中使用了上述POSIX共享内存函数,则编译的时候需要指定链接选线-lrt

消息队列

消息队列是在两个进程之间传递二进制块数据的有一种简单有效的方式。每个数据块都有一个特定的类型,接收方可以根据类型来有选择地接收数据,而不一定像管道和命名管道那样必须以先进先出的方式接收数据。

Linux消息队列的API定义在sys/msg.h头文件中,包括:msgget、msgsnd、msgrcv和msgctl。

msgget

msgget用于创建一个消息队列,或者获取一个已有的消息队列。其定义如下:

#include <sys/msg.h>
int msgget(key_t key,int msgflg);
  • key参数是一个键值,用来标识一个全局唯一的消息队列。
  • msgflg参数是权限控制符,如:IPC_CREAT表示如果消息队列不存在,则创建,否则打开;

msgsnd

msgsnd把一条消息添加到消息队列中。定义如下:

#include <sys/msg.h>
int msgsnd(int msqid,const void* msg_ptr,size_t msg_sz,int msgflg);
  • msqid参数是由msgget调用返回的消息队列标识符;
  • msg_ptr参数指向一个准备发送的消息,消息必须被定义为如下类型:
struct msgbuf
{
	long mtype; /* 消息类型 */
	char mtext[512]; /* 消息数据 */
}
  • msg_sz是消息的数据部分的长度
  • msgflg参数控制msgsnd的行为。它通常仅支持IPC_NOWAIT标志,即以非阻塞的方式发送消息。默认情况下,发送消息时如果消息队列满了,则msgsnd将阻塞。如IPC_NOWAIT标志被指定,则msgsnd将立即返回并设置errnoEAGAIN

msgrcv

msgrcv系统调用从消息队列中获取消息。其定义如下:

#include <sys/msg.h>
int msgrcv(int msqid, void* msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
  • msqidmsgget调用返回的消息队列标识符;
  • msg_ptr用于存储接收消息的类型,msg_sz指的是消息数据部分的长度;
  • msgtype参数指定接收何种类型的消息:
    • 等于0:读取消息队列中的第一个消息;
    • 大于0:读取消息队列中第一个类型值为msgtype的消息;
    • 小于0:读取消息队列中第一个类型值比msgtype的绝对值小的消息;
  • msgflg控制msgrcv函数的行为:
    • IPC_NOWAIT:如果消息队列中没有消息,则msgrcv调用立即返回并设置errnoENOMSG
    • MSG_EXCEPT:如果msgtype大于0,则接收消息队列中第一个非msgtype类型的消息;
    • MSG_NOERROR:如果消息数据部分的长度超过msg_sz,就将它截断;

msgctl系统调用

msgctl系统调用控制消息队列的某些属性。其定义如下:

#include <sys/msg.h>
int msgctl(int msqid, int command, struct msqid_ds* buf);		
  • msqid参数是由msgget调用返回的共享内存标识符。command参数指定要执行的命令。

如果觉得还不错,关注公众号获取更多优质文章 ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值