MST

星途 面试题库

面试题:进程管理之管道实现与使用(中等难度)

在Linux系统下,简述无名管道和有名管道在创建、使用场景以及读写特性上有哪些主要区别?请分别举例说明它们的简单使用方法。
42.9万 热度难度
操作系统进程管理

知识考点

AI 面试

面试题答案

一键面试

创建区别

  • 无名管道:使用pipe函数创建,其函数原型为int pipe(int pipefd[2]); ,该函数在内核中创建一个管道,并返回两个文件描述符pipefd[0](读端)和pipefd[1](写端)。例如:
#include <stdio.h>
#include <unistd.h>

int main() {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    // 后续可进行读写操作
    close(pipefd[0]);
    close(pipefd[1]);
    return 0;
}
  • 有名管道:使用mkfifo函数创建,函数原型为int mkfifo(const char *pathname, mode_t mode);pathname是管道的路径名,mode指定管道的权限。例如在命令行中可以使用 mkfifo myfifo 创建一个名为myfifo的有名管道,在程序中:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main() {
    if (mkfifo("myfifo", 0666) == -1) {
        perror("mkfifo");
        return 1;
    }
    // 后续可进行读写操作
    return 0;
}

使用场景区别

  • 无名管道:主要用于具有亲缘关系(如父子进程)的进程间通信。例如,父进程创建一个子进程,通过无名管道父进程可以向子进程发送数据,或者子进程向父进程汇报结果。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    } else if (pid == 0) {  // 子进程
        close(pipefd[1]);  // 关闭写端
        char buffer[1024];
        ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer));
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("子进程读到: %s\n", buffer);
        close(pipefd[0]);
    } else {  // 父进程
        close(pipefd[0]);  // 关闭读端
        const char *msg = "Hello, child!";
        ssize_t bytes_written = write(pipefd[1], msg, strlen(msg));
        if (bytes_written == -1) {
            perror("write");
            return 1;
        }
        close(pipefd[1]);
    }
    return 0;
}
  • 有名管道:不仅可以用于亲缘关系的进程间通信,还可以用于没有亲缘关系的进程间通信。比如一个客户端进程和一个服务端进程,它们可以通过有名管道进行数据交互。例如有一个写端程序:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main() {
    int fd = open("myfifo", O_WRONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    const char *msg = "Data from writer";
    ssize_t bytes_written = write(fd, msg, strlen(msg));
    if (bytes_written == -1) {
        perror("write");
        return 1;
    }
    close(fd);
    return 0;
}

以及一个读端程序:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("myfifo", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    char buffer[1024];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
    if (bytes_read == -1) {
        perror("read");
        return 1;
    }
    buffer[bytes_read] = '\0';
    printf("读到: %s\n", buffer);
    close(fd);
    return 0;
}

读写特性区别

  • 无名管道
    • 半双工通信,数据只能在一个方向上流动,要么读要么写。
    • 如果管道写端关闭,读端读取完管道中剩余数据后,再次read会返回0,表示文件结束。
    • 如果管道读端关闭,写端继续write会产生SIGPIPE信号,默认情况下进程会终止。
  • 有名管道
    • 同样是半双工通信。
    • 当所有写端关闭时,读端读取完数据后,再次read会返回0。
    • 当读端不存在时,以O_WRONLY方式打开有名管道,open操作会阻塞,直到有读端打开该管道;如果以O_WRONLY | O_NONBLOCK方式打开,open会立即返回 -1,并设置errnoENXIO