进程间通信IPC
进程间通信(Inter Process Communication),简称IPC
进程间通信的四种方式:
- 管道
匿名管道(pipe)–血缘关系进程间通信
有名管道(fifo) --有无血缘关系均可 - 信号
- 共享内存–有无血缘关系均可
- 本地套接字
匿名管道(pipe)
man pipe查询得
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set
appropriately.
On Linux (and other systems), pipe() does not modify pipefd on failure. A
requirement standardizing this behavior was added in POSIX.1-2016. The
Linux-specific pipe2() system call likewise does not modify pipefd on fail‐
ure.
pipe()函数的传出参数fd[2]固定,其中,fd[0] 代表读端,fd[1] 代表写端。
- 特质:
(1)本质为伪文件,实质为内核缓冲区,不占用磁盘空间
(2)写端流入,读端流出
(3)进程销毁则管道自动释放
(4)管道默认阻塞
-
原理
内核使用环形队列机制,借助内核缓冲区(4k)空间实现,先进先出 -
局限性
(1)单向半双工通信,数据只能单向流动
(2)数据只能读取一次,不能重读读取
(3)只有公共祖先的进程间才能使用
父子进程通信实例
#include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<string.h>
5
6
7 int main()
8 {
9 int fd[2];//pipe所需的文件描述符数组
10 //成功返回文件描述符,fd[0]-r,fd[1]-w
11 int res = pipe(fd);
12
13 pid_t pid;
14
15 if(res == -1)
16 {
17 perror("pipe error");
18 exit(1);
19 }
20
21 pid = fork(); //进程创建成功,创建管道的进程同时掌握读和写端
22 if(pid == -1)
23 {
24 perror("fork error");
25 exit(1);
26 }else if(pid == 0)//子进程 读数据
27 {
28 close(fd[1]);//读则关闭写端
29 char buf[1024];
30 res = read(fd[0],buf,sizeof(buf));//读出数据到buf,返回实际读取到的数据数目
31
32 if(res == 0)
33 {
34 printf("--------\n");
35 }
36
37 write(STDOUT_FILENO, buf, res);//将buf内容写到标准输出流
38
39 }else{
40 sleep(2);
41 close(fd[0]);//父进程,关闭读端,进行写操作
42 char *str = "Hello pipe\n";
43
44 write(fd[1], "Hello pipe\n", strlen("Hello pipe\n"));
45
46 }
47
48
49
50 return 0;
51 }
代码简析:
- 创建管道,默认阻塞,当前进程既掌控读端,也掌控写端
- 创建子进程,根据pid返回值区分父子进程,父子进程分别关闭读端和写端,进行通信
兄弟进程间通信
1 #include<stdlib.h>
2 #include<stdio.h>
3 #include<unistd.h>
4 #include<sys/wait.h>
5
6 int main(int argc, char *argv[])
7 {
8 pid_t pid;
9 int fd[2];
10 int ret = pipe(fd);//创建管道
11
12 if(ret == -1)
13 {
14 perror("pipe error");
15 return 0;
16 }
17
18 int i =0;
19 for(; i < 2;i++)
20 {
21 pid = fork();
22 if(pid == -1)
23 {
24 perror("fork error");
25 }
26 else if(pid == 0)
27 {
28 break;
29 }
30 }
31
32 if(i == 0)//进程1
33 {
34 close(fd[0]);//关闭读,进行写操作
35 dup2(fd[1],STDOUT_FILENO);//输出重定向
36 execlp("ps", "ps", "aux", NULL);
37
38 }else if(i == 1)//进程2
39 {
40 close(fd[1]);//关闭写,进行读操作
41 dup2(fd[0],STDIN_FILENO);//输入重定向
42 execlp("grep", "grep", "bash","--color=auto", NULL);
43
44 }else if(i == 2)//父进程
45 {
46 close(fd[0]);
47 close(fd[1]);
48 }
49 //回收子进程
50
51 int wpid;
52
53 while(wpid = waitpid(-1, NULL, WNOHANG) != -1)//调用出错返回-1
54 {
55 if(wpid == 1)
56 {
57 continue;//init进程
58 }
59
60 if(wpid == 0)
61 {
62 continue;//WNOHANG状态下没有已退出的子进程可供回收
63 }
64
65 printf("child pid = %d\n",wpid);//打印回收子进程号
66
67
68 }
69
70 printf("pipewrite = %d,pipered = %d\n",fd[1], fd[0]);
71
72 return 0;
73
74
75 }
此处引入内核区文件描述符的基本知识,默认0,1,2文件描述符为STDIN,STDOUT,STDERROR描述符,进程管道的文件描述符为3号和4号,分别对应读和写。
int dup2(int oldfd, int newfd);
文件描述符重定向使用dup2,将管道文件描述符重定向至标注输入输出文件描述符。
运行程序,两个子进程分别向管道内写入内容,父进程打印信息。