1. 进程间通信概述
(1)概述
①数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
②共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
③通知事件:一个进程需要向另一个(组)进程发送消息,通知它们发生了某种事件(如进程终止时要通知父进程)。
④资源共享:多个进程之间共享同样的资源。为了做到这一点,需要内核提供锁和同步机制。
⑤进程控制:有些进程希望完全控制另一个进程的执行(如Degub进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
(2)现代的进程间通信方式
①管道(pipe)和命名管理(FIFO) ②信号(signal) ③消息队列 ④共享内存 ⑤信号量 ⑥套接字(socket)
2. 管道通信
2.1 概述
(1)管道是针对本地计算机的两个进程之间的通信而设计的通信方法,管道建立后,实际获得的是两个文件描述符:一个用于读取,另一个用于写入。
(2)最常见的IPC机制,通过pipe系统调用
(3)管道是单工的,数据只能向一个方向流动,需要双向通信时,需要建立起两个管道。
(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据(即读取的顺序应与写入的顺序一致!)
2.2 管道的分类和读写
(1)管道的分类
①匿名管道:
A.在关系进程中进程(父进程和子进程,兄弟进程之间)
B.由pipe系统调用,管道由父进程建立
C.管道位于内核空间,其实是一块缓存
②命名管道(FIFO):
A.两个没有任何关系的进程之间通信可通过命名管道进行数据传输,本质上是内核中一块缓存,另在文件系统中以一个特殊的设计文件(管道文件)存在。
B.通过系统调用mkfifo创建。
(2)管道的创建:
|
头文件 |
#include <unistd.h> |
|
函数 |
int pipe(int fd[2]); |
|
功能 |
等待一个或者多个指定信号发生 |
|
返回值 |
成功返回0,否则返回-1 |
|
备注 |
①fd[0]:为pipe的读端,用于读取管道。 ②fd[1]:为pipe的写端,用于写入管道。 |
(2)管道的读写
①管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建另一个子进程。
②注意管道是单工的,所以要关闭父子进程中其中的一些fd(如上图所示)。如果需要双向通信,则需要创建2个管道。
【编程实验】父进程写入,子进程读取
//cal_pipe.c
#include <unistd.h> #include <stdio.h> #include <stdlib.h> /* * 父进程通过管道传输两个数据给子进程 * 由子进程负再从管道中读取并输出 */ int main(void) { int fd[2]; //创建管道 if(pipe(fd) < 0){ perror("fork error"); exit(1); } pid_t pid; if((pid = fork()) < 0){ perror("fork error"); exit(1); }else if(pid >0){ //parent process close(fd[0]); //父进程关闭读端,保留写端。 int start = 1, end = 100; //往管道中写入数据 if(write(fd[1], &start, sizeof(int)) != sizeof(int)){ perror("write error"); exit(1); } if(write(fd[1], &end, sizeof(int)) != sizeof(int)){ perror("write error"); exit(1); } close(fd[1]); wait(pid); }else{ //child process close(fd[1]); //子进程用来读取数据 int start, end; //从管道中读取数据(注意与写入的顺序相同) if(read(fd[0], &start, sizeof(int)) < 0){ perror("read error"); exit(1); } if(read(fd[0], &end, sizeof(int)) < 0){ perror("read error"); exit(1); } close(fd[0]); printf("child process read start: %d, end: %d\n", start, end); } return 0; } /*输出结果: child process read start: 1, end: 100 */