《Linux 编程 c》学习笔记--管道

15 篇文章 0 订阅

管道是半双工的,通常来说,只能在具有相同祖先的进程间使用,例如(父子进程,兄弟进程)

数据读出后,管道中就没有数据了。


管道操作符:|

格式:命令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)作结束。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值