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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32095699/article/details/88619098

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

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


管道操作符:|

格式:命令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)

前面所讨论的管道为匿名管道,存储与内存中,进程之间有亲缘关系

而命名管道,是以文件的新式存于文件系统,进程间不存在亲缘关系,只要路径可访问,就能通过命名管道通信。 

猜你喜欢

转载自blog.csdn.net/qq_32095699/article/details/88619098