0. 进程间通信(IPC inter process communication)的概念
进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问的介质呢?
1)进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。【内存】
2)系统空间是“公共场所”,各进程均可以访问,所以内核也可以提供这样的条件。【内核】
3)双方都可以访问的外设。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息。【磁盘】
1. 管道的工作原理
2. pipe(有名管道)
函数原型
1. #include <unistd.h> 2. 3. int pipe(int filedes[2]);
1)管道作用于有血缘关系的进程之间,通过fork来传递
2)调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]固定用于管道的读端,filedes[1]固定用于管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。
3)pipe函数调用成功返回0,调用失败返回-1。
4)无名管道是单工的工作方式,即同一时刻要么只能读管道,要么只能写管道。父子进程虽然同时拥有管道的读端和写端,但只能使用其中一个。
程序1:实现父进程写,子进程读父进程写的内容,并将读到的内容输出到标准输出。
1. #include <stdio.h> 2. #include <unistd.h> 3. #include <string.h> 4. #include <stdlib.h> 5. #include <wait.h> 6. 7. int main(void) 8. { 9. int fd[2]; 10. char str[1024] = "hello world.\n"; 11. char buf[1024]; 12. pid_t pid; 13. 14. //fd[0]读端, fd[1]写端 15. if(pipe(fd) < 0){ 16. perror("pipe"); 17. exit(1); 18. } 19. 20. pid = fork(); 21. //父写子读 22. if(pid > 0){ //父进程 23. close(fd[0]); //关闭父进程的读端 24. sleep(2); 25. write(fd[1], str, strlen(str)); 26. wait(NULL); 27. } 28. else if(pid == 0){//子进程 29. int len; 30. close(fd[1]); //关闭子进程的写端 31. len = read(fd[0], buf, sizeof(buf)); 32. //sprintf(str, "child %s", buf); 33. write(STDOUT_FILENO, buf, len); //将子进程读到的内容输出到标准输出 34. } 35. else{ 36. perror("fork"); 37. exit(1); 38. } 39. 40. return 0; 41. }
使用pipe管道时要注意:
1、写管道关闭,读端读完管道里面内容时,再次读,返回0,相当于读到EOF
2、写端未关闭,写端暂时无数据;读端读完管道里数据时,再次读,阻塞
3、读端关闭,写端写管道,产生SIGPIPE信号,写进程默认情况下回终止进程
4、读端为读管道数据,当写端写满管道数据后,再次写,阻塞
3. fifo(有名管道)
3.1 fifo原理
有名管道,解决无血缘关系的进程通信,有名管道fifo特点:
1)当只写打开FIFO管道时,如果没有FIFO没有读端打开,则open写打开会阻塞。
2) FIFO内核实现时可以支持双向通信。(pipe单向通信,因为父子进程共享同一个file结构体)
3)FIFO可以一个读端,多个写端;也可以一个写端,多个读端。
通过mkfifo指令创建管道文件(注意在WinShare目录下不能创建管道文件,因为windows没有管道文件)
fifo_write.c----向管道文件中写数据
1. #include <stdio.h> 2. #include <unistd.h> 3. #include <string.h> 4. #include <stdlib.h> 5. #include <sys/types.h> 6. #include <sys/stat.h> 7. #incude <fcntl.h> 8. 9. void sys_err(char *str, int exitno) 10. { 11. perror(str); 12. exit(exitno); 13. } 14. 15. int main(int argc, char * argv[]) 16. { 17. int fd; 18. char buf[1024] = "hello world.\n"; 19. if(argc < 2){ 20. printf("./a.out fifoname.\n"); 21. exit(1); 22. } 23. 24. //fd = open(argv[1], O_RDONLY); 25. fd = open(argv[1], O_WRONLY); 26. 27. if(fd < 0) 28. sys_err("open", 1); 29. 30. write(fd, buf, strlen(buf)); 31. close(fd); 32. 33. return 0; 34. }
fifo_read.c---读取管道文件中的数据
1. #include <stdio.h> 2. #include <unistd.h> 3. #include <sys/types.h> 4. #include <sys/stat.h> 5. #include <fcntl.h> 6. #include <string.h> 7. #include <stdlib.h> 8. 9. void sys_err(char *str, int exitno) 10. { 11. perror(str); 12. exit(exitno); 13. } 14. 15. int main(int argc, char *argv[]) 16. { 17. int fd, len; 18. char buf[1024]; 19. if(argc < 2){ 20. printf("./a.out fifoname.\n"); 21. exit(1); 22. } 23. 24. //fd = open(argv[1], O_RDONLY); 25. fd = open(argv[1], O_RDONLY); 26. if(fd < 0) 27. sys_err("open", 1); 28. 29. len = read(fd, buf, sizeof(buf)); 30. write(STDOUT_FILENO, buf, len); 31. close(fd); 32. 33. return 0; 34. 35. }
执行的时候开两个终端窗口,先执行fifo_write.c,向管道中写数据,可以看到此时是阻塞的;
打开另外一个终端窗口,执行fifo_read.c,可以看到读出管道的数据,并打印在终端。
同时可以看到读写前后管道文件的大小是不变的