管道是半双工的,通常来说,只能在具有相同祖先的进程间使用,例如(父子进程,兄弟进程)
数据读出后,管道中就没有数据了。
管道操作符:|
格式:命令A | 命令B | 命令C... | 命令N
前一个命令的输出作为后一个命令的输入
例如:kill - l | grep SIGKILL
在信号列表中查找SIGKILL
管道结构:
注意!管道位于内核缓冲区,用环形队列来实现
管道有两个文件描述符,一个用于管道输入,一个用于管道输出
父进程通过fork生成子进程,因此子进程可以从父进程那继承到读写管道的描述符:
如果数据从父进程传到子进程,则父进程关闭读描述符, 子进程关闭写描述符。
int pipe(int pipefd[2]);
pipefd[2]为文件描述符数组,其中pipefd[0]是读描述符,pipefd[1]是写描述符,注意一定不能反了!
创建成功返回0,失败返回1
例子1:在一个进程中创建pipe
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int fd[2];
char writebuf[]="this is a test!\n";
char readbuf[20];
if(pipe(fd)<0){
printf("fail to create pipe!\n");
exit(0);
}
write(fd[1],writebuf,sizeof(writebuf));
read(fd[0],readbuf,sizeof(writebuf));
printf("%s",readbuf);
printf("read descripter f[0] = %d\n",fd[0]);
printf("write descripter f[1] = %d\n",fd[1]);
close(fd[1]);
close(fd[0]);
return 0;
}
输出:
this is a test!
read descripter f[0] = 3
write descripter f[1] = 4
例子2:父子进程中使用管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int fd[2];
char writebuf[] = "parent send to child!\n";
char readbuf[30];
if(pipe(fd)<0){
printf("fail to create pipe!\n");
exit(0);
}
pid_t pid = fork();
if(pid<0){
printf("error!\n");
exit(0);
}
else if(pid ==0){
printf("this is child\n");
close(fd[1]);
read(fd[0],readbuf,sizeof(writebuf));
printf("%s",readbuf);
}
else{
printf("this is parent ..\n");
close(fd[0]);
write(fd[1],writebuf,sizeof(writebuf));
}
return 0;
}
输出:
this is parent ..
this is child
parent send to child!
例子3:兄弟进程中使用管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFERSIZE 4096
int main(int argc, char const *argv[])
{
int fd[2];
char buf[BUFFERSIZE];
if(pipe(fd)<0)
perror("pipe failed!\n");
pid_t pid1 = fork();
if(pid1<0){
printf("fork1 error!\n");
exit(0);
}
else if(pid1 == 0){
close(fd[0]);
char buf1[]="hello brother !\n";
strcpy(buf,buf1);
printf("I am child1 sneding info to my brother..\n");
write(fd[1],buf,strlen(buf));
exit(0);
}
pid_t pid2 = fork();
if(pid2<0){
printf("fork2 error\n");
exit(0);
}
else if(pid2 ==0){
close(fd[1]);
read(fd[0],buf,BUFFERSIZE);
printf("I am child2..\n");
printf("len=%d\n",(int)strlen(buf));
printf("child2 recv buf = %s",buf );
exit(0);
}
else{
close(fd[0]);
close(fd[1]);
exit(0);
}
return 0;
}
输出:
I am child1 sneding info to my brother..
I am child2..
len=16
child2 recv buf = hello brother !
例子4:一个进程写管道,两个进程读
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFERSIZE 256
int main(int argc, char const *argv[])
{
int fd[2];
char buf[BUFFERSIZE];
if(pipe(fd)<0)
perror("pipe failed!\n");
pid_t pid1 = fork();
if(pid1<0){
printf("fork1 error!\n");
exit(0);
}
else if(pid1 == 0){
close(fd[0]);
char buf1[]="hello brother !\n";
strcpy(buf,buf1);
printf("I am child1 sneding info to my brother..\n");
write(fd[1],buf,strlen(buf));
printf("pid1 write finished............\n");
close(fd[1]);
exit(0);
}
pid_t pid2 = fork();
if(pid2<0){
printf("fork2 error\n");
exit(0);
}
else if(pid2 ==0){
close(fd[1]);
//sleep(3);
printf("pid2 begin read...............\n");
read(fd[0],buf,BUFFERSIZE);
printf("I am child2 and my recv buf = %s",buf );
exit(0);
}
pid_t pid3 = fork();
if(pid3<0){
printf("fork2 error\n");
exit(0);
}
else if(pid3==0){
close(fd[1]);
printf("pid3 begin read...............\n");
read(fd[0],buf,BUFFERSIZE);
printf("I am child3 and my recv buf = %s",buf );
exit(0);
}
else{
close(fd[0]);
close(fd[1]);
exit(0);
}
return 0;
}
进程pid1写完后关闭写端,有可能是pid2读到buf,也有可能是pid3读到buf。由于写端已经关闭,read不会阻塞!
输出:
I am child1 sneding info to my brother..
pid1 write finished............
pid2 begin read...............
I am child2 and my recv buf = hello brother !
pid3 begin read...............
I am child3 and my recv buf =
我们这里设置的BUFFERSIZE=256
在buf小于BUFFERSIZE的情况下,所有的读操作或者写操作都是原子操作。谁先抢到了谁先读,就是当pid2读到管道数据时,会一次把所有数据读完。所以pid3并没有读到数据。
我们把传输的字符串写得长一些,使其大于BUFFERSIZE:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFERSIZE 256
int main(int argc, char const *argv[])
{
int fd[2];
char buf[BUFFERSIZE];
if(pipe(fd)<0)
perror("pipe failed!\n");
pid_t pid1 = fork();
if(pid1<0){
printf("fork1 error!\n");
exit(0);
}
else if(pid1 == 0){
close(fd[0]);
char buf1[]="hello brother hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh !\n";
strcpy(buf,buf1);
printf("I am child1 sneding info to my brother..\n");
write(fd[1],buf,strlen(buf));
printf("pid1 write finished............\n");
close(fd[1]);
exit(0);
}
pid_t pid2 = fork();
if(pid2<0){
printf("fork2 error\n");
exit(0);
}
else if(pid2 ==0){
close(fd[1]);
//sleep(3);
printf("pid2 begin read...............\n");
read(fd[0],buf,BUFFERSIZE);
printf("I am child2 and my recv buf = %s",buf );
exit(0);
}
pid_t pid3 = fork();
if(pid3<0){
printf("fork2 error\n");
exit(0);
}
else if(pid3==0){
close(fd[1]);
printf("pid3 begin read...............\n");
read(fd[0],buf,BUFFERSIZE);
printf("I am child3 and my recv buf = %s",buf );
exit(0);
}
else{
close(fd[0]);
close(fd[1]);
exit(0);
}
return 0;
}
输出:
I am child1 sneding info to my brother..
pid1 write finished............
pid2 begin read...............
I am child2 and my recv buf = hello brother hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhpid3 begin read...............
I am child3 and my recv buf = hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh !
可以看到pid2读了256B,剩余的友pid3读取。
例子5:两个进程写管道,一个进程读管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFERSIZE 256
int main(int argc, char const *argv[])
{
int fd[2];
char buf[BUFFERSIZE];
if(pipe(fd)<0)
perror("pipe failed!\n");
pid_t pid1 = fork();
if(pid1<0){
printf("fork1 error!\n");
exit(0);
}
else if(pid1 == 0){
close(fd[0]);
char buf1[]="hello brother I am pid1 !\n";
strcpy(buf,buf1);
printf("I am child1 sneding info to my brother..\n");
write(fd[1],buf,strlen(buf));
printf("pid1 write finished............\n");
close(fd[1]);
exit(0);
}
pid_t pid2 = fork();
if(pid2<0){
printf("fork2 error\n");
exit(0);
}
else if(pid2 ==0){
close(fd[0]);
char buf1[]="hello brother I am pid2 !";
strcpy(buf,buf1);
printf("I am child2 sneding info to my brother..\n");
write(fd[1],buf,strlen(buf));
printf("pid2 write finished............\n");
close(fd[1]);
exit(0);
}
pid_t pid3 = fork();
if(pid3<0){
printf("fork2 error\n");
exit(0);
}
else if(pid3==0){
close(fd[1]);
printf("pid3 begin read...............\n");
read(fd[0],buf,BUFFERSIZE);
printf("I am child3 and my recv buf = %s",buf );
exit(0);
}
else{
close(fd[0]);
close(fd[1]);
exit(0);
}
return 0;
}
输出:
I am child1 sneding info to my brother..
pid1 write finished............
I am child2 sneding info to my brother..
pid2 write finished............
pid3 begin read...............
I am child3 and my recv buf = hello brother I am pid1 !
hello brother I am pid2 !
命名管道(FIFO)
前面所讨论的管道为匿名管道,存储与内存中,进程之间有亲缘关系
而命名管道,是以文件的新式存于文件系统,进程间不存在亲缘关系,只要路径可访问,就能通过命名管道通信。
有名管道具有以下特点:
1. 它可以使互不相关的两个进程间实 现彼此通信;
2. 该管道可以通过路径名来指出,并且在文件系统中是可见的 。 在建立了管道之后,两个进程就可以把
它当作普通文件一样进行读写操作,使用非常方便 ;
3. FIFO 严格地遵循先进先出规则,对管道及 FIFO 的读操作总是从开始处返回数据,对它们的写操作则是把数据添加到末尾
命名管道读:
name_pipe.cpp
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define P_FIFO "/tmp/p_fifo"
int main(int argc, char const *argv[])
{
char cache[100];
int fd;
memset(cache,0,sizeof(cache));
if(access(P_FIFO,F_OK)==0){/*管道文件存在 */
execlp("rm","-f",P_FIFO,NULL);
printf("access\n");
}
if(mkfifo(P_FIFO,0777)<0){
printf("createnamed pipe failed.\n");
}
fd = open(P_FIFO,O_RDONLY|O_NONBLOCK); /**/
while(1){
memset(cache,0,sizeof(cache));
if(read(fd,cache,100)==0){
printf("nodata\n");
}
else{
printf("getdata:%s\n",cache);
}
sleep(1);
}
close(fd);
return 0;
}
命名管道写:
pipe_write.cpp
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define P_FIFO "/tmp/p_fifo"
int main(int argc, char const *argv[])
{
int fd;
if(argc<2){
printf("please input the write data\n");
}
fd = open(P_FIFO,O_WRONLY|O_NONBLOCK);
write(fd,argv[1],100);
close(fd);
return 0;
}
mkfifo(P_FIFO,0777) 创建管道
ttps://blog.csdn.net/21aspnet/article/details/7568067
execlp
从PATH 环境变量中查找文件并执行
定义:
int execlp(const char * file,const char * arg,……);
头文件:
#include<unistd.h>
说明:
execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0]、argv[1]……, 最后一个参数必须用空指针(NULL)作结束。