步步LINUX C--进程间通信(一)管道

linux间进程通信的方法在前一篇文章中已有详细介绍。http://blog.csdn.net/jmy5945hh/article/details/7350564


本篇详细介绍及代码测试第一种方式,即管道(Pipe)及有名管道(named pipe)。


1-1 管道简介

也称匿名管道,其在系统中没有实名,是进程的一种资源,因此不可以在文件系统中以任何形式查看。

生存周期从被创建开始,到进程结束或进程主动关闭管道。

数据在管道之间以无格式流式传递(想象为水在管道里流动)。

管道是一种半双工通信方式,因此通常需要建立起两根管道以完成收发工作。遵从FIFO原则。

优点是使用方便简单,缺点是功能上有很多限制,且只能作为父子进程间通信手段。

典型应用:LINUX SHELL命令的管道连接。例如:

kill -l | grep SIGINT



1-2 相关函数

创建管道:

#include 
int pipe(int fd[2]);

成功后返回0;出错返回-1。fd是文件描述符数组。

读写管道:

使用文件I/O的write和read函数,参考http://blog.csdn.net/jmy5945hh/article/details/7465879

关闭管道:

使用close函数,参考http://blog.csdn.net/jmy5945hh/article/details/7465879


1-3 管道测试代码

#include 
#include 
#include 
#include 
#include 

#define MAX 4096

int main(int argc, char *argv[]) {
	int pipefd[2], recvBytes, sendBytes;
	pid_t pid;
	unsigned char sendBuf[MAX], recvBuf[MAX];

	memset(sendBuf, 0x00, MAX);
	memset(recvBuf, 0x00, MAX);
	if(pipe(pipefd) < 0) { //创建管道
		perror("pipe:");
		exit(1);
	}

	if((pid = fork()) == 0) { //子进程从管道读取
		close(pipefd[1]);
		sleep(2);
		if((recvBytes = read(pipefd[0], recvBuf, MAX)) == -1) {
			perror("read");
			exit(1);
		}
		printf("Receive %d Bytes data:%s\n", recvBytes, recvBuf);
		close(pipefd[1]);
	}
	else if(pid > 0) { //父进程向管道写入
		close(pipefd[0]);
		strcpy(sendBuf, "Pipe test!");
		if((sendBytes = write(pipefd[1], sendBuf, MAX)) == -1) {
			perror("write");
			exit(1);
		}
		printf("Send %d Bytes data:%s\n", sendBytes, sendBuf);
		close(pipefd[0]);
		sleep(5);
	}

	exit(0);
}

运行结果:

jimmy@MyPet:~/code/ipc$ gcc -o -ggdb3 ipc ipc1.c 
jimmy@MyPet:~/code/ipc$ ./ipc 
Send 4096 Bytes data:Pipe test!
Receive 4096 Bytes data:Pipe test!

非正常测试结果及总结:

1)注释掉子进程中的接收部分,测试结果为父进程依然能向管道发送数据。

2)注释掉父进程中的发送部分,测试结果为子进程依然能从管道接收数据,recvBytes = 0。

3)子进程改为关闭pipefd[0]从pipefd[1]接收,父进程改为关闭pipefd[1]从pipefd[0]发送,测试结果为发送和接收均失败,提示“BAD FILE DESCRIPTOR”。说明了在创建管道时管道对于读写接口已经有了严格的定义,pipefd[0]为读端,pipefd[1]为写端,不能随意更改。读端写,写端读都会产生错误。

4)在父进程写入之前关闭子进程读端,在gdb调试情况下父进程写入时产生“Program received signal SIGPIPE, Broken pipe.”,管道破裂。

5)无名管道只能用于具有亲缘关系的父子进程间通信。

6)管道是半双工的,因此要达到全双工通信创建管道是必须创建两条。


2-1 有名管道简介

也称FIFO,系统提供一个路径名与此管道关联,以FIFO形式存在与文件系统中。

生存周期从被创建开始,到该管道文件被删除(进程结束不会造成管道消失)。

数据在管道之间以无格式流式传递。

只需要建立一个有名管道便可进行读写操作。遵从FIFO原则,不保证操作的原子性。

优点是使用方便简单,可以作为任何进程间通信手段,缺点是功能上有很多限制。


2-2 相关函数

创建FIFO:

#include 
#include 
int mkfifo( const char * pathname, mode_t mode );

成功后返回0;出错返回-1。参数2与打开文件的mode参数一致。

需要注意的是与一般文件不同,当以读方式打开FIFO时,如果设置了阻塞且无进程写打开此FIFO,将会阻塞。当以写方式打开FIFO时,如果设置了阻塞切无进程读打开此FIFO,将会阻塞。

读写FIFO:

使用文件I/O的write和read函数,参考http://blog.csdn.net/jmy5945hh/article/details/7465879

关闭FIFO:

使用close函数,参考http://blog.csdn.net/jmy5945hh/article/details/7465879

close函数并不删除FIFO,而只是关闭。unlink函数接触关联关系,也不删除FIFO。


2-3 有名管道测试代码

#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO_SERV "/home/jimmy/code/ipc/fifo_serv"  //管道名,是一个文件
#define MAX 4096

int main(int argc, char *argv[]) {
	int pipewrfd, piperdfd, sendBytes, recvBytes;
	unsigned char sendBuf[MAX], recvBuf[MAX];
	pid_t pid;

	memset(sendBuf, 0x00, MAX);
	memset(recvBuf, 0x00, MAX);

	if((pid = fork()) == 0) { //子进程
		if((piperdfd = open(FIFO_SERV,O_RDONLY,0)) == -1) { //只读方式打开有名管道
			perror("child open FIFO_SERV");
			exit(1);	
		}

		recvBytes = read(piperdfd, recvBuf, MAX); //从管道读出
		if(recvBytes == -1 && errno == EAGAIN)
				printf("no data avlaible\n");
		printf("recv %d Bytes data:%s\n", recvBytes, recvBuf);
		pause();
		unlink(FIFO_SERV);
	}
	else if(pid > 0) {
		if((mkfifo(FIFO_SERV, O_CREAT|O_EXCL|0777) < 0) && (errno != EEXIST)) //创建有名管道
			printf("cannot create fifoserver\n");

   		if((pipewrfd = open(FIFO_SERV, O_WRONLY,0)) == -1) { //只写方式打开有名管道
			perror("father open FIFO_SERV");
			exit(1);
		}
		strcpy(sendBuf, "Name pipe test!");
		sendBytes = write(pipewrfd, sendBuf, MAX); //向管道写入
		if(sendBytes == -1 && errno == EAGAIN)
			printf("write to fifo error; try later\n");
	}
}

运行结果:

jimmy@MyPet:~/code/ipc$ gcc -g -o fifo fifo.c
jimmy@MyPet:~/code/ipc$ ./fifo 
recv 4096 Bytes data:Name pipe test!

非正常测试结果及总结:

1)管道名错误,无法创建有名管道

2)注释掉子进程中的接收部分,测试结果为父进程依然能向管道发送数据。

3)注释掉父进程中的发送部分,测试结果为子进程依然能从管道接收数据,recvBytes = 0。

4)有名管道可以看作无名管道的升级版,克服了只能在亲缘关系进程间使用的门槛。

5)由于需要通过文件操作,在通信速度上不具有优势



(完)




你可能感兴趣的:(C语言,Linux,linux,测试,descriptor,signal,gcc,shell)