目录
概览
单工、半双工、双工
各种IPC通信方式优缺点
1. 管道
2. 消息队列
3. 共享内存
4. 信号量
5. 信号
6. Socket套接字
精筛优秀博客
原本想详细写下,后发现有很多人总结过,那我就直接战在巨人的肩膀上不再重复造轮子了。
分为匿名管道和命名管道,都是半双工。
匿名管道只能亲缘关系的进程间使用(父子进程)。
#include
int pipe(int fd[2]); // 返回值:若成功返回0,失败返回-1
命名管道(又叫FIFO)本质是文件,可以非亲缘关系的进程间使用。
#include
// 返回值:成功返回0,出错返回-1
int mkfifo(const char *pathname, mode_t mode);
不管是匿名管道还是命名管道,都是:先进先出,效率低,不适合频繁交换数据。
#include
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// send:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// receive:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// control:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
不一定要先进先出,可以用type控制。消息队列里有消息体,消息体被读取后删除,但消息队列生命周期跟随内核,进程销毁仍存在。
效率较高。
不适合比较大的数据,在 Linux 内核中,会有两个宏定义 MSGMAX
和 MSGMNB
,它们以字节为单位,分别定义了一条消息的最大长度和一个队列的最大长度。
存在用户态与内核态间数据拷贝开销。
#include
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr);
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
两个进程虚拟地址映射到相同的物理地址。
效率最高,无用户态与内核态间数据拷贝开销。
但无同步机制。
#include
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);
// 控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);
信号量其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。若要进程间传递数据要配合共享内存。
信号量初始化为1。P(通过)操作-1,V(释放)操作+1,信号量>=0进程才可继续执行。信号量<0进程阻塞等待其他进程把信号量恢复。
具体的过程如下:
信号是进程间通信机制中唯一的异步通信机制。对于异常情况下的工作模式,就需要用「信号」的方式来通知进程。
在 Linux 操作系统中, 为了响应各种各样的事件,提供了几十种信号,分别代表不同的意义。我们可以通过 kill -l
命令,查看所有的信号:
$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
可以跨网络与不同主机的进程间通信,也可以同主机通信。本地字节流 socket 和 本地数据报 socket 在 bind 的时候,不像 TCP 和 UDP 要绑定 IP 地址和端口,而是绑定一个本地文件,这也就是它们之间的最大区别。
根据创建 socket 类型的不同,通信的方式也就不同:
各种通信方式的比较和优缺点
管道:速度慢,容量有限,只有父子进程能通讯
FIFO:任何进程间都能通讯,但速度慢
消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
信号量:不能传递复杂消息,只能用来同步
共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
如想看具体实现看下面链接。
进程间通信——几种方式的比较和详细实例_worthsen的博客-CSDN博客_进程间的通信方式三种
5.2 进程间有哪些通信方式? | 小林coding (xiaolincoding.com)
Linux c/c++之IPC进程间通信_石小浪♪的博客-CSDN博客_ipc_set
Linux进程间通信的几种方式总结--linux内核剖析(七)-阿里云开发者社区 (aliyun.com)