颖儿个人资料,中医养生保健知识,好好先生快播
进程间通信:ipc概念
ipc:interprocess communication,通过内核提供的缓冲区进行数据交换的机制。
ipc通信的方式:
通信种类:
pipe通信是单双工的。
#include <unistd.h> int pipe(int pipefd[2]);
例子:
#include <unistd.h> #include <sys/types.h> #include <stdio.h> int main(){ int fds[2]; pipe(fds); pid_t pid = fork(); if(pid == 0){ write(fds[1], "hello\n", 6); char buf[10] = {0}; int ret = read(fds[0], buf, sizeof buf); if(ret > 0){ printf("%s", buf); } } if(pid > 0){ char buf[10] = {0}; int ret = read(fds[0], buf, sizeof buf); if(ret > 0){ printf("%s", buf); } write(fds[1], "world\n", 6); sleep(1); } }
例子1:子进程写,父进程读。
#include <unistd.h> #include <sys/types.h> #include <stdio.h> int main(){ int fds[2]; pipe(fds); pid_t pid = fork(); if(pid == 0){ write(fds[1], "hello\n", 6); /* char buf[10] = {0}; int ret = read(fds[0], buf, sizeof buf); if(ret > 0){ printf("%s", buf); } */ } if(pid > 0){ char buf[10] = {0}; int ret = read(fds[0], buf, sizeof buf); if(ret > 0){ printf("%s", buf); } //write(fds[1], "world\n", 6); //sleep(1); } }
例子2:用管道实现【ps aux | grep bash】命令。
实现办法,用dup2函数把标准输出,重定向到写端;再把标准输入重定向到读端。
#include <unistd.h> #include <sys/types.h> #include <stdio.h> int main(){ int fds[2]; pipe(fds); pid_t pid = fork(); int stdoutfd = dup(stdout_fileno); if(pid == 0){ //close(fds[0]);//------------① dup2(fds[1], stdout_fileno); execlp("ps", "ps", "aux", null); } if(pid > 0){ //close(fds[1]);//----------② dup2(fds[0], stdin_fileno); execlp("grep", "grep", "bash", null); dup2(stdoutfd, stdout_fileno); } }
运行结果:发现程序没有结束,阻塞住了,必须按ctol-c才能结束。
ys@ys:~/test$ ./pi2 ys 1551 0.0 0.2 29692 5548 pts/0 ss 10:05 0:00 bash ys 2316 0.0 0.2 29560 5328 pts/1 ss+ 11:33 0:00 bash ys 2486 0.0 0.0 21536 1060 pts/0 s+ 11:56 0:00 grep bash
用【ps aux】调查一下,发现,由于父进程【grep bash】没有结束还没有回收子进程,导致【ps】变成了僵尸进程。
ys 2437 0.0 0.0 21536 1088 pts/0 s+ 11:50 0:00 grep bash ys 2438 0.1 0.0 0 0 pts/0 z+ 11:50 0:00 [ps] <defunct> ys 2439 0.0 0.1 44472 3800 pts/1 r+ 11:50 0:00 ps aux
为什么父进程【grep bash】没有结束呢?确实在子进程里给父进程【ps aux】的输出结果了啊!
这是grep命令本身的缘故,在终端执行【grep bash】的话,就变成了阻塞状态,grep在等待标准输入,如果输入了【bash】grep就会给出结果,但是还是在继续等待标准输入,所以这就是父进程没有结束,阻塞在【grep bash】那里的原因。
解决办法:告诉【grep】,管道的写端不会再写入数据了后,grep就不会再继续等待,所以grep就会结束。grep的结束了,父进程也就结束了,所以僵尸进程也就自动消失了。
需要改代码的地方是②处,加上【close(fds[1]);】,就告诉了grep,已经没有写入了,所以grep就不会阻塞,父进程就能够结束掉。
注意:其实应该在子进程里也应该加上【close(fds[1]);】,才能达到写端全部关闭了,为什么没写也没错误呢,因为子进程先执行结束了,进程结束后,系统会自动把进程中打开的文件描述符全部关闭,所以没在子进程里写关闭写端的代码,也没出问题。
管道有如下的规则:
例子1:写端全部关闭:read函数返回0。
在①和②两处必须都关闭写端,read函数才能返回0.
#include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <sys/wait.h> int main(){ int fds[2]; pipe(fds); pid_t pid = fork(); if(pid == 0){ char buf[10] = {0}; int ret = read(fds[0], buf, sizeof buf); if(ret > 0){ printf("%s", buf); } close(fds[1]);//----① sleep(1); if(read(fds[0],buf, sizeof buf) == 0){ printf("all closed\n"); } } if(pid > 0){ int ret = write(fds[1], "hello\n", 6); close(fds[1]);//------② wait(null); } }
例子2:读端全部关闭:write函数会产生sigpipe信号,程序异常结束。
在①和②两处必须都关闭读端,write函数会产生sigpipe信号。
#include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <sys/wait.h> int main(){ int fds[2]; pipe(fds); pid_t pid = fork(); if(pid == 0){ close(fds[0]);//------② int ret = write(fds[1], "hello\n", 6); } if(pid > 0){ close(fds[0]);//----① //close(fds[1]); int status; wait(&status); if(wifsignaled(status)){ printf("killed by %d\n", wtermsig(status)); } } }
执行结果:【killed by 13】。13是sigpipe
查看系统默认的管道缓冲区的大小:ulimit -a
pipe size (512 bytes, -p) 8
查看系统默认的管道缓冲区的大小的函数:fpathconf
#include <unistd.h> long fpathconf(int fd, int name);
例子:
#include <unistd.h> #include <stdio.h> int main(){ int fds[2]; pipe(fds); long ret = fpathconf(fds[0], _pc_pipe_buf); printf("size:%ld\n", ret); }
执行结果:size:4096
上面的【例子:用管道实现【ps aux | grep bash】命令】有个问题,父进程直接调用了exec函数,导致无法在父进程中回收子进程的资源。下面的例子就去解决这个问题,方法是,不在父进程里调用exec函数,在2个兄弟子进程里分别调用exec函数,然后在父进程里回收资源。
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> int main(){ int fds[2]; pipe(fds); pid_t pid = fork(); if(pid == 0){ pid_t pid1 = fork(); if(pid1 == 0){ dup2(fds[1], stdout_fileno); execlp("ps", "ps", "aux", null); } else if(pid1 > 0){ close(fds[1]);//----① dup2(fds[0], stdin_fileno); execlp("grep", "grep", "bash", null); //dup2(stdoutfd, stdout_fileno); } } else if(pid > 0){ close(fds[1]);//----② wait(null); } }
注意在①和②处的关闭代码。
到此为止,可以看出来管道的
创建fifo伪文件的命令:【mkfifo】
prw-r--r-- 1 ys ys 0 4月 29 15:59 myfifo
文件类型为p,大小为0。
也可以用函数:mkfifo创建
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
fifo通信原理:内核对fifo文件开辟一个缓冲区,操作fifo伪文件,就相当于操作缓冲区,实现里进程间的通信。实际上就是文件读写。
fifo例子:传进一个事先用mkfifo 创建好的fifo文件。可以同时打开多个读端和写端。
写端:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> int main(int argc, char* argv[]){ printf("begin write\n"); int fd = open(argv[1], o_wronly); printf("end write\n"); int num = 0; char buf[20] = {0}; while(1){ sprintf(buf, "num=%04d\n", num++); write(fd, buf, strlen(buf)); sleep(1); } close(fd); }
读端:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <string.h> int main(int argc, char* argv[]){ printf("begin read\n"); int fd = open(argv[1], o_rdonly); printf("end read\n"); int num = 0; char buf[20] = {0}; while(1){ memset(buf, 0x00, sizeof buf); int ret = read(fd, buf, sizeof buf); if(ret > 0){ printf("%s\n", buf); } else if(ret == 0){ break; } sleep(1); } close(fd); }
例子里有两个注意点:
open的时候是阻塞的,只有当读端和写端都打开后,open函数才会返回。非fifo文件的open函数不是阻塞的。
fifos opening the read or write end of a fifo blocks until the other end is also opened (by another process or thread). see fifo(7) for further details.
强制终止读端进程后,写端会自动终止。理由是读端已经关闭了,再往里写就会收到sigfifo信号,这个和管道的原理是一样的。
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
如何在没有core文件的情况下用dmesg+addr2line定位段错误
用QT制作3D点云显示器——QtDataVisualization
网友评论