Linux 系统中,进程间通信机制

发布于:2025-02-14 ⋅ 阅读:(15) ⋅ 点赞:(0)

在 Linux 系统中,进程间通信(Inter-Process Communication, IPC)是多个进程之间交换数据和同步操作的机制。Linux 提供了多种 IPC 方式,每种方式适用于不同的场景。以下是常见的 IPC 方式及其详解:


1. 管道(Pipe)

管道是一种半双工的通信方式,数据只能单向流动。通常用于父子进程或兄弟进程之间的通信。

(1)无名管道(Anonymous Pipe)
  • 特点
    • 只能在具有亲缘关系的进程之间使用(如父子进程)。
    • 通过 pipe() 系统调用创建。
    • 数据是字节流,没有消息边界。
  • 示例
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        int fd[2];
        pipe(fd); // 创建管道
    
        if (fork() == 0) { // 子进程
            close(fd[0]); // 关闭读端
            write(fd[1], "Hello", 6); // 写入数据
            close(fd[1]);
        } else { // 父进程
            close(fd[1]); // 关闭写端
            char buf[6];
            read(fd[0], buf, 6); // 读取数据
            printf("Received: %s\n", buf);
            close(fd[0]);
        }
        return 0;
    }
    
(2)命名管道(Named Pipe, FIFO)
  • 特点
    • 可以在无关进程之间使用。
    • 通过 mkfifo() 创建,是一个特殊的文件。
    • 数据是字节流,没有消息边界。
  • 示例
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    int main() {
        mkfifo("my_fifo", 0666); // 创建命名管道
    
        if (fork() == 0) { // 子进程
            int fd = open("my_fifo", O_WRONLY);
            write(fd, "Hello", 6);
            close(fd);
        } else { // 父进程
            int fd = open("my_fifo", O_RDONLY);
            char buf[6];
            read(fd, buf, 6);
            printf("Received: %s\n", buf);
            close(fd);
        }
        return 0;
    }
    

2. 消息队列(Message Queue)

消息队列是一种全双工的通信方式,允许进程通过消息的形式交换数据。

  • 特点
    • 消息是有边界的,发送和接收的是完整的消息。
    • 消息队列独立于进程存在,即使进程结束,消息队列仍然存在。
    • 通过 msgget()msgsnd()msgrcv() 等系统调用操作。
  • 示例
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdio.h>
    #include <string.h>
    
    struct msg_buffer {
        long msg_type;
        char msg_text[100];
    };
    
    int main() {
        int msgid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); // 创建消息队列
    
        struct msg_buffer message;
        message.msg_type = 1;
        strcpy(message.msg_text, "Hello");
    
        msgsnd(msgid, &message, sizeof(message), 0); // 发送消息
    
        msgrcv(msgid, &message, sizeof(message), 1, 0); // 接收消息
        printf("Received: %s\n", message.msg_text);
    
        msgctl(msgid, IPC_RMID, NULL); // 删除消息队列
        return 0;
    }
    

3. 共享内存(Shared Memory)

共享内存允许多个进程共享同一块内存区域,是最高效的 IPC 方式。

  • 特点
    • 数据直接存储在内存中,无需内核介入。
    • 需要同步机制(如信号量)来避免竞争条件。
    • 通过 shmget()shmat()shmdt() 等系统调用操作。
  • 示例
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        int shmid = shmget(IPC_PRIVATE, 1024, 0666 | IPC_CREAT); // 创建共享内存
        char *shmaddr = (char *)shmat(shmid, NULL, 0); // 附加共享内存
    
        if (fork() == 0) { // 子进程
            strcpy(shmaddr, "Hello");
            shmdt(shmaddr); // 分离共享内存
        } else { // 父进程
            wait(NULL); // 等待子进程完成
            printf("Received: %s\n", shmaddr);
            shmdt(shmaddr); // 分离共享内存
            shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
        }
        return 0;
    }
    

4. 信号量(Semaphore)

信号量用于进程间的同步,避免竞争条件。

  • 特点
    • 通常与共享内存一起使用。
    • 通过 semget()semop() 等系统调用操作。
  • 示例
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <stdio.h>
    
    int main() {
        int semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); // 创建信号量
        semctl(semid, 0, SETVAL, 1); // 初始化信号量值为 1
    
        struct sembuf op = {0, -1, 0}; // P 操作
        semop(semid, &op, 1);
    
        printf("Critical section\n");
    
        op.sem_op = 1; // V 操作
        semop(semid, &op, 1);
    
        semctl(semid, 0, IPC_RMID); // 删除信号量
        return 0;
    }
    

5. 套接字(Socket)

套接字是一种通用的 IPC 方式,可以用于本地或网络通信。

  • 特点
    • 支持跨机器的进程通信。
    • 通过 socket()bind()listen()accept() 等系统调用操作。
  • 示例
    #include <sys/socket.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    
    int main() {
        int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); // 创建套接字
    
        struct sockaddr_un addr;
        addr.sun_family = AF_UNIX;
        strcpy(addr.sun_path, "my_socket");
    
        bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)); // 绑定地址
        listen(sockfd, 5); // 监听连接
    
        int clientfd = accept(sockfd, NULL, NULL); // 接受连接
        write(clientfd, "Hello", 6); // 发送数据
        close(clientfd);
        close(sockfd);
        return 0;
    }
    

6. 信号(Signal)

信号是一种异步通信机制,用于通知进程发生了某个事件。

  • 特点
    • 信号是异步的,进程无法预测何时收到信号。
    • 通过 kill() 发送信号,通过 signal()sigaction() 处理信号。
  • 示例
    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    
    void handler(int sig) {
        printf("Received signal: %d\n", sig);
    }
    
    int main() {
        signal(SIGINT, handler); // 注册信号处理函数
        while (1) {
            printf("Running...\n");
            sleep(1);
        }
        return 0;
    }
    

总结

IPC 方式 特点
管道 半双工,适用于亲缘进程。
消息队列 全双工,消息有边界。
共享内存 高效,需要同步机制。
信号量 用于同步,避免竞争条件。
套接字 通用,支持本地和网络通信。
信号 异步通信,用于事件通知。

根据具体需求选择合适的 IPC 方式,可以高效地实现进程间的通信和同步。