Linux进程间通信

在 Linux 系统中,进程间通信(IPC,Inter-Process Communication)是实现多进程协作的核心机制。常见的 IPC 方法包括:管道(Pipe)、命名管道(FIFO)、信号(Signal)、共享内存(Shared Memory)、消息队列(Message Queue)、信号量(Semaphore)和套接字(Socket)。以下分别介绍这些方法并提供示例代码。

在 Linux 系统中,进程间通信(IPC)是多进程协作的基础,提供了多种机制满足不同场景需求。以下详细介绍七种常用 IPC 方法并提供示例代码:

1. 管道(Pipe)

特点

  • 半双工通信(单向传输),仅适用于父子或兄弟进程
  • 基于内存的临时通道,进程退出后自动销毁
  • 无需显式创建文件节点

示例:父子进程管道通信

运行

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main() {
    int pipefd[2];  // 0:读端, 1:写端
    pid_t pid;
    char buf[100];

    // 创建管道
    if (pipe(pipefd) == -1) {
        perror("pipe failed");
        return 1;
    }

    pid = fork();
    if (pid == -1) {
        perror("fork failed");
        return 1;
    }

    if (pid == 0) {  // 子进程:读取数据
        close(pipefd[1]);  // 关闭写端
        read(pipefd[0], buf, sizeof(buf));
        printf("子进程收到: %s\n", buf);
        close(pipefd[0]);
    } else {  // 父进程:写入数据
        close(pipefd[0]);  // 关闭读端
        const char* msg = "Hello from parent";
        write(pipefd[1], msg, strlen(msg)+1);
        close(pipefd[1]);
        wait(NULL);  // 等待子进程结束
    }

    return 0;
}

编译运行

bash

gcc pipe_demo.c -o pipe_demo && ./pipe_demo

2. 命名管道(FIFO)

特点

  • 允许无亲缘关系进程通信,通过文件系统路径标识
  • 半双工通信,需要显式创建和删除
  • 具有文件属性,可通过ls查看,rm删除

示例:两个独立进程通信

写进程(fifo_write.c)

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char* fifo_path = "/tmp/myfifo";
    // 创建FIFO
    mkfifo(fifo_path, 0666);

    int fd = open(fifo_path, O_WRONLY);
    const char* msg = "Hello from FIFO writer";
    write(fd, msg, strlen(msg)+1);
    close(fd);
    unlink(fifo_path);  // 可选:删除FIFO

    return 0;
}

读进程(fifo_read.c)

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

int main() {
    const char* fifo_path = "/tmp/myfifo";
    char buf[100];

    int fd = open(fifo_path, O_RDONLY);
    read(fd, buf, sizeof(buf));
    printf("收到: %s\n", buf);
    close(fd);

    return 0;
}

编译运行

# 终端1
gcc fifo_read.c -o fifo_read && ./fifo_read

# 终端2
gcc fifo_write.c -o fifo_write && ./fifo_write

3. 信号(Signal)

特点

  • 用于通知进程发生特定事件的异步机制
  • 只能传递信号编号,不能携带大量数据
  • 每个信号有默认处理方式,可自定义处理函数

示例:信号发送与处理

接收进程(signal_recv.c)

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

// 自定义信号处理函数
void handle_sigusr1(int signo) {
    printf("收到SIGUSR1信号\n");
}

int main() {
    printf("接收进程PID: %d\n", getpid());
    signal(SIGUSR1, handle_sigusr1);  // 注册信号处理函数
    
    while(1) pause();  // 等待信号
    return 0;
}

发送进程(signal_send.c)

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    if (argc != 2) {
        printf("用法: %s <目标进程PID>\n", argv[0]);
        return 1;
    }
    
    pid_t pid = atoi(argv[1]);
    kill(pid, SIGUSR1);  // 发送信号
    printf("已发送SIGUSR1信号\n");
    
    return 0;
}

编译运行

# 终端1
gcc signal_recv.c -o signal_recv && ./signal_recv

# 终端2 (替换1234为实际PID)
gcc signal_send.c -o signal_send && ./signal_send 1234

4. 共享内存(Shared Memory)

特点

  • 多个进程共享同一块物理内存,IPC 中速度最快
  • 需配合同步机制(如信号量)避免数据竞争
  • 通过键值标识,需显式创建和销毁

示例:共享内存读写

写进程(shm_write.c)

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

#define SHM_KEY 0x1234
#define SHM_SIZE 1024

int main() {
    // 创建共享内存
    int shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) { perror("shmget"); return 1; }

    // 映射到进程地址空间
    char* shm_ptr = (char*)shmat(shmid, NULL, 0);
    if (shm_ptr == (void*)-1) { perror("shmat"); return 1; }

    // 写入数据
    strcpy(shm_ptr, "Hello from shared memory");
    printf("已写入共享内存\n");

    // 等待读取
    printf("按回车继续...\n");
    getchar();

    // 清理
    shmdt(shm_ptr);
    shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存
    return 0;
}

读进程(shm_read.c)

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_KEY 0x1234
#define SHM_SIZE 1024

int main() {
    // 获取共享内存
    int shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
    if (shmid == -1) { perror("shmget"); return 1; }

    // 映射到进程地址空间
    char* shm_ptr = (char*)shmat(shmid, NULL, 0);
    if (shm_ptr == (void*)-1) { perror("shmat"); return 1; }

    // 读取数据
    printf("从共享内存读取: %s\n", shm_ptr);

    // 清理
    shmdt(shm_ptr);
    return 0;
}

编译运行

# 终端1
gcc shm_write.c -o shm_write && ./shm_write

# 终端2
gcc shm_read.c -o shm_read && ./shm_read

5. 消息队列(Message Queue)

特点

  • 以消息为单位传递数据,支持按类型接收
  • 消息有优先级,可实现异步通信
  • 生命周期随内核,需显式删除

示例:消息队列通信

发送进程(msg_send.c)

#include <stdio.h>
#include <sys/msg.h>
#include <string.h>

#define MSG_KEY 0x4321

// 消息结构
struct msgbuf {
    long mtype;       // 消息类型
    char mtext[100];  // 消息内容
};

int main() {
    // 创建消息队列
    int msqid = msgget(MSG_KEY, IPC_CREAT | 0666);
    if (msqid == -1) { perror("msgget"); return 1; }

    struct msgbuf msg;
    msg.mtype = 1;  // 设置消息类型
    strcpy(msg.mtext, "Hello from message queue");
    
    // 发送消息
    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
    printf("消息已发送\n");

    return 0;
}

接收进程(msg_recv.c)

#include <stdio.h>
#include <sys/msg.h>

#define MSG_KEY 0x4321

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

int main() {
    // 获取消息队列
    int msqid = msgget(MSG_KEY, 0666);
    if (msqid == -1) { perror("msgget"); return 1; }

    struct msgbuf msg;
    // 接收类型为1的消息
    msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0);
    printf("收到消息: %s\n", msg.mtext);

    // 删除消息队列
    msgctl(msqid, IPC_RMID, NULL);
    return 0;
}

编译运行

# 终端1
gcc msg_recv.c -o msg_recv && ./msg_recv

# 终端2
gcc msg_send.c -o msg_send && ./msg_send

6. 信号量(Semaphore)

特点

  • 用于进程同步,控制对共享资源的访问
  • 通过 PV 操作(P: 获取资源,V: 释放资源)实现
  • 可用于多个进程间的同步

示例:信号量同步

#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <unistd.h>

#define SEM_KEY 0x5678

// 信号量操作函数
void sem_op(int semid, int op) {
    struct sembuf sops;
    sops.sem_num = 0;  // 信号量编号
    sops.sem_op = op;  // 操作:-1(P), +1(V)
    sops.sem_flg = 0;
    semop(semid, &sops, 1);
}

int main() {
    // 创建信号量(初始值1)
    int semid = semget(SEM_KEY, 1, IPC_CREAT | 0666);
    semctl(semid, 0, SETVAL, 1);  // 设置初始值

    if (fork() == 0) {  // 子进程
        sem_op(semid, -1);  // P操作:获取资源
        printf("子进程获得资源\n");
        sleep(2);  // 模拟资源使用
        printf("子进程释放资源\n");
        sem_op(semid, 1);   // V操作:释放资源
        return 0;
    }

    // 父进程
    sem_op(semid, -1);  // P操作:获取资源
    printf("父进程获得资源\n");
    sleep(2);  // 模拟资源使用
    printf("父进程释放资源\n");
    sem_op(semid, 1);   // V操作:释放资源

    wait(NULL);
    semctl(semid, 0, IPC_RMID);  // 删除信号量
    return 0;
}

编译运行

gcc sem_demo.c -o sem_demo && ./sem_demo

7. 套接字(Socket)

特点

  • 支持跨网络和本地进程通信
  • 全双工通信,提供可靠字节流
  • 本地通信使用 AF_UNIX 域,通过文件系统标识

示例:本地套接字通信

服务端(socket_server.c)

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>

#define SOCK_PATH "/tmp/mysock"

int main() {
    int server_fd, client_fd;
    struct sockaddr_un addr;
    char buf[100];

    // 创建套接字
    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_fd == -1) { perror("socket"); return 1; }

    // 绑定地址
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, SOCK_PATH);
    unlink(SOCK_PATH);  // 确保路径可用
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));

    // 监听连接
    listen(server_fd, 1);
    
    // 接受连接
    client_fd = accept(server_fd, NULL, NULL);
    read(client_fd, buf, sizeof(buf));
    printf("收到: %s\n", buf);

    // 清理
    close(client_fd);
    close(server_fd);
    unlink(SOCK_PATH);
    return 0;
}

客户端(socket_client.c)

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>

#define SOCK_PATH "/tmp/mysock"

int main() {
    int fd;
    struct sockaddr_un addr;

    // 创建套接字
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd == -1) { perror("socket"); return 1; }

    // 连接服务器
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, SOCK_PATH);
    connect(fd, (struct sockaddr*)&addr, sizeof(addr));

    // 发送数据
    const char* msg = "Hello from socket client";
    write(fd, msg, strlen(msg)+1);

    close(fd);
    return 0;
}

编译运行

# 终端1
gcc socket_server.c -o socket_server && ./socket_server

# 终端2
gcc socket_client.c -o socket_client && ./socket_client

 

总结

方法适用场景优点缺点
管道父子进程简单通信简单高效仅限亲缘进程,单向
命名管道无亲缘关系进程简单,可跨进程单向,需文件系统
信号事件通知异步,开销小数据量有限
共享内存大数据传输速度最快需要同步机制
消息队列异步消息传递按类型接收速度较慢
信号量进程同步有效控制资源不传递数据
套接字网络 / 本地通信灵活,全双工开销较大

选择 IPC 方法时,需根据进程关系、数据量、同步需求和性能要求综合考虑。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值