进程间通信
2021/7/2 7:26:29
本文主要是介绍进程间通信,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
进程间通信(Interprocess Communication, IPC):管道、FIFO、消息队列、信号量、共享存储、套接字
1. 管道
1.1 创建:
#include <unistd.h> int pipe(int fd[2]); // 返回值:若成功,返回0,若出错,返回-1. // 函数调用成功返回r/w两个文件描述符。无需open,但需手动close。 // 规定:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样 // 向管道文件读写数据其实是在读写内核缓冲区。
1.2 特点:
- 其本质是一个伪文件(实为内核缓冲区)
- 只适用于有血缘关系之间的进程
- 数据一旦被读走,便不在管道中存在(不可反复读取)
1.3 局限性:
- 半双工,数据只能在一个方向流动,只能单向通信
- 管道只能在具有公共祖先之间的两个进程之间使用;
1.4 通信过程
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。父子进程间通信的步骤:
-
父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
-
父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
-
父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
2. 命名管道(FIFO):
1.1 创建:
#include <sys/stat.h> int mkfifo(const char *path, mode_t mode); //返回值:若成功,返回0;若出错,返回-1。 // 参数pathname为路径名,创建管道的名字 // mode为创建fifo的读写权限。
1.2 特点:
- 命名管道不同于匿名管道:它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中,即第一个被写入的数据将首先从管道中读出
- 命名管道是一个设备文件,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。
1.3 与匿名管道对比
mkfifo
函数的作用是在文件系统中创建一个真实存在文件,该文件用于提供FIFO功能,即命名管道。前边讲的那些管道都没有名字,因此它们被称为匿名管道,或简称管道。- 对文件系统来说,匿名管道是不可见的,它的作用仅限于在父进程和子进程两个进程间进行通信。而命名管道是一个可见的文件,因此,它可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。
3. 消息队列
1.1 创建:
第一步:获得key值。因为消息队列独立于进程而存在,所以为了区别不同的消息队列,需要以key值标记消息队列,这样两个不相关的进程可以通过事先约定的key值通过消息队列进行消息收发。
#include <sys/types.h> #include <sys/ips.h> key_t futon(char *pathname, int projid) // pathname:文件名(含路径),通常设置为当前目录“.” // projid:子序号,虽然为int类型,但只使用了低8位,通常用一个字母表示 // 返回值:成功返回key值,失败返回-1;
第二步:创建或打开消息队列
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflag) /* 功能:用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。 参数: key:函数ftok的返回值或IPC_PRIVATE,为IPC_PRIVATE时表示创建自己的消息队列,这样就只能用于和自己有亲缘关系的进程间通信。 msgflag: IPC_CREAT:创建新的消息队列。 IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。 返回值: 调用成功返回队列标识符,否则返回-1. */
第三步:将消息添加到消息队列
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflag) /* 功能: 将新消息添加到队列尾端,即向消息队列中发送一条消息。 参数: msqid:消息队列的标识符。 msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下 struct msgbuf { long mtype; /* 消息类型,必须 > 0 */ char mtext[1]; /* 消息文本 */ }; msgsz:消息的大小,消息的总大小不能超过8192字节,在<linux/msg.h>有个宏控制,#define MSGMAX 8192。 msgflag: IPC_NOWAIT: 指明在消息队列没有足够空间容纳要发送的消息时,msgsnd立即返回。 0:msgsnd调用阻塞直到条件满足为止.(一般选这个) */
第四步:从消息队列读取消息:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtype, int msgflag) /* 功能: 从队列中接收消息 参数: msqid:已打开的消息队列id msgp:存放消息的结构体指针。msgp->mtype与第四个参数是相同的。 msgsz:消息的字节数,指定mtext的大小。 msgtype:消息类型,消息类型 mtype的值。如果为0,则接受该队列中的第一条信息,如果小于0,则接受小于该值的绝对值的消息类型,如果大于0,接受指定类型的消息,即该值消息。 msgflag:函数的控制属性。 msgflag: MSG_NOERROR:若返回的消息比msgsz字节多,则消息就会截短到msgsz字节,且不通知消息发送进程. IPC_NOWAIT:调用进程会立即返回.若没有收到消息则返回-1. 0:msgrcv调用阻塞直到条件满足为止. 在成功地读取了一条消息以后,队列中的这条消息将被删除。 返回值:成功执行时,msgrcv()返回0, 失败返回-1 */
1.2 特点:
- 消息队列是消息的链表,具有特定的格式,存放在内存中,并由消息队列标识符标识.
- 消息队列允许一个或多个进程向它写入与读取消息.
- 管道和命名管道都是通信数据都是先进先出的原则,而消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取;比FIFO更有优势
4. 共享存储
共享存储允许一个或多个进程共享一个给定的存储区,因为数据不需要在客户端进程和服务器进程之间进行复制,所以这是最快的一种IPC,对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。
不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制——互斥锁和信号量都可以。
4.1 相关函数
第一步:创建/打开共享内存
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); /* 功能:分配一块共享内存 参数: key: 函数ftok的返回值或IPC_PRIVATE,为IPC_PRIVATE时创建的共享内存只能用于有亲缘关系的进程通信; size:建立共享内存的大小(单位字节),通常取系统页长的整数倍,若size值不是系统页的整数倍,则最后一页剩余部分是不可用的; shmflg: 0:取共享内存标识符,若不存在则函数会报错 IPC_CREAT:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符 IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个消息队列;如果存在这样的共享内存则报错 16 返回值:成功,返回共享内存标识符,失败返回-1; */
第二步:映射共享内存
#include <sys/types.h> #include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg) /* 功能:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问 参数: msqid:共享内存标识符 shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置 shmflg:SHM_RDONLY:为只读模式,否则为读写模式 返回值: 成功,返回共享内存地址,出错返回-1 */
4.2 共享存储的特点:
-
速度快,效率高;
-
需要有同步机制;
4.3 共享存储操作流程
-
创建/打开共享内存
-
映射共享内存,即把即把指定的共享内存映射到进程的地址空间用于访问
-
撤销共享内存映射
-
删除共享内存
5. UNIX域套接字
- UNIX域套接字用于在同一台机器上运行的进程之间的通信,虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高。
- UNIX域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。
- UNIX域套接字有两种类型的套接字:字节流套接字和数据包套接字,字节流套接字类似于TCP,数据包套接字类似于UDP。
这篇关于进程间通信的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求