LINUX_C编程实战-第十章《进程间通信》-管道

一、进程间的通信(IPC)-管道

管道分为无名管道(pipe),有名管道(FIFO)二种;

区别:无名管道是存在于内存的特殊文件,而有名管道是一个存在硬盘上的文件

无名管道的概念:管道是由内核管理的一个缓冲区,相当于放入内存的一个纸条。管道的一端连接一个进程的输出,这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失

无名管道特点:1、半双工(单向通信)机制,数据只能有一个进程流入下一个进程(一个写管道,一个读管道);全双工通信需要创建两个管道;

                        2、数据只能在具有血缘关系的进程间通信;

                        3、管道没有名字,缓冲区大小一定(被分配一个页面作为缓冲区),是一种特殊的文件—PIPE_BUF;

无名管道的创建与打开:定义头文件 #include<unistd.h>

                                      int pipe(int fd[2]); 成功返回0,数组中包含两个文件描述符,失败返回-1;

                                      fd[0](管道读端) & fd[1](管道写端)用于描述管道的两端,读端只能读,写端只能写;可以用操作I/O文件的方式操作管道;

无名管道的读写:读端在读取数据时应该及时关闭不需要的管道的另一端(写端);写端亦如此

                          ps:必须再fork之前调用pipe函数创建管道,否则子进程无法继承管道的描述符

代码功能如下:主进程通过命令行向管道写入数据,子进程从标准输入里面读出该参数;将读端重定向到标准输入端

/* pipe.c文件*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>

int main( int argc,char **argv)
{
	pid_t pid;
	int fd[2];
	int stat_val;
	if(pipe(fd))
	{
		printf("creat pipe failed\n");
		exit(1);
	}
	
	pid = fork();
	switch(pid)
	{
		case 0:
			sleep(5);
			close(fd[1]);
			close(0);
			dup(fd[0]);
			//dup2(fd[0],0);
			execve("ctrlprocess",(void *)argv,NULL);
			exit(0);
		case -1:
			printf("fork failed\n");
			
			exit(0);
			
		default:
			close(fd[0]);
			write(fd[1],argv[1],strlen(argv[1]));
			close(fd[1]);
			wait(&stat_val);
			break;
	}
	
	return 0;
}

可执行的代码:

/*ctrlprocess.c*/
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<stdio.h>
int main(int argc,char **argv)
{
	printf("I am here\n");
	char message[100];
	int n;
	while(1){
		n=read(0,message,90);
		printf("n= %d\n",n);
		if(n>0)
		{
			printf("message= %s\n",message);
		}
		exit(0);
	}
}
程序执行如下:



有名管道(named pipe 或pipe)

特点:可以在非亲属关系中进行通信;以FIFO的文件形式存在文件系统中,是一个设备文件,相当于创建了一个文件,只要具有读写权限就可以访问 

创建的方式二种:第一种,shell命令,mknod(mkfifo) 路径名>                                       

第二种,系统函数:int mkfifo(const char *pathname,mode_t mode);//S_IFFO | 0666,表明文件存取权限                                     

                             int mknod(const char *pathname,mode_t mode,dev_t dev);
                                  成功返回0,失败返回-1;

注意:使用FIFO时要先使用open()函数将其打开;调用open打开有名管道的进程会阻塞,但如果以O_RDWR打开,一定不会阻塞;以O_RDONLY打开,则调用open的进程将会被阻塞直到有写方打开有名管道;以O_WRONLY打开,则调用open的进程也会被阻塞直到有读方打开有名管道;

/*writepipe.c*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#define NAMEDPIPE  "/home/czg/file/pipe"

int main(char argc,char *argv[])
{
	int fd,n;
	if(argc < 2)
		printf("input data\n");
	
	/* 非堵塞,只读的形式打开文件*/
	fd = open(NAMEDPIPE,O_WRONLY );
	if(fd<0)
	{
		perror("open failed");
	}
	printf("fd = %d\n",fd);
	if(fd < 0)
		printf("open failed\n");
	n=write(fd,argv[1],3);
	printf("n=%d\n",n);
	close(fd);
	return 0;
}

/* readpipe.c*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<unistd.h>
#define NAMEDPIPE  "/home/czg/file/pipe"

int main(int argc,char **argv)
{
	int fd,n;
	char buf[200];
	memset(buf,0,sizeof(buf));
	
	/* 确认管道文件是否存在,有则删除*/
	if(access(NAMEDPIPE,F_OK)==0)
	{
		printf("into access\n");
		execlp("rm","-f",NAMEDPIPE,NULL);
		
	}

	/* 创建管道,使其克读写执行*/
	if(mkfifo(NAMEDPIPE,0777) != 0)
	{
		printf("mkfifo error\n");
	}
	/* 只读方式open管道时,进程被阻塞,至到有写方可打开管道,亦可加O_NONBLOCK(无堵塞)*/
	fd = open(NAMEDPIPE,O_RDONLY );
	printf("fd = %d\n",fd);
	sleep(5);
	while(1)
	{
		memset(buf,0,sizeof(buf));
		n=read(fd,buf,2);
		if(n==-1)
			perror("read error");
		else if(n==0)
			printf("no data\n");
			
		else
		{
			printf("get data:%s\nn=%d\n",buf,n);
			sleep(2);
		
		}
		
	
	}
	close(fd);	
	return 0;	

}

代码结果:


猜你喜欢

转载自blog.csdn.net/caozhigang129/article/details/78373725