进程间通信(Inter Process Communication),简称IPC 进程间通信的四种方式:
管道 匿名管道(pipe)–血缘关系进程间通信 有名管道(fifo) --有无血缘关系均可信号共享内存–有无血缘关系均可本地套接字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,将管道文件描述符重定向至标注输入输出文件描述符。 运行程序,两个子进程分别向管道内写入内容,父进程打印信息。