Linux——进程间通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40797414/article/details/82253047

进程间通信目的:

  1. 数据传输:一个进程需要将它的数据发送给另一个进程。
  2. 资源共享:多个进程之间共享同样的资源
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
  4. 进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程能够拦截另一个进程的所有 陷入和异常,并能够及时知道它的状态改变。

2.管道
管道 是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个管道。
匿名管道

#include <unistd.h>
创建一无名管道
int pipe(int fd[2]);
fd: 文件描述符数组,其中fd[0]表示读端,fd[1]表示写端,成功返回0,失败返回错误代码。
管道读写规则:O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read 调用返回-1,errno值为EAGAIN。
如果所有管道写端对应的文件描述符被关闭,则read返回0。
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出。
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性(就是写完了之后,缓冲区再刷新。)
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
管道的特点:只能用于具有共同祖先的进程之间进行通信,通常,一个管道由一个进程创建,然后该进程调用fork,此后,父子进程之间就可应用该管道。
管道提供流式服务,一般而言,进程退出,管道释放,所以管道的生命周期随进程。
一般而言,内核会对管道操作进行同步与互斥。
管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道。
父进程读,子进程写。
命名管道:管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件。
创建一个命名管道:命名管道可以从命令行上创建:mkfifo filename
命名管道也可以从程序里创建,相关函数有:int mkfifo(const char* filename,mode_t mode)
匿名管道与命名管道的区别:
匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开用open,FIFO(命名管道)与pipe(匿名管道)之间唯一区别在它们创建于打开的方式不同,一旦这些工作完成之后,它们具有相同的语义。
命名管道的打开规则:
如果当前打开操作是为读而打开FIFO时,O_ONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO。
O_NONBLOCK enable:立刻返回成功。
如果当前打开操作是为写而打开FIFO时,O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为enxio.

3.消息队列
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法。
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的,每个消息队列总得字节数是有上限的,系统上消息队列的总数也有一个上限。
消息队列相关函数
int magget(key_t key, int msgflg)
key:某个消息队列的名字
msgflg:权限标志
成功返回一个非父整数,即该消息队列的标识码,失败返回-1。
int msgctl(int msqid,int cmd,struct msqid_ds *buf)
msqid:由msgget函数返回的消息队列标识码。
cmd:将要采取的动作:IPC_STAT:将msqid_ds结构中的数据设置为消息队列的当前关联值。
IPC_SET:在进程有足够权限的前提下,将消息队列的当前关联值设置为msqid_ds数据结构中给出的值。
IPC_RMID:删除消息队列。
int msgsnd(int msqid,const void*msgp,size_t msgsz,int msgflg)
msgid:由msgget函数放回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息。
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int常整型

msgflg:控制着当前消息队列满或达到系统上限时将要发生的事情。
msgflg=IPC_NOWAIT:表示队列满不等待,返回EAGAIN错误。
成功返回0,失败返回-1
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz ,long msgtyp,int msgflg);
功能:从一个消息队列接收消息
msgid:由msgget函数返回的消息队列标识码。
msgp:是一个指针,指针指向准备接收的消息。
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int 长整形。
msgtype:它可以实现接收优先级的简单形式。
msgflg:控制着队列中没有相应类型的消息可接收时将要发生的事。
若成功返回实际放到接收缓冲区里去的字符个数,失败返回-1。
msgtype=0返回队列第一条信息。
msgtype>0返回队列第一条类型等于msgtype的消息。
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息。
msgflg=IPC_NOWAIT:队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR:消息大小超过msgsz时被截断。
msgtype>0且msgflg-=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。
ipcs:显示IPC资源
ipcrm:手动删除IPC资源
ipcrm -q 进程号:删除
ipcs -q 显示
4.共享内存:共享内存是最快的IPC形式,一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
共享内存函数:
int shmget(key_t key,size_t size,int shmflg);
key:这个共享内存段名字。
size:共享内存大小。
shmflg:由9个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,
返回值:成功返回一个非负整数,即该共享内存段的标识码,失败返回-1。
void *shmat(int shmid,const void*shmaddr,int shmflg);
shmid:共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
成功返回一个指针,指向共享内存第一个节;失败返回-1。
shmaddr 位NULL,核心自动选择一个地址。
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。
shmflg=SHM_RDONLY表示连接操作用来只读共享内存。
int shmdt(const void* shmaddr)
shmaddr:由shmat所返回的指针。
返回值:成功返回0,失败返回-1。
注意,将共享内存段与当前进程脱离不等于删除共享内存段。
int shmctl(int shmid,int cmd,struct shmid_ds *buf)
shmid:由shmget返回的共享内存标识码。
cmd:将要采取的动作。
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构。
成功返回0,失败返回-1。
IPC_STAT:将shmid_ds结构中的数据设置为共享内存的当前关联值。
IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值。
IPC_RMID:删除共享内存段。
ipc -m:显示共享内存资源
ipcrm -m 进程号:删除共享内存资源。
注意:共享内存没有进行同步与互斥。
5.信号量主要用于同步和互斥,
进程互斥:由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
在进程中涉及到互斥资源的程序段叫临界区。
进程同步:指多个进程需要相互配合共同完成一项任务。
信号量和P,V原语。
信号量:互斥:P.V在同一个进程中。
同步:P.V在不同进程中。
信号量值含义:s>0:s表示可用资源的个数。
s=0:表示无可用资源,无等待进程。
int semget(key_t key,int nsems,int semflg)
key:信号集的名字
nsems:信号集中信号量的个数
semflg:权限标志。
返回值:成功返回一个非负整数,即该信号集的标识码,失败返回-1。
int semctl(int semid,int semnum,int cmd,…);
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号。
cmd:将要采取的动作。
最后一个参数根据命令不同而不同
成功返回0;失败返回-1。
SETVAL:设置信号量集中信号量的计数值。
GETVAL:获取信号集中信号量的计数值。
IPC_STAT:把semid_ds结构中的数据设置为信号集的当前失联值。
IPC_SET:在进程有足够权限的前提下,将信号集的当前关联值设置为semid_ds数据结构中给出的值。
IPC_RMID:删除信号集。
int semop(int semid,struct senbuf,unsigned nsops);
semid:是该信号量的标识码,也就是semget函数的返回值。
sops:是个指向一个结构数值的指针。
nsops:信号量的个数。
成功返回0,失败返回-1。
ipcs -s:显示信号量资源。
ipcrm -s 进程号:收到删除信号量资源。
六. 进程间通信总结:
创建IPC;
删除IPC,ipcctl+IPC_RMID
每种IPC都有自己个性化的操作接口。
特性方面:IPC资源必须手动删除,否则不会自动清除,除非重启,所以 system V IPC资源的生命周期随内核。

猜你喜欢

转载自blog.csdn.net/weixin_40797414/article/details/82253047