返回

Linux 系统中的进程通信:标准输入输出详解

后端

前言

在计算机系统中,进程是执行中的程序,它们彼此独立运行,但又需要相互协作。为了实现进程间的通信,操作系统提供了多种机制,其中标准输入输出是最基本也是最常用的方法。

标准输入输出

标准输入输出(Standard Input/Output,简称 I/O)是操作系统为每个进程默认打开的三个文件:标准输入(stdin)、标准输出(stdout)和标准错误输出(stderr)。这三个文件分别对应键盘、显示器和错误输出设备,为进程提供了与外界进行交互和数据传输的通道。

标准输入

标准输入(stdin)用于从键盘读取数据。当您在终端中输入命令或文本时,这些输入都会被写入标准输入。程序可以通过调用 read()scanf() 等函数从标准输入读取数据。

#include <stdio.h>

int main() {
    char input[100];
    printf("Enter your name: ");
    scanf("%s", input);
    printf("Hello, %s!\n", input);
    return 0;
}

标准输出

标准输出(stdout)用于向显示器输出数据。当程序调用 printf()puts() 等函数时,输出的内容都会被写入标准输出。

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

标准错误输出

标准错误输出(stderr)用于输出错误信息或警告信息。当程序遇到错误或异常时,错误信息通常会通过标准错误输出输出。

#include <stdio.h>

int main() {
    fprintf(stderr, "Error: file not found\n");
    return 1;
}

管道

管道(Pipe)是一种简单的进程间通信机制,它允许一个进程将数据写入管道,而另一个进程从管道中读取数据。管道通常用于将一个进程的输出作为另一个进程的输入,例如:

$ ls | grep "main.c"

在这个命令中,ls 命令将当前目录下的文件列表输出到管道,而 grep 命令从管道中读取数据,并过滤出包含 "main.c" 字符串的行。

创建管道

可以使用 pipe() 系统调用创建管道。管道由两个文件符组成:一个用于读,一个用于写。

#include <unistd.h>

int main() {
    int fd[2];
    pipe(fd);

    // 将标准输出重定向到管道
    dup2(fd[1], STDOUT_FILENO);

    // 执行 ls 命令
    execlp("ls", "ls", "-l", NULL);

    // 从管道中读取数据并打印
    close(fd[1]);
    char buf[1024];
    while (read(fd[0], buf, sizeof(buf)) > 0) {
        printf("%s", buf);
    }

    close(fd[0]);
    return 0;
}

使用管道

创建管道后,就可以使用 read()write() 函数在进程之间传递数据。

#include <unistd.h>

int main() {
    int fd[2];
    pipe(fd);

    int pid = fork();
    if (pid == 0) {
        // 子进程

        // 将标准输出重定向到管道
        dup2(fd[1], STDOUT_FILENO);

        // 执行 ls 命令
        execlp("ls", "ls", "-l", NULL);
    } else {
        // 父进程

        // 从管道中读取数据并打印
        close(fd[1]);
        char buf[1024];
        while (read(fd[0], buf, sizeof(buf)) > 0) {
            printf("%s", buf);
        }

        close(fd[0]);
    }

    return 0;
}

FIFO

FIFO(First In First Out)是一种特殊类型的管道,它允许多个进程同时读写数据。FIFO通常用于在不同进程之间交换数据,例如:

$ mkfifo myfifo
$ cat > myfifo &
$ echo "Hello, world!" > myfifo

在这个命令中,mkfifo 命令创建了一个名为 myfifo 的 FIFO,cat 命令将标准输入重定向到 myfifo,然后在后台运行。echo 命令将 "Hello, world!" 字符串写入 myfifo

创建 FIFO

可以使用 mkfifo() 系统调用创建 FIFO。

#include <sys/stat.h>
#include <fcntl.h>

int main() {
    mkfifo("myfifo", 0666);
    return 0;
}

使用 FIFO

创建 FIFO 后,就可以使用 open() 函数打开 FIFO,然后使用 read()write() 函数在进程之间传递数据。

#include <sys/stat.h>
#include <fcntl.h>

int main() {
    int fd = open("myfifo", O_RDWR);

    if (fd == -1) {
        perror("open");
        exit(1);
    }

    char buf[1024];
    while (read(fd, buf, sizeof(buf)) > 0) {
        printf("%s", buf);
    }

    close(fd);
    return 0;
}

消息队列

消息队列(Message Queue)是一种进程间通信机制,它允许进程在内核中创建和维护一个消息队列,并通过这个队列发送和接收消息。消息队列通常用于在不同进程之间传递复杂的数据结构,例如:

$ ipcs -q

这个命令显示了系统中所有的消息队列。

创建消息队列

可以使用 msgget() 系统调用创建消息队列。

#include <sys/msg.h>

int main() {
    int msgid = msgget(IPC_PRIVATE, 0666);
    if (msgid == -1) {
        perror("msgget");
        exit(1);
    }

    return 0;
}

使用消息队列

创建消息队列后,就可以使用 msgsnd()msgrcv() 函数在进程之间发送和接收消息。

#include <sys/msg.h>

int main() {
    int msgid = msgget(IPC_PRIVATE, 0666);
    if (msgid == -1) {
        perror("msgget");
        exit(1);
    }

    struct msgbuf {
        long mtype;
        char mtext[1024];
    };

    struct msgbuf msg;
    msg.mtype = 1;
    strcpy(msg.mtext, "Hello, world!");

    if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
        perror("msgsnd");
        exit(1);
    }

    struct msgbuf rcv_msg;
    if (msgrcv(msgid, &rcv_msg, sizeof(rcv_msg.mtext), 1, 0) == -1) {
        perror("msgrcv");
        exit(1);
    }

    printf("Received message: %s\n", rcv_msg.mtext);

    return 0;
}

共享内存

共享内存(Shared Memory)是一种进程间通信机制,它允许进程在内核中创建和维护一块共享内存区域,并通过这块内存区域交换数据。共享内存通常用于在不同进程之间传递大块数据,例如:

$ ipcs -m

这个命令显示了系统中所有的共享内存段。

创建共享内存

可以使用 shmget() 系统调用创建共享内存段。

#include <sys/shm.h>

int main() {
    int shmid = shmget(IPC_PRIVATE, 1024, 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    return 0;
}

使用共享内存

创建共享内存段后,就可以使用 shmat()shmdt() 函数将共享内存段映射到进程的地址空间,然后使用普通的内存访问操作在进程之间交换数据。

#include <sys/shm.h>

int main() {