曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 8569|回复: 4
打印 上一主题 下一主题

[进程] 管道的创建与读写

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2009-7-21 18:52:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
管道的创建
可以通过 pipe() 函数来创建管道。该函数成功调用返回 0 ,有错误则返回 -1。函数声明:
#include <unistd.h>

int pipe(int pipefd[2]);
在调用函数成功后,参数数组中将包含两个新的文件描述符。

管道两端分别用两个描述符 pipefd[0] 和 pipefd[1] 来描述。需要注意的是,管道两端的任务是固定的,一端只能用于读,由文件描述符 pipefd[0] 表示,称其为管道读端
                                                                                                                                                                                另一端只能用于写,由文件描述符 pipefd[1] 表示,称其为管道写端
如果试图从管道写端读数据,或者向管道读端写数据都将导致出错。

管道是一种文件,因此对文件操作的 I/O 函数都可以用于管道,如 read(),write() 等。

注意:管道一旦创建成功,就可以作为一般文件来使用,对一般文件进行操作的 I/O 函数也适用于管道。

管道的一般用法是,进程在使用 fork() 函数创建子进程前创建一个管道,然后创建子进程,之后父进程关闭管道的读端,子进程关闭管道的写端。也就是说,父进程只负责写,子进程只负责读。当然反过来也行。这样的管道可以用于父子进程间的通信,也可以用于兄弟进程间的通信。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2009-7-21 20:11:53 | 只看该作者

从管道中读数据

如果进程要读取管道中的数据,那么进程应该关闭 pipefd[1] (写端),同时向管道写数据的进程应当关闭 pipefd[0] ( 读端 )。因为管道只能用于具有亲缘关系的进程间的通信,在各进程进行通信时,它们共享文件描述符。在使用前,应及时地关闭不需要的管道一端,以免发生意外错误。

进程在管道的读端读取数据时,如果管道写端不存在,则读进程认为已经读到了数据的末尾,该函数返回读出的字节数为 0 ;
                                           如果管道的写端存在,且请求读取的字节数大于 PIPE_BUF ,则返回管道中现有的所有数据。
                                                                         如果请求的字节数不大于 PIPE_BUF ,则返回管道中现有的所有数据(管道中数据量小于请求的数据量。) 或者 返回请求的字节数(管道中数据量大于等于请求的数据量)。

注意: PIPE_BUFinclude/linux/limits.h 头文件中定义,不同的内核版本可能会有所不同。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
板凳
 楼主| 发表于 2009-7-21 20:21:03 | 只看该作者

向管道中写数据

如果某进程希望向管道中写数据,那么该进程应该关闭 pipefd[0] 描述符,同时管道的另一端的进程关闭 pipefd[1] 。

向管道中写入数据时, Linux 不保证写入的原子性 ( 原子性是指操作在任何时候都不能被任何原因所打断,操作要么不做要么就一定要完成 )。管道缓冲区一有空闲区域,写进程就会试图向管道中写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直被阻塞等待。

在写管道时,如果要求写入的字节数小于等于 PIPE_BUF ,则多个进程对同一管道的写操作不会交错进行( 空间够用,一个写完到另一个 )。但是,如果多个进程同时写一个管道,而且某些进程要求写的字节数超过 PIPE_BUF 所容纳时,则多个写操作的数据可能会交错。

注意:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的 SIGPIPE 信号。应用程序可以处理也可以忽略信号,如果忽略信号或者捕捉该信号并从其处理程序返回,则 write 出错,出错代码为 EPIPE

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
地板
 楼主| 发表于 2009-7-22 01:46:35 | 只看该作者

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

/*读管道*/
void read_from_pipe (int fd)
{
    char    message[100];
    read(fd, message, 100);
    printf("read from pipe: %s", message);
}

/*写管道*/
void write_to_pipe (int fd)
{
    char *message = "Hello, pipe!\\n";
    write(fd, message, strlen(message) + 1);
}

int main(void)
{
    int     pipefd[2];
    pid_t    pid;
    int     stat_val;
   
    if (pipe(pipefd)) {
        printf("create pipe failed\\n");
        exit(1);
    }
    pid = fork();
    switch(pid) {
        case -1:
          printf("fork error!\\n");
          exit(1);
        case 0:
          close (pipefd[1]);
          read_from_pipe (pipefd[0]);
          exit(0);
        default:
          close (pipefd[0]);
          write_to_pipe (pipefd[1]);
          wait(&stat_val);
          exit(0);
    }
    return 0;
}
运行及输出
beyes@linux-beyes:~/C/pipe> ./pipe.exe
read from pipe: Hello, pipe!
说明
程序中,父进程向管道中写数据,子进程从管道中获取数据。可以看到,对管道的读写和对一般文件的读写没有什么区别。
注意: 必须在系统调用 fork() 之前调用 pipe() ,否则子进程将不会继承管道的文件描述符。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
5#
 楼主| 发表于 2009-7-23 17:44:24 | 只看该作者

通过管道的全双工通信

对一个管道来说,通信是半双工的( 一端只写不读,另一端只读不写 ),但是可以通过创建两个管道来实现全双工通信( 两端都可以读写 )。

测试代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

/*子进程读写管道函数*/
void child_rw_pipe(int writefd, int readfd)
{
    char *message1 = "这是一条来自子进程的消息!\\n";
    write(writefd, message1, strlen(message1)+1);

    char message2[100];
    read(readfd, message2, 100);
    printf("子进程从管道中读取到的消息: %s", message2);
}
/*父进程读写管道函数*/
void parent_rw_pipe(int readfd, int writefd)
{
    char *message1 = "这是一条来自父进程的消息!\\n";
    write(writefd, message1, strlen(message1)+1);

    char message2[100];
    read(readfd, message2, 100);
   
    printf("父进程从管道中读取到的消息: %s", message2);
}
   

int main(void)
{
    int     pipefd1[2], pipefd2[2];
    pid_t    pid;
    int     stat_val;

    printf("全双工通信:\\n\\n");
    if (pipe(pipefd1)) {
        printf("管道-1创建失败\\n");
        exit(1);
    }
    if (pipe(pipefd2)) {
        printf("管道-2创建失败\\n");
        exit(1);
    }
       
    pid = fork();
    switch(pid) {
        case -1:
          printf("创建进程失败\\n");
          exit(1);
        case 0:
          /*子进程关闭 pipefd1 读端,关闭 pipefd2 的写端*/
          close(pipefd1[0]);
          close(pipefd2[1]);
          child_rw_pipe(pipefd1[1],pipefd2[0]);
          exit(0);
        default:
          /*父进程关闭 pipefd1 写端,关闭 pipefd2 的读端*/
          close(pipefd1[1]);
          close(pipefd2[0]);
          parent_rw_pipe(pipefd1[0], pipefd2[1]);
          wait(&stat_val);
          exit(0);
    }
}
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 23:25 , Processed in 0.085506 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表