了解C语言中的pipe()系统调用

基本概念

    从概念上讲,管道是两个进程之间的连接,一个进程的标准输出成为另一个进程的标准输入。在UNIX操作系统中,管道用于进程间通信。

  • 管道只是单向通信,即我们可以这样使用管道:一个进程向管道写入数据,另一个进程从管道读取数据。管道,是内存中被视为“虚拟文件”的一块区域。
  • 管道可以被创建进程及其所有子进程读写。一个进程可以写入这个“虚拟文件”或管道,另一个相关进程可以从中读取。
  • 如果在某个内容写入管道之前,某个进程试图读取该内容,则该进程将挂起,直到内容被写入。
  • 管道系统调用在进程的打开文件表中找到前两个可用位置,并将其分配给管道的读取和写入端。

在这里插入图片描述

    C语言语法:

int pipe(int fds[2]);

参数:
fd[0] 将是管道读取端的fd(文件描述符)
fd[1] 将是管道写入端的fd
返回值:0表示成功,-1表示失败。

    管道表现为FIFO(先进先出),管道实现类似队列数据结构。读写大小不是必须一致。在管道中,我们可以一次写入512字节,但可以一次只读取1字节。

// C program to illustrate 
// pipe system call in C 
#include <stdio.h> 
#include <unistd.h> 
#define MSGSIZE 16 
char* msg1 = "hello, world #1"; 
char* msg2 = "hello, world #2"; 
char* msg3 = "hello, world #3"; 
  
int main() 
{
    
     
    char inbuf[MSGSIZE]; 
    int p[2], i; 
  
    if (pipe(p) < 0) 
        exit(1); 
  
    /* continued */
    /* write pipe */
  
    write(p[1], msg1, MSGSIZE); 
    write(p[1], msg2, MSGSIZE); 
    write(p[1], msg3, MSGSIZE); 
  
    for (i = 0; i < 3; i++) {
    
     
        /* read pipe */
        read(p[0], inbuf, MSGSIZE); 
        printf("% s\n", inbuf); 
    } 
    return 0; 
} 

    输出:

hello, world #1
hello, world #2
hello, world #3
父子进程共享管道

    当我们在任何进程中使用fork时,文件描述符在子进程和父进程之间保持打开状态。如果我们在创建管道后调用fork,则父级和子级可以通过管道进行通信。
在这里插入图片描述

// C program to illustrate 
// pipe system call in C 
// shared by Parent and Child 
#include <stdio.h> 
#include <unistd.h> 
#define MSGSIZE 16 
char* msg1 = "hello, world #1"; 
char* msg2 = "hello, world #2"; 
char* msg3 = "hello, world #3"; 
  
int main() 
{
    
     
    char inbuf[MSGSIZE]; 
    int p[2], pid, nbytes; 
  
    if (pipe(p) < 0) 
        exit(1); 
  
    /* continued */
    if ((pid = fork()) > 0) {
    
     
        write(p[1], msg1, MSGSIZE); 
        write(p[1], msg2, MSGSIZE); 
        write(p[1], msg3, MSGSIZE); 
  
        // Adding this line will 
        // not hang the program 
        // close(p[1]); 
        wait(NULL); 
    } 
  
    else {
    
     
        // Adding this line will 
        // not hang the program 
        // close(p[1]); 
        while ((nbytes = read(p[0], inbuf, MSGSIZE)) > 0) 
            printf("% s\n", inbuf); 
        if (nbytes != 0) 
            exit(2); 
        printf("Finished reading\n"); 
    } 
    return 0; 
} 

    输出:

hello world, #1
hello world, #2
hello world, #3
(hangs)         //program does not terminate but hangs

    在此代码中,在完成读/写之后,父级和子级都阻塞而不是终止进程,这就是程序挂起的原因。

  • 如果管道为空,并且我们调用read系统调用,那么如果没有进程打开写入端,则管道上的读取将返回EOF(返回值0)。
  • 如果其他进程打开了管道进行写入,则read将阻塞,以等待新的数据,因此代码输出挂起,因为写入端的父进程和子进程没有关闭。
参考文档

[1]Kadam Patel.pipe() System call[EB/OL].https://www.geeksforgeeks.org/pipe-system-call/,2019-06-12.

猜你喜欢

转载自blog.csdn.net/zsx0728/article/details/113830964