Linux进程间通信二之管道(含代码)
来源:互联网 发布:ntfs for mac破解版 编辑:程序博客网 时间:2024/05/21 08:40
Linux进程间通信二之管道
默认情况下,一个进程会打开三个默认的设备文件:标准输入设备即键盘,标准输出设备即显示器,标准错误输出设备即显示器。默认从标准输入读取信息,将正确的信息写入到标准输出,将错误的信息写入到标准输出。在shell命令应用中“|”可以将两个命令连接起来,将前一个命令的输出作为另一个命令的输入,连接输入输出的中间设备即为一个管道文件。这种管道是临时的,命令执行完后会自动消失,这类管道称为无名管道。
无名管道是一种特殊的文件,它不属于文件系统,而是对于内核中的一段内存区域,由操作系统管理和维护,应用程序只能通过系统调用访问它。
无名管道的内核资源会在进程退出后自动释放。不能跟普通文件那样存放大量常规数据。在编程应用方面具有普通文件一样的特点,可以用read/write函数读写,但不能用lseek函数修改读写的位置,管道需要满足先进先出的原则。
无名管道的操作:
1.创建无名管道
int fd[2];
pipe(fd[2]);
如果执行成功它会存储两个整型的文件描述符于fd[2],它分别代表管道的两端。系统调用失败会返回-1。无名管道是单向的,即只能从一个进程向另一个进程发送信息。管道中的数据从fd[0]读文件描述符读出,输入到管道中的数据从fd[1]写文件描述符写入。如果需要全双工的需要两个管道。
2.读写无名管道
任何的进程读/写无名管道时必须确认还存在一个进程(可以是自己),该进程以写/读的方式访问管道(可以操作相应的文件描述符)。读写管道用read和write,两者以堵塞方式读写管道,可以用fcntl函数修改。
(1)以堵塞的方式读无名管道,如果当前没有一个进程(包括当前进程)可以访问写端,读操作将立即返回。并按如写操作:
如果管道现有数据无数据,立即返回0;
如果管道现有数据大于要读出的数据,立即读出期望大小的数据;
如果管道现有数据小于要读出的数据,立即读出现有所有的数据;
#include<stdio.h>#include<unistd.h>int main(void){ int p[2]; pipe(p); close(p[1]); char buf[128]; memset(buf,'\0',128); int ret=-1; ret=read(p[0],buf,128); printf("buf=%s\n",buf); printf("ret=%d\n",ret);}情况一验证如上代码;
#include<stdio.h>#include<unistd.h>int main(void){ int p[2]; pipe(p); write(p[1],"hello world",10); close(p[1]); char buf[128]; memset(buf,'\0',128); int ret=-1; ret=read(p[0],buf,3); printf("first ret=%d,buf=%s\n",ret,buf); ret=read(p[0],buf,15); printf("second ret=%d,buf=%s\n",ret,buf);}
情况二三验证如上;
(2) 如果以堵塞的方式读无名管道,有某个进程可以访问写端,按如下操作;
如果管道现有数据无数据,读操作堵塞;
如果管道现有数据大于要读出的数据,读出期望大小的数据;
如果管道现有数据小于要读出的数据,读出现有所有的数据;
#include<stdio.h>#include<unistd.h>int main(void){ int p[2]; pipe(p); char buf[128]; memset(buf,'\0',128); read(p[0],buf,128); printf("buf=%s\n",buf);}情况一验证如上,二三不再验证;
(3)如果以堵塞的方式写无名管道,但是没有某个进程可以访问读端,写操作将收到SIGPIPE信号,write()返回-1,如果某个进程可以可以访问读端,则写入成功。
#include<stdio.h>#include<unistd.h>#include<signal.h>/*信号处理函数*/void handler(int sig){ if(SIGPIPE==sig) printf("recv SIGPIPE\n");}int main(){ int p[2]; signal(SIGPIPE,handler); pipe(p); close(p[0]); int ret=0; ret=write(p[1],"hello world",10); printf("ret=%d\n",ret);}
验证如上;
补充signal()用法:
第一个参数指明所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
第二个参数描述了与信号关联的动作,它可以取以下三种值:
1)一个无返回值的函数地址.此函数必须在signal()被调用前申明,handler为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。sig是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行handler()函数。当handler()函数执行结束后,控制权返回进程被中断的那一点继续执行。
2)SIG_IGN.这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
3)SIG_DFL.这个符号表示恢复系统对信号的默认处理。
signal()会依第一个参数指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。返回值:返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。
(4)如果以堵塞的方式写无名管道,但是当前管道已满,写操作就会发生堵塞。若是有好多进程试图写管道,有进程读操作会唤醒写管道,唤醒那一个不一定。写操作写入数据大小建议小于PIPE_BUF大小(默认4096)。
(5)如果以O_NDELAY和O_NONBLOCK设置了管道读端,如果写管道无数据,将立即返回-1.且置errno为EAGAIN错误。
(6)如果以O_NDELAY和O_NONBLOCK设置了管道写端,如果管道无空间,将立即返回-1.且置errno为EAGAIN错误。
无名管道是临时的,完成通信后就会消失。有名管道依赖文件系统,是一个存在的特殊文件,具有磁盘存放路径,文件权限和其他属性;有名管道并没有在磁盘上存放真正的信息,它存储的通信信息在内存中,两个进程结束后会自动丢失,拥有的磁盘路径仅仅是一个接口。通信的两个进程结束后,有名管道的文件路径本身依然存在。
命令行应用:
mknod 管道名 p;
例如:mknod pipe p;
echo test>pipe&;//写管道
cat<pipe;//读管道
1.创建有名管道
mkfifo()函数,第一个参数为建立有名管道文件名,第二个为生成文件的模式。mkfifo()建立有名管道时,该有名管道文件名必须不存在,mkfifo()建立的FIFI文件其他进程可以用读写一般文件的方式存取。执行成功返回0,否则返回-1,失败原因存储在errno中。
2.读写有名管道
读写操作之前必须先open()函数打开该文件。
1)如果进程以某种方式打开管道操作,系统将堵塞该进程,直到另一个进程以另一种方式打开该管道后才会继续执行。当然一个进程可以以读写的方式打开管道。
2)两进程已经完成打开管道操作,堵塞读操作按以下方式进行。
如果管道现有数据无数据,读操作堵塞;
如果管道现有数据大于要读出的数据,读出期望大小的数据;
如果管道现有数据小于要读出的数据,读出现有所有的数据;
3)两进程已经完成打开管道操作,堵塞写操作按以下方式进行。
如果管道中没有空间,写操作堵塞;
如果管道中有空间,但小于欲写入的数据,写满空间后堵塞;
如果管道中有空间,且大于欲写入的数据,写入数据后返回;
4)两进程已经完成打开管道操作,中途其中一个进程退出
未退出一端如果是写操作,将返回SIGPIPE信号;
未退出一端如果是堵塞读操作,读操作将不再堵塞,直接返回0;
非亲缘关系进程使用有名管道通信实例:
向有名管道中写入数据源代码如下:
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<limits.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#define FIFO_NAME "/tmp/my_fifo"int main(int argc,char* argv[]){ int pipe_fd; int res; char buf[]="hello world!"; if(access(FIFO_NAME,F_OK)==-1)//文件是否存在 { res=mkfifo(FIFO_NAME,0766); if(res!=0) { fprintf(stderr,"could not create fifo %s\n",FIFO_NAME); exit(EXIT_FAILURE); } } printf("Process %d opening FIFO O_WRONLY\n",getpid());//打印提示信息 pipe_fd=open(FIFO_NAME,O_WRONLY); printf("file's descriptor is %d\n",pipe_fd); if(pipe_fd!=-1) { res=write(pipe_fd,buf,sizeof(buf)); if(res==-1) { fprintf(stderr,"Write error on pipe\n"); exit(EXIT_FAILURE); } printf("write data is %s,%d bytes is write\n",buf,res);//打印写入的数据 close(pipe_fd); } else exit(EXIT_FAILURE); printf("Process %d finished,\n",getpid()); exit(EXIT_SUCCESS);}向有名管道中读取数据源代码如下:
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<limits.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#define FIFO_NAME "/tmp/my_fifo"int main(int argc,char* argv[]){ int pipe_fd; int res; char buf[4096]; int bytes_read=0; memset(buf,'\0',sizeof(buf)); printf("Process %d opening FIFO O_RDONLY\n",getpid());//打印提示信息 pipe_fd=open(FIFO_NAME,O_RDONLY); printf("file's descriptor is %d\n",pipe_fd); if(pipe_fd!=-1) { bytes_read=read(pipe_fd,buf,sizeof(buf));//读数据输出 printf("the read data is %s\n",buf); close(pipe_fd); } else exit(EXIT_FAILURE); printf("Process %d finished,%d bytes read\n",getpid(),bytes_read); exit(EXIT_SUCCESS);}
- Linux进程间通信二之管道(含代码)
- linux进程通信--管道(pipe)(含实例代码)
- linux进程通信--有名管道(FIFO)(含实例代码)
- Linux环境编程之IPC进程间通信(二):管道
- Linux--进程间通信之命名管道(代码实现)
- Linux进程间通信之管道通信
- Linux进程间通信之管道通信
- Linux进程间通信(二)---管道通信之无名管道及其基础实验
- Linux进程间通信(二)---管道通信之无名管道及其基础实验
- Linux进程间通信(二)---管道通信之无名管道及其基础实验
- Linux进程间通信(二)管道
- linux进程间通信之管道
- Linux进程间通信之---管道
- Linux进程间通信之管道初识
- Linux进程间通信之管道(pipe)
- linux进程间通信之匿名管道
- linux进程间通信之实名管道
- linux进程间通信之管道篇
- 在对话框上添加软键盘
- [OpenCV2]编写有效率的图像循环
- 研发周报:最火的前端开源项目
- iOS开发中的单元测试
- 算GPS照片之间的距离
- Linux进程间通信二之管道(含代码)
- Linux下patch的制作和应用
- 给Qt—Creator添加include路径方法
- 欧几里得距离(N维)
- VS2010 hotkey
- 理解SapLocation()
- Android开发:service服务里获得屏幕信息
- [C#基础教程]之String.Empty,NULL和""的区别
- apache tomcat 集群 和 负载均衡