合肥外贸网站推广,wordpress阅读量,营销推广模式有哪些,系统软件使用管道需要注意以下4种特殊情况#xff08;默认都是阻塞I/O操作#xff0c;没有设置O_NONBLOCK标志#xff09;#xff1a;
1. 如果所有指向管道写端的文件描述符都关闭了#xff08;管道写端引用计数为0#xff09;#xff0c;而仍然有进程从管道的读端读数据#…使用管道需要注意以下4种特殊情况默认都是阻塞I/O操作没有设置O_NONBLOCK标志
1. 如果所有指向管道写端的文件描述符都关闭了管道写端引用计数为0而仍然有进程从管道的读端读数据那么管道中剩余的数据都被读取后再次read会返回0就像读到文件末尾一样。
2. 如果有指向管道写端的文件描述符没关闭管道写端引用计数大于0而持有管道写端的进程也没有向管道中写数据这时有进程从管道读端读数据那么管道中剩余的数据都被读取后再次read会阻塞直到管道中有数据可读了才读取数据并返回。
3. 如果所有指向管道读端的文件描述符都关闭了管道读端引用计数为0这时有进程向管道的写端write那么该进程会收到信号SIGPIPE通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉不终止进程。具体方法信号章节详细介绍。
4. 如果有指向管道读端的文件描述符没关闭管道读端引用计数大于0而持有管道读端的进程也没有从管道中读数据这时有进程向管道写端写数据那么在管道被写满时再次write会阻塞直到管道中有空位置了才写入数据并返回。
总结
读管道1.管道中有数据read返回实际读到的字节数。2.管道中无数据管道写端被全部关闭read返回0好像读到文件结尾写端没有全部被关闭read阻塞等待不久的将来可能有数据递达此时会让出cpu。
写管道1.管道读端全部被关闭进程异常终止也可使用捕捉SIGPIPE信号使进程不终止。2. 管道读端没有全部关闭管道已满write阻塞管道未满write将数据写入并返回实际写入的字节数。
重点注意
如果写入的数据大小nPIPE_BUF时linux保证写入的原子性即要么不写要么全写入。如果没有足够的空间供n个字节全部写入则会阻塞直到有足够空间供n个字节全部写入如果写入的数据大小nPIPE_BUF时写入不再具有原子性可能中间有其它进程穿插写入其自身也会阻塞直到将n字节全部写入在才返回写入的字节数否则阻塞等待。
读数据时如果请求读取的数据read函数的缓冲区大小PIPE_BUF则直接返回管道中现有的数据字节数即将管道中的数据全部读出如果 PIPE_BUF则返回管道中现有的数据字节数此时管道中的实际数据量请求的数据量大小或者返回请求数据量的大小。
练习1父子进程使用管道通信父写入字符串子进程读出并打印到屏幕。
#include stdio.h
#include stdlib.h
#include unistd.h
#include string.hint main(void)
{int ret,fd1;char *pzhangshuxiong\n;int fd[2];ret pipe(fd);if(ret -1){perror(pipe);exit(1);}fd1 fork( );if(fd1 -1){perror(fork);exit(1);}else if(fd1 0) {sleep(3); //子进程睡3秒close(fd[1]); //子进程关闭写端char buff[1024]{0};ret read(fd[0],buff,1024); //子进程读数据if(ret -1){perror(read);exit(1);}else if(ret 0) {printf(父进程没有向管道里写入数据\n);}else {int res write(STDOUT_FILENO,buff,ret); //将读出的数据输出到屏幕if(res -1){perror(write);exit(1);}}close(fd[0]); //子进程结束前关闭掉文件描述符}else {close(fd[0]);int rer write(fd[1],p,strlen(p)); //父进程写入数据if(rer -1){perror(write);exit(1);}close(fd[1]); //父进程结束前关闭掉文件描述符wait( NULL ); //父进程回收阻塞等待}return 0;
}
[rootlocalhost pipe]# ./pip
zhangshuxiong
[rootlocalhost pipe]# //可见如果没有wait则父进程会先结束正因为有了wait父进程会等待子进程结束最后shell进程才会收回前台等待与用户交互。注意即使没有sleep函数依然能保证子进程运行时一定会读到数据因为是阻塞读。 练习2使用管道实现父子进程间通信完成ls | wc –l。假定父进程实现ls子进程实现wc。
[rootlocalhost pipe]# ls
makefile pip pip.c pipe pipe1 pipe1.c pipe2 pipe2.c pipe3 pipe3.c pipe.c pipe_test pipe_test.c test
[rootlocalhost pipe]# ls | wc –l //统计文件的字数
14
其实 ls | wc –l命令执行后shell进程会创建两个子进程并创建一个管道用于两子进程通信下面给出详细实现过程
#include stdio.h
#include stdlib.h
#include unistd.hint main(void)
{int ret,fd1;int fd[2];ret pipe(fd);if(ret -1){perror(pipe);exit(1);}fd1 fork( );if(fd1 -1){perror(fork);exit(1);}else if(fd1 0) {close(fd[1]);int as dup2(fd[0],STDIN_FILENO); //将标准输入重定向到管道读端if(as -1){perror(dup2);exit(1);}close(fd[0]); //只是关了fd[0]不关也可以进程结束会自动关闭execlp(wc,wc,-l,NULL); //该命令从标准输入读取文本}else {close(fd[0]);int as dup2(fd[1],STDOUT_FILENO); //将标准输出重定向到管道写端if(as -1){perror(dup2);exit(1);}execlp(ls,ls,NULL); ///该命令结果会写到标准输出}return 0;
}
[rootlocalhost pipe]# ./pip
14 //可见跟ls | wc –l的结果一样
注意上述程序并没有考虑到子进程的回收问题如果父进程比子进程先结束子进程会被init进程回收后结束子进程会先变为僵尸进程等父进程结束了再被init进程回收。
ls命令正常会将结果集写出到stdout但现在会写入管道的写端wc –l 正常应该从stdin读取数据但此时会从管道的读端读。
也有可能会出现这种情况程序执行发现程序执行结束shell还在阻塞等待用户输入。这是因为shell → fork → ./pipe1 程序pipe1的子进程将stdin重定向给管道父进程执行的ls会将结果集通过管道写给子进程。若父进程在子进程打印wc的结果到屏幕之前被shell调用wait回收shell就会先输出$提示符。 练习3使用管道实现兄弟进程间通信。 兄ls 弟 wc -l 父等待回收子进程。要求使用“循环创建N个子进程”模型创建兄弟进程使用循环因子i标示。注意管道读写行为。
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/wait.h
#include sys/types.hint main(void)
{int i,ret,fd1;int n2;int fd[2];ret pipe(fd);if(ret -1){perror(pipe);exit(1);}for(i0;in;i){fd1 fork( );if(fd1 -1){perror(fork);exit(1);}else if(fd1 0)break;}if(i n){close(fd[0]);close(fd[1]); //特别强调父进程不用管道必须要关掉否则运行出错为了维护管道的单向通信int status;do {pid_t pidwaitpid(-1,status,0);if(pid 0)n--;if(pid -1){perror(waitpid);exit(1);}if(WIFEXITED(status))printf(the child process of exit with %d\n,WEXITSTATUS(status));else if(WIFSIGNALED(status))printf(the child process was killed by %dth signal\n,WTERMSIG(status));}while(n0);}else if(i 1) {close(fd[1]);int as dup2(fd[0],STDIN_FILENO);if(as -1){perror(dup2);exit(1);}close(fd[0]);execlp(wc,wc,-l,NULL);}else {close(fd[0]);int as dup2(fd[1],STDOUT_FILENO);if(as -1){perror(dup2);exit(1);}execlp(ls,ls,NULL);}return 0;
}
[rootlocalhost pipe]# ./pip
14
the child process of exit with 0
the child process of exit with 0
强调一点在使用管道传递数据之前不用的管道读或写端都必须要关闭这是为了维护管道的正常运行单向通信。 测试是否允许一个pipe有一个写端多个读端呢是否允许有一个读端多个写端呢
#include stdio.h
#include unistd.h
#include sys/wait.h
#include string.h
#include stdlib.hint main(void)
{pid_t pid;int fd[2], i, n;char buf[1024];int ret pipe(fd);if(ret -1){perror(pipe error);exit(1);}for(i 0; i 2; i){if((pid fork()) 0)break;else if(pid -1){perror(pipe error);exit(1);}}if (i 0) {close(fd[0]);write(fd[1], 1.hello\n, strlen(1.hello\n));} else if(i 1) {close(fd[0]);write(fd[1], 2.world\n, strlen(2.world\n));} else {close(fd[1]); //父进程关闭写端,留读端读取数据 //sleep(1); //这条语句是很关键的n read(fd[0], buf, 1024); //从管道中读数据write(STDOUT_FILENO, buf, n);for(i 0; i 2; i) //两个儿子wait两次wait(NULL);}return 0;
}
如果父进程不睡眠
[rootlocalhost pipe]# ./pipe3
2.world
1.hello
[rootlocalhost pipe]# ./pipe3
1.hello
[rootlocalhost pipe]# ./pipe3
2.world
可见三个进程的执行顺序是随机的如果两个子进程在父进程读之前都先写入那么两个都会读出。为了确保两个都读出可以使用读两次的方法也可以让父进程先睡眠一会如下
如果父进程睡眠
[rootlocalhost pipe]# ./pipe3
1.hello
2.world
[rootlocalhost pipe]# ./pipe3
1.hello
2.world 最终练习统计当前系统中进程ID大于10000的进程个数。
提示 采用awk命令可以统计文本中符合条件列的个数及和。运用ps aux和管道。