首页 > 代码库 > Linux进程通信——管道

Linux进程通信——管道

进程间通信(IPC:Inner Proceeding Communication)

进程是操作系统实现程序独占系统执行的假象的方法。是对处理器、主存、I/O设备的抽象表示。每一个进程都是一个独立的资源管理单元,每一个进程所示是自己独占使用系统的假象。因此各个进程之间是不可以直接的訪问对方进程的资源的,不同的进程之间进行信息交互须要借助操作系统提供的特殊的进程通信机制。

进程之间的通信。从物理上分,能够分为同主机的进程之间的通信和不同主机间的进程之间的通信。从通信内容方式上分。能够分为数据交互、同步通信、异步通信。

Linux系统进程之间的通信方式大致例如以下图所看到的

技术分享

集合上述两种从物理和内容方式的划分,能够这样理解上图:

(1)同主机进程间数据交互机制:无名管道(PIPE)、有名管道(FIFO)、消息队列(Message Queue)和共享内存(Shared Memory)。

(2)同主机进程间同步通信机制:信号量(Semaphore)。

(3)同主机进程间异步通信机制:信号(Signal)。

(4)不同主机间进程数据交互机制:套接字(Socket)、远程调用RPC(Remote Procedure Call)。


管道通信

管道又能够分为无名管道命名管道,两者的用途是不一样的。

无名管道PIPE:主要用于具有亲缘关系的进程之间的通信,无名管道的通信是单向的。仅仅能由一段到另外一段;无名管道是暂时性的。完毕通信后将自己主动消失。

一般採用先创建无名管道,再创建子进程,使子进程继承父进程的管道文件描写叙述符,从而实现父子进程间的通信。在非亲缘关系管道之间,假设想利用无名管道进行通信。则须要借助另外的文件描写叙述符传递机制。

有名管道FIFO:有名管道是一个实际存在的特殊文件,利用有名管道能够实现同主机随意进程之间的数据交互。


Shell中的无名管道

在Shell命令行中使用无名管道是到过管道符“ | ” 实现的。如以下的shell命令将cat命令的输出通过管道传递给grep命令的输入。

技术分享

无名管道是一种特殊的文件,这就意味着你能够向操作文件一样操作无名管道,无名管道在内核中相应的是一段特殊的内存空间,这段内存空间由操作系统进行管理,对用户是不可见的,在用户空间的应用程序中仅仅能通过系统调用来訪问它。在这段内存空间中以循环队列的方式来暂时存储一个进程发往另外一个进程的信息,而且在通信完毕后就会自己主动释放相应的空间。


Linux C编程中的无名管道

1、创建无名管道

创建无名管道须要使用pipe(int _pipedes[2])函数,这个函数的參数是一个含有两个元素的整型数组,假设运行成功,这个整形数组将分别存储无名管道读端的文件描写叙述符和写端的文件描写叙述符,利用这两个读、写文件描写叙述符,我们能够像读写文件一样,操作无名管道的读写。如pipe( )函数调用失败将返回-1。


2、读写无名管道

以下的程序是在一个程序中完毕,即一个进程既充当读进程又充当写进程。

#include<stdio.h>
#include<unistd.h>
#include<string.h>

int main()
{
        int p[2];
        char *str = "HelloWorld";  //待写入无名管道的字符串
        char buf[128]; //读取无名管道时的缓冲区
        memset(buf,'\0',128); //清空缓冲区

        if(pipe(p) == -1)
        {
                printf("function pipe() calls failed.");
                return -1;
        }

        write(p[1],str,strlen(str));  //写无名管道

        read(p[0],buf,strlen(str));  //读无名管道

        printf("%s\n",buf); //打印读取的内容

        return 0;

}

以下的程序模拟了具有亲缘关系的父子进程之间的通信,子进程向父进程发送字符串str,遵循上面的先创建管道再创建子进程,这样子进程会复制父进程的管道文件描写叙述符。

#include<stdio.h>
#include<unistd.h>
#include<string.h>

int main()
{
        int p[2];
        int pid;
        char *str = "HelloWorld";
        char buf[128];
        memset(buf,'\0',128);

        if(pipe(p) == -1)
        {
                printf("function pipe() calls failed.");
                return -1;
        }

        if((pid=fork()) == -1)  //创建一个子进程
        {
                printf("function fork() calls failed.\n");
                return -1;
        }
        else if(pid == 0)  //在子进程中
        {
                printf("In sub : pid=%d\n",getpid());
                write(p[1],str,strlen(str)); //向无名管道中写入str
        }else {  //在父进程中
                printf("In father : pid=%d\n",getpid());
                read(p[0],buf,strlen(str));  //读取无名管道
                printf("In father : buf=%s\n",buf);
        }
}

上述程序的输出结果:

技术分享


注意:读写无名管道的read和write方法默认是堵塞式的读写,当读取管道,可是这时管道中没有数据。也没有进程向写端写数据的时候,读操作会发生堵塞。同理假设,向管道中写数据,可是这时管道已经满了(管道是一个特殊的内存空间文件,默认大小是PIPE_BUF,通常是4096字节)。写操作将发生堵塞。另外假设。写管道时,读端已经关闭,那么写操作将收到SIGPIPE信号,write函数返回-1.


命名管道

命名管道是实际存在的一种文件,在shell中,我们能够使用mknod命令创建一个命名管道。以下是mknod的使用方法。而且利用mknod创建了一个p_file文件,能够发现这个文件的文件类型是p

技术分享


Linux C编程中的命名管道

1、创建命名管道

在编程中,能够使用mkfifo(char *path, _mode_t _mode)函数创建一个命名管道,mkfifo有两个參数。第一个是指定要创建的命名管道的名字。第二个是生成的命名管道文件的模式。


2、读写命名管道

和无名管道一样。命名管道的实质仍然是一段内核空间管理的内存,可是在使用write和read之前须要先使用open函数打开命名管道文件

以下的程序是两个没有亲缘关系的进程之间利用命名管道进行通信

写进程源代码例如以下

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main()

{	
	int fd;
	int res;
	char *str = "HelloWorld";
	res = mkfifo("myfifo",0766);

	if(res != 0)
	{
		printf("function nkfifo() calls failed.");
		return -1;
	}

	fd = open("myfifo",O_WRONLY); //create fifo file named "myfifo"
	if(fd == -1)
	{
		printf("function open() calls failed.");
		return -1;
	}
	printf("This proceeding id =%d, fifo file file ID=%d\n",getpid(),fd);
	
	res = write(fd,str,strlen(str));  //write to fifo file, This will be blocking if no read
	if(res == -1)
	{
		printf("function write() calls failed.");
		return -1;
	}
	printf("write ok.\n");
	close(fd);
	return 0;
	
}

读取管道程序的源代码例如以下

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main()
{
	int fd;
	int res;
	char buf[4096];
	memset(buf,'\0',4096);
	
	fd = open("myfifo",O_RDONLY);  //open fifo file
	if(fd == -1)
	{
		printf("function open() calls failed.");
		return -1;
	}

	printf("This proceeding id=%d, fd=%d\n",getpid(),fd);

	read(fd,buf,sizeof(buf)); //read fifo into buffer, This is a blocking method

	printf("read : buf=%s\n",buf);

	close(fd);
	return 0;

}

须要先执行写进程,由于须要创建管道文件,write方法将被堵塞,由于没有读关联到管道。继续在另外的终端,执行读取进程,读进程能够读取到写进程发送的数据。而且写进程的堵塞将会解除。

技术分享


总结:

无名管道主要用于具有亲缘关系的父子进程之间的通信。是暂时性的。须要先创建管道,再创建子进程;管道都是单向的。若要实现双向通信,则须要两个管道。

命名管道是实际存在的文件,使用前须要先打开,管道默认的read和write操作都是堵塞式的。



Linux进程通信——管道