Linux系统主要的进程间通信机制如下:
管道本身是一种数据结构,遵循先进先出的原则。先进入管道的数据,也能先从管道中读出。数据一旦读取后,就会在管道中自动删除。
管道通信以管道数据结构作为内部数据存储方式,以文件系统作为数据存储媒体。
pipe()函数别定义在头文件unistd.h
中,它的一般形式是:
int pipe(int filedes[2]);
pipe系统调用需要打开两个文件,文件标识符通过参数传递给pipe()函数。文件描述符filedes[0]用来读取数据,filedes[1]用来写数据。调用成功时,返回值为0,错误时返回-1。
管道的工作方式可以总结为以下3个步骤
使用的是write()
函数,与写入普通文件的操作方法一样。不同的是管道长度受到限制,管道满时写入操作会被阻塞。执行写操作的进程进入睡眠状态,直到管道中的数据被读取。fcntl()函
数可将管道设置为非阻塞模式,管道满时,write()
函数的返回值为0。如果写入数据长度小于管道数据,则要求一次写入完成,若大于管道长度,写完管道长度的数据时,write()
将被阻塞。
读取数据使用read()
函数实现,读取的顺序与写入顺序相同。当数据被读取后,这些数据将自动被管道清除。因此,使用管道通信的方式只能是一对一,不能由一个进程同时向多个进程传递同一数据。
**如果读取的管道为空,并且管道写入端口是打开的,read()函数将被阻塞。**读取操作的进程进入睡眠状态,直到有数据写入管道为止。fcntl()
函数也可将管道读取模式设置为非阻塞。
管道虽然有两个端口,但只有一个端口能被打开,避免了同时对管道进行读和写的操作。关闭端口使用的是close()函数,关闭读端口时,在管道上进行写操作的进程将收到SIGPIPE信号,关闭写端口时,进行读操作的read()函数将返回0。如下例所示:
#include
#include
#include
#include
#include
#include
int main()
{
int fd[2],cld_pid,len;
char buf[200];
int status;
if(pipe(fd)==-1)
{
perror("创建管道出错");
exit(1);
}
if((cld_pid=fork())==0)
{
printf("\n进入子进程\n");
close(fd[1]);
len=read(fd[0],buf,sizeof(buf));
buf[len]=0;
printf("子进程从管道中读取的数据是:\n%s",buf);
printf("\n退出子进程\n");
exit(0);
}
else
{
printf("\n进入父进程\n");
close(fd[0]);
sprintf(buf,"父进程为子进程(PID=%d)创建该数据\n123\t456\t789",cld_pid);
write(fd[1],buf,strlen(buf));
printf("\n退出父进程\n");
//exit(0);
}
wait(&status);
return 0;
}
输出结果如下:
上述程序中首先创建了一个管道,并且将管道的文件标识符传递给fp[]数组。该数组有两个元素,fd[0]是读取管道的端口,fd[1]是写入管道的端口。然后,通过fork()系统调用创建子进程。父进程的操作是向管道写入数据,子进程的操作是读取管道内的数据,最后子进程将所读数据显示到终端上。
sprintf()
函数是向缓冲区中写入内容。
系统调用dup用来复制一个文件描述符,该操作是通过对u区中文件描述符复制实现的。因此,系统调用dup能让多个文件描述符指向同一文件,便于管道操作。与该调用相关的函数有两个,分别是dup()
函数和dup2()
函数,一般形式如下:
int dup(int oldfd);
int dup2(int oldfd,int newfd);
两个函数的区别为,dup()函数自动分配新文件描述符,并保证该文件描述符没有被使用。dup2()函数使用newfd参数指定新文件描述符,如果该文件描述符已存在,则覆盖对应的文件描述符。
新旧文件描述符可交换使用,并共享文件锁、文件指针和文件状态。
调用成功时,函数返回值为新文件描述符,否则返回-1。例如下例:
#include
#include
#include
#include
#include
int main()
{
int fd;
if((fd=open("output",O_CREAT|O_RDWR,0644))==-1)
{
perror("打开或创建文件出错");
return 1;
}
close(1); //标准输出的文件描述符为1
dup(fd);
close(fd);
puts("该行数据将输出到文件中");
return 0;
}
程序执行结果如下:
上述代码中,标准输出(文件描述符为1)关闭,并将一个普通文件output的文件描述符复制到标准输出上。因为刚关闭了文件描述符,文件描述符表的第一个空表项是1,dup()函数将调用fd的文件描述符复制到该位置上。所以,程序以后的向标准输出写的内容都写到了文件output中。