嵌入式学习|C语言篇进程间通信(IPC)全面解析与示例

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

一、进程通信基础概念

1.1 进程隔离原理

现代操作系统通过虚拟内存技术为每个进程创建独立的地址空间,这种隔离机制保障了系统的安全性,但也导致进程无法直接访问彼此的内存数据。进程间通信(IPC)正是为解决这一矛盾而设计的核心机制。

1.2 IPC分类体系

主要通信方式可分为:

  • 传统Unix IPC:管道、FIFO

  • System V IPC:消息队列、信号量、共享内存

  • POSIX IPC:改进的消息队列、信号量、共享内存

  • 网络扩展:套接字(本地/网络)

  • 高级封装:D-Bus、RPC等

二、核心通信机制详解

2.1 匿名管道(Anonymous Pipes)

实现原理:
使用单向数据通道,通过pipe()系统调用创建,返回两个文件描述符(fd[0]读端,fd[1]写端)

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

#define BUFFER_SIZE 25

int main() {
    int fd[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];

    if (pipe(fd) == -1) {
        perror("pipe creation failed");
        exit(EXIT_FAILURE);
    }

    pid = fork();
    if (pid == 0) { // 子进程
        close(fd[1]); // 关闭写端
        read(fd[0], buffer, BUFFER_SIZE);
        printf("Child received: %s\n", buffer);
        close(fd[0]);
    } else { // 父进程
        close(fd[0]); // 关闭读端
        const char* msg = "Hello from parent";
        write(fd[1], msg, strlen(msg)+1);
        close(fd[1]);
        wait(NULL);
    }
    return 0;
}

2.2 命名管道(FIFO)

创建与使用:

bash

复制

mkfifo /tmp/myfifo  # 命令行创建

写入端代码:

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

int main() {
    int fd = open("/tmp/myfifo", O_WRONLY);
    char* data = "FIFO Message";
    write(fd, data, strlen(data)+1);
    close(fd);
    return 0;
}

读取端代码:

#include <fcntl.h>

int main() {
    int fd = open("/tmp/myfifo", O_RDONLY);
    char buffer[100];
    read(fd, buffer, sizeof(buffer));
    printf("Received: %s\n", buffer);
    close(fd);
    return 0;
}

2.3 System V消息队列

完整示例:

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

struct msg_buffer {
    long msg_type;
    char msg_text[100];
};

int main() {
    key_t key = ftok("progfile", 65);
    int msgid = msgget(key, 0666 | IPC_CREAT);

    // 发送消息
    struct msg_buffer message = {1, "Message Content"};
    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;
}

2.4 共享内存进阶

带同步的共享内存示例:

#include <sys/shm.h>
#include <sys/sem.h>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int main() {
    key_t key = ftok("shmfile",65);
    int shmid = shmget(key, 1024, 0666|IPC_CREAT);
    char *str = (char*) shmat(shmid,(void*)0,0);

    // 创建信号量
    int semid = semget(key, 1, 0666|IPC_CREAT);
    union semun arg;
    arg.val = 1; // 二进制信号量
    semctl(semid, 0, SETVAL, arg);

    struct sembuf sb = {0, -1, 0}; // P操作
    semop(semid, &sb, 1);

    // 临界区操作
    sprintf(str, "Shared Memory Data");
    printf("Data written: %s\n", str);

    sb.sem_op = 1; // V操作
    semop(semid, &sb, 1);

    shmdt(str);
    shmctl(shmid, IPC_RMID, NULL);
    semctl(semid, 0, IPC_RMID);
    return 0;
}

2.5 POSIX信号量

生产者-消费者模型示例:

#include <semaphore.h>
#include <fcntl.h>

#define SHM_SIZE 1024

int main() {
    sem_t *empty = sem_open("/empty", O_CREAT, 0644, 5);
    sem_t *full = sem_open("/full", O_CREAT, 0644, 0);
    sem_t *mutex = sem_open("/mutex", O_CREAT, 0644, 1);

    int shm_fd = shm_open("/buffer", O_CREAT|O_RDWR, 0666);
    ftruncate(shm_fd, SHM_SIZE);
    char *buffer = mmap(0, SHM_SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);

    // 生产者逻辑
    for(int i=0; i<10; i++) {
        sem_wait(empty);
        sem_wait(mutex);

        // 写入共享内存
        sprintf(buffer, "Item %d", i);
        
        sem_post(mutex);
        sem_post(full);
    }

    sem_close(empty);
    sem_unlink("/empty");
    // 类似清理其他信号量和共享内存
    return 0;
}

三、高级通信技术

3.1 域套接字(Unix Domain Socket)

服务端实现:

#include <sys/socket.h>
#include <sys/un.h>

#define SOCK_PATH "/tmp/example.sock"

int main() {
    int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path)-1);

    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 5);

    int client_fd = accept(server_fd, NULL, NULL);
    char buffer[100];
    read(client_fd, buffer, sizeof(buffer));
    printf("Received: %s\n", buffer);

    close(client_fd);
    unlink(SOCK_PATH);
    return 0;
}

3.2 内存映射文件(mmap)

跨进程文件映射示例:

#include <sys/mman.h>

int main() {
    int fd = open("data.bin", O_RDWR | O_CREAT, 0666);
    ftruncate(fd, 4096);
    char *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

    // 写入数据
    sprintf(mem, "Memory mapped data");

    // 同步到磁盘
    msync(mem, 4096, MS_SYNC);
    munmap(mem, 4096);
    close(fd);
    return 0;
}