进程间使用管道通信

本文介绍了进程间如何通过管道进行数据通信,包括无名管道(pipe)和有名管道(FIFO)。详细讲解了pipe系统调用的使用,以及管道的读写规则。通过一个示例展示了如何创建管道并进行父进程与子进程间的通信。此外,还提到了dup系统调用,用于复制文件描述符。
摘要由CSDN通过智能技术生成


By  书生 , 出处:only_you

4.进程间使用管道通信

  前几节中我们讲述了有关进程的操作,我们已经学会产生一个新的进程,改变进程的执行图像等操作。然而,子进程与父进程,子进程与子进程之间,还缺少数据交换的方法。这一节,我们就来看一种比较简单的数据传送的方法,即通过管道传送数据。

  管道允许在进程之间按先进先出的方式传送数据,管道也能使进程同步执行。管道传统的实现方法是通过文件系统作为存储数据的地方。有两种类型的管道:一种是无名管道,简称为管道;另一种是有名管道,也称为FIFO。进程使用系统调用open来打开有名管道,使用系统调用pipe来建立无名管道。使用无名管道通讯的进程,必须是发出pipe调用的进程及其子进程。使用有名管道通讯的进程没有上述限制。在以后的叙述中,所有无名管道都简称为管道。下面来看一下系统调用pipe。

4.1 pipe 系统调用

  系统调用pipe是用来建立管道的。该调用的声明格式如下:

int pipe(int filedes[2]);

  在使用该调用的程序中加入下面的头文件:

#include 

  一个管道拥有两个文件描述符用来通信,它们指向一个管道的索引节点,该调用将这两文件描述符放在参数filedes中返回。现在的许多系统中管道允许数据双向流动,但一般习惯上,文件描述符filedes[0]用来读数据,filedes[1]用来写数据。如果要求程序的可移植性好,就按照习惯的用法来编程。调用成功时,返回值为0;错误时,返回-1,并设置错误代码errno:

  • EMFILE:进程使用了过多的文件描述符。
  • ENFILE:系统文件表满。
  • EFAULT:参数filedes无效。

  下面介绍管道的操作的情况:

  1. 对于写管道:

      写入管道的数据按到达次序排列。如果管道满,则对管道的写被阻塞,直到管道的数据被读操作读取。对于写操作,如果一次write调用写的数据量小于管道容量,则写必须一次完成,即如果管道所剩余的容量不够,write被阻塞直到管道的剩余容量可以一次写完为止。如果write调用写的数据量大于管道容量,则写操作分多次完成。如果用fcntl设置管道写端口为非阻塞方式,则管道满不会阻塞写,而只是对写返回0��

  2. 对于读管道:

      读操作按数据到达的顺序读取数据。已经被读取的数据在管道内不再存在,这意味着数据在管道中不能重复利用。如果管道为空,且管道的写端口是打开状态,则读操作被阻塞直到有数据写入为止。一次read调用,如果管道中的数据量不够read指定的数量,则按实际的数量读取,并对read返回实际数量值。如果读端口使用fcntl设置了非阻塞方式,则当管道为空时,read调用返回0。

  3. 对于管道的关闭:

      如果管道的读端口关闭,那么在该管道上的发出写操作调用的进程将接收到一个SIGPIPE信号。关闭写端口是给读端口一个文件结束符的唯一方法。对于写端口关闭后,在该管道上的read调用将返回0。下面再来看看,系统调用pipe的例子。在下面的例子中,父进程通过管道向子进程发送了一个字符串。子进程将它显示出来:

    #include 
    #include 
    #include 
    #include 
    #include 
    int main()
    {
    int fd[2],cld_pid,status;
    char buf[200], len;

    if (pipe(fd) == -1)
    {
    printf("creat pipe error ");
    exit(1);
    }
    if ((cld_pid=fork()) == 0)
    {
    close(fd[1]);
    len = read(fd[0],buf,sizeof(buf));
    buf[len]=0;
    printf("Child read pipe is -- %s ",buf);
    exit(0);
    }
    else
    {
    close(fd[0]);
    sprintf(buf,"Parent creat this buff for cld %d",cld_pid);
    write(fd[1],buf, strlen(buf));
    exit(0);
    }
    }

      该程序执行过程的屏幕拷贝:

    [root@wapgw /tmp]# ./pipe
    [root@wapgw /tmp]# Child read pipe is -- Parent creat this buff for cld 5954
    [root@wapgw /tmp]#
     

4.2 dup 系统调用

  系统调用dup是用来复制一个文件描述符,也就是将进程u区的文件描述符表中的一项复制一份,使得这两项同时指向系统文件表的同一表项。该调用的声明格式如下:

int dup(int oldfd);
int dup2(int oldfd, int newfd);

  在使用该调用的程序中加入下面的头文件:

#include 

  系统调用dup复制由参数oldfd指定的文件描述到进程文件描述符表的第一个空表项处。而系统调用dup2复制由参数oldfd指定的文件描述到参数newfd指定的文件描述符表项处。老的文件描述符和新复制的文件描述符可以互换使用。它们共享锁、文件指针和文件状态。例如,对其中一个文件描述符使用系统调用lseek修改文件指针的位置,对另一文件描述符来说文件指针也改变了,其实我们了解了内核的工作原理,这一点很容易理解。因为我们知道,文件指针是放在系统文件表中的。但这两个文件描述符具有不同的close-on-exec标志,因为该标志是存放在文件描述符表中的。

  该调用成功时,返回值为新的描述符;错误时,返回-1,并设置相应的错误代码errno:

  • EBADF:参数oldfd不是一个已经打开的文件描述符;或者参数newfd超出允许的文件描述符的取值范围。
  • EMFILE:进程打开的文件描述符数量已经到达最大值,但仍然企图打开新的文件描述符。

  下面我们来看一个简单的例子。在这个例子中,我们将标准输出(文件描述符为1)关闭,并将一个打开了普通文件“output”的文件描述符��制到标准输出上,因为刚关闭了文件描述符1,所以,文件描述符表的第一个空表项是1。所以,程序以后的printf等向标准输出写的内容都写到了文件output中。

#include 
#include 
#include 
#include 
#include 
int main()
{
int fd;
if ((fd=open("output",O_CREAT|O_RDWR,0644))==-1)
{
printf("cannot open output file ");
exit(1);
}
close(1); /* 关闭标准输出 */
dup(fd); /* 复制fd到文件描述符1上 */
close(fd); /* 即时关闭不用的文件描述符是一个好习惯 */
printf("This line will write to file ");
exit(0);
}

  该程序执行过程的屏幕拷贝:

[wap@wapgw /tmp]$ gcc -o dup_test dup_test.c
[wap@wapgw /tmp]$ ./dup_test
[wap@wapgw /tmp]$ more output
This line will write to file
[wap@wapgw /tmp]$
By  书生 , 出处:only_you

4.进程间使用管道通信

  前几节中我们讲述了有关进程的操作,我们已经学会产生一个新的进程,改变进程的执行图像等操作。然而,子进程与父进程,子进程与子进程之间,还缺少数据交换的方法。这一节,我们就来看一种比较简单的数据传送的方法,即通过管道传送数据。

  管道允许在进程之间按先进先出的方式传送数据,管道也能使进程同步执行。管道传统的实现方法是通过文件系统作为存储数据的地方。有两种类型的管道:一种是无名管道,简称为管道;另一种是有名管道,也称为FIFO。进程使用系统调用open来打开有名管道,使用系统调用pipe来建立无名管道。使用无名管道通讯的进程,必须是发出pipe调用的进程及其子进程。使用有名管道通讯的进程没有上述限制。在以后的叙述中,所有无名管道都简称为管道。下面来看一下系统调用pipe。

4.1 pipe 系统调用

  系统调用pipe是用来建立管道的。该调用的声明格式如下:

int pipe(int filedes[2]);

  在使用该调用的程序中加入下面的头文件:

#include 

  一个管道拥有两个文件描述符用来通信,它们指向一个管道的索引节点,该调用将这两文件描述符放在参数filedes中返回。现在的许多系统中管道允许数据双向流动,但一般习惯上,文件描述符filedes[0]用来读数据,filedes[1]用来写数据。如果要求程序的可移植性好,就按照习惯的用法来编程。调用成功时,返回值为0;错误时,返回-1,并设置错误代码errno:

  • EMFILE:进程使用了过多的文件描述符。
  • ENFILE:系统文件表满。
  • EFAULT:参数filedes无效。

  下面介绍管道的操作的情况:

  1. 对于写管道:

      写入管道的数据按到达次序排列。如果管道满,则对管道的写被阻塞,直到管道的数据被读操作读取。对于写操作,如果一次write调用写的数据量小于管道容量,则写必须一次完成,即如果管道所剩余的容量不够,write被阻塞直到管道的剩余容量可以一次写完为止。如果write调用写的数据量大于管道容量,则写操作分多次完成。如果用fcntl设置管道写端口为非阻塞方式,则管道满不会阻塞写,而只是对写返回0��

  2. 对于读管道:

      读操作按数据到达的顺序读取数据。已经被读取的数据在管道内不再存在,这意味着数据在管道中不能重复利用。如果管道为空,且管道的写端口是打开状态,则读操作被阻塞直到有数据写入为止。一次read调用,如果管道中的数据量不够read指定的数量,则按实际的数量读取,并对read返回实际数量值。如果读端口使用fcntl设置了非阻塞方式,则当管道为空时,read调用返回0。

  3. 对于管道的关闭:

      如果管道的读端口关闭,那么在该管道上的发出写操作调用的进程将接收到一个SIGPIPE信号。关闭写端口是给读端口一个文件结束符的唯一方法。对于写端口关闭后,在该管道上的read调用将返回0。下面再来看看,系统调用pipe的例子。在下面的例子中,父进程通过管道向子进程发送了一个字符串。子进程将它显示出来:

    #include 
    #include 
    #include 
    #include 
    #include 
    int main()
    {
    int fd[2],cld_pid,status;
    char buf[200], len;

    if (pipe(fd) == -1)
    {
    printf("creat pipe error ");
    exit(1);
    }
    if ((cld_pid=fork()) == 0)
    {
    close(fd[1]);
    len = read(fd[0],buf,sizeof(buf));
    buf[len]=0;
    printf("Child read pipe is -- %s ",buf);
    exit(0);
    }
    else
    {
    close(fd[0]);
    sprintf(buf,"Parent creat this buff for cld %d",cld_pid);
    write(fd[1],buf, strlen(buf));
    exit(0);
    }
    }

      该程序执行过程的屏幕拷贝:

    [root@wapgw /tmp]# ./pipe
    [root@wapgw /tmp]# Child read pipe is -- Parent creat this buff for cld 5954
    [root@wapgw /tmp]#
     

4.2 dup 系统调用

  系统调用dup是用来复制一个文件描述符,也就是将进程u区的文件描述符表中的一项复制一份,使得这两项同时指向系统文件表的同一表项。该调用的声明格式如下:

int dup(int oldfd);
int dup2(int oldfd, int newfd);

  在使用该调用的程序中加入下面的头文件:

#include 

  系统调用dup复制由参数oldfd指定的文件描述到进程文件描述符表的第一个空表项处。而系统调用dup2复制由参数oldfd指定的文件描述到参数newfd指定的文件描述符表项处。老的文件描述符和新复制的文件描述符可以互换使用。它们共享锁、文件指针和文件状态。例如,对其中一个文件描述符使用系统调用lseek修改文件指针的位置,对另一文件描述符来说文件指针也改变了,其实我们了解了内核的工作原理,这一点很容易理解。因为我们知道,文件指针是放在系统文件表中的。但这两个文件描述符具有不同的close-on-exec标志,因为该标志是存放在文件描述符表中的。

  该调用成功时,返回值为新的描述符;错误时,返回-1,并设置相应的错误代码errno:

  • EBADF:参数oldfd不是一个已经打开的文件描述符;或者参数newfd超出允许的文件描述符的取值范围。
  • EMFILE:进程打开的文件描述符数量已经到达最大值,但仍然企图打开新的文件描述符。

  下面我们来看一个简单的例子。在这个例子中,我们将标准输出(文件描述符为1)关闭,并将一个打开了普通文件“output”的文件描述符��制到标准输出上,因为刚关闭了文件描述符1,所以,文件描述符表的第一个空表项是1。所以,程序以后的printf等向标准输出写的内容都写到了文件output中。

#include 
#include 
#include 
#include 
#include 
int main()
{
int fd;
if ((fd=open("output",O_CREAT|O_RDWR,0644))==-1)
{
printf("cannot open output file ");
exit(1);
}
close(1); /* 关闭标准输出 */
dup(fd); /* 复制fd到文件描述符1上 */
close(fd); /* 即时关闭不用的文件描述符是一个好习惯 */
printf("This line will write to file ");
exit(0);
}

  该程序执行过程的屏幕拷贝:

[wap@wapgw /tmp]$ gcc -o dup_test dup_test.c
[wap@wapgw /tmp]$ ./dup_test
[wap@wapgw /tmp]$ more output
This line will write to file
[wap@wapgw /tmp]$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值