GoDm@'s Blog

POSIX IPC:System V 的继任者

版权信息

warning

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


1. POSIX IPC:System V 的继任者

POSIX IPC(Portable Operating System Interface)是一套新的 IPC 标准,旨在解决 System V IPC 的一些局限性。它提供了一套更统一、更现代的 API,使用文件名作为标识符,而不是 System V 的键值,这使得 IPC 资源的管理更加直观。

1.1. POSIX 消息队列

与 System V 消息队列类似,但 API 更简洁。

1.1.1. 消息队列属性

在POSIX消息队列中,消息队列属性mq_attr)是用来设置和获取消息队列的特性和参数的结构体。它的主要作用是控制消息队列的行为,例如最大消息数、消息大小等。mq_attr结构体包含了以下几个字段:

struct mq_attr {
    long    mq_flags;      // 消息队列的标志
    long    mq_maxmsg;     // 消息队列中最多可以容纳的消息数量
    long    mq_msgsize;    // 队列中每条消息的最大大小
    long    mq_curmsgs;    // 当前队列中的消息数
};
  1. mq_flags

    这个字段用于设置消息队列的标志。常见的标志有:

    • O_NONBLOCK:以非阻塞模式打开消息队列。这意味着如果消息队列为空,接收操作(mq_receive())会立即返回,而不是阻塞等待。

    • O_RDWR:允许对消息队列进行读写操作。

    如果没有设置这些标志,消息队列将会默认阻塞模式操作。

  2. mq_maxmsg

    这个字段定义了消息队列可以容纳的最大消息数量。如果消息队列已经存满,且没有空间容纳新的消息,后续的消息发送(mq_send())将会被阻塞,直到队列中有足够的空间。这个属性通常设置为你希望消息队列中能存储的消息个数。

  3. mq_msgsize

    此字段定义每条消息的最大字节数。在创建消息队列时,必须确保消息发送和接收操作的消息大小不会超过这个值。如果消息超过了这个大小,发送操作将返回错误。

  4. mq_curmsgs

    这个字段是一个只读属性,用于获取当前队列中的消息数量。它是一个动态更新的值,每次读取时会返回当前消息队列中实际存储的消息数。这个字段对应用程序来说很有用,尤其是在需要了解队列当前状态的场景。

这个例子展示了两个不相关的进程如何通过 POSIX 消息队列进行通信。
发送方(sender.c):

#include <stdio.h>
#include <stdlib.h>          /* 动态内存管理及其他辅助功能如exit、abort */
#include <string.h>
#include <fcntl.h>           /* 提供 O_CREAT, O_RDWR 用于文件控制 */
#include <sys/stat.h>        /* 文件属性操作 如chmod、umask */
#include <mqueue.h>          /* 提供POSIX消息队列支持 */
#include <unistd.h>          /* 提供POSIX接口 */

#define MQ_NAME "/my_mq"

int main() {
    mqd_t mqd;
    struct mq_attr attr;
    char buffer[256];
    const char *msg = "Hello from sender!";

    // 设置消息队列属性
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 256;
    attr.mq_curmsgs = 0;

    // 创建或打开消息队列
    mqd = mq_open(MQ_NAME, O_CREAT | O_WRONLY, 0666, &attr);
    if (mqd == (mqd_t)-1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }

    printf("Sender: Sending message...\n");

    // 发送消息,优先级为1
    if (mq_send(mqd, msg, strlen(msg) + 1, 1) == -1) {
        perror("mq_send");
        exit(EXIT_FAILURE);
    }

    // 关闭消息队列描述符
    mq_close(mqd);

    printf("Sender: Message sent and mq closed.\n");
    return 0;
}

接收方(receiver.c):

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

#define MQ_NAME "/my_mq"

int main() {
    mqd_t mqd;
    struct mq_attr attr;
    char buffer[256 + 1];
    unsigned int prio;

    // 打开消息队列
    mqd = mq_open(MQ_NAME, O_RDONLY);
    if (mqd == (mqd_t)-1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }

    // 获取消息队列属性
    mq_getattr(mqd, &attr);

    printf("Receiver: Waiting for message...\n");

    // 接收消息
    if (mq_receive(mqd, buffer, attr.mq_msgsize, &prio) == -1) {
        perror("mq_receive");
        exit(EXIT_FAILURE);
    }

    printf("Receiver: Received message: %s (Priority: %u)\n", buffer, prio);

    // 关闭和删除消息队列
    mq_close(mqd);
    mq_unlink(MQ_NAME);

    return 0;
}

编译和运行gcc sender.c -o sender -lrt gcc receiver.c -o receiver -lrt (需要链接实时库 -lrt) 先运行 ./sender,再运行 ./receiver

1.2. POSIX 信号量

用于进程间的同步,功能与 System V 信号量类似,但提供了更简单的接口。

这个例子展示了如何使用 POSIX 信号量来同步两个进程对一个共享资源的访问。

共享资源访问程序 (sem_client.c):

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>           /* For O_CREAT, O_RDWR */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <unistd.h>

#define SEM_NAME "/my_semaphore"

int main() {
    sem_t *sem;
    int value;

    // 打开信号量
    sem = sem_open(SEM_NAME, 0); // O_CREAT is not needed here
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Client: Waiting to get semaphore...\n");

    // P 操作:等待信号量,如果值为0则阻塞
    if (sem_wait(sem) == -1) {
        perror("sem_wait");
        exit(EXIT_FAILURE);
    }

    printf("Client: Got the semaphore. Accessing shared resource...\n");
    sleep(2); // 模拟访问共享资源

    printf("Client: Finished. Releasing semaphore...\n");

    // V 操作:释放信号量,计数器加1
    if (sem_post(sem) == -1) {
        perror("sem_post");
        exit(EXIT_FAILURE);
    }

    // 关闭信号量
    sem_close(sem);

    return 0;
}

信号量控制程序 (sem_main.c):`

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

#define SEM_NAME "/my_semaphore"

int main() {
    sem_t *sem;
    pid_t pid;

    // 创建并初始化信号量,初始值为1
    sem = sem_open(SEM_NAME, O_CREAT, 0666, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Main: Semaphore created and initialized to 1.\n");

    // 创建一个子进程
    pid = fork();
    if (pid == 0) {
        // 子进程运行另一个程序来访问信号量
        execlp("./sem_client", "sem_client", NULL);
        perror("execlp"); // 如果执行失败
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        // 父进程也尝试访问信号量
        sleep(1); // 确保子进程先运行
        printf("Main: Waiting to get semaphore...\n");
        sem_wait(sem);
        printf("Main: Got the semaphore. Accessing shared resource...\n");
        sleep(2);
        printf("Main: Finished. Releasing semaphore...\n");
        sem_post(sem);

        wait(NULL); // 等待子进程结束

        // 删除信号量
        sem_unlink(SEM_NAME);
        printf("Main: Child process finished. Semaphore unlinked.\n");
    }

    // 关闭信号量
    sem_close(sem);

    return 0;
}

编译和运行gcc sem_client.c -o sem_client -lrt gcc sem_main.c -o sem_main -lrt (同样需要链接实时库 -lrt) 运行 ./sem_main

1.3. POSIX 共享内存

和 System V 共享内存一样,都是最快的 IPC 方式,但 POSIX 版本使用了文件描述符。

这个例子展示了两个进程如何通过 POSIX 共享内存来共享一块内存区域,并用一个信号量来同步。

写入方 (shm_writer.c)

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

#define SHM_NAME "/my_shm"
#define SEM_NAME "/my_shm_sem"
#define SHM_SIZE 4096

int main() {
    int shm_fd;
    void *ptr;
    sem_t *sem;
    const char *msg = "Hello from shm writer!";

    // 创建/打开共享内存对象
    shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        exit(EXIT_FAILURE);
    }

    // 设置共享内存大小
    ftruncate(shm_fd, SHM_SIZE);

    // 将共享内存映射到进程地址空间
    ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    // 创建/打开信号量,用于同步
    sem = sem_open(SEM_NAME, O_CREAT, 0666, 0); // 初始值为0,表示不可访问
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Writer: Writing to shared memory...\n");
    // 写入数据
    sprintf(ptr, "%s", msg);

    // V 操作:释放信号量,通知读者可以读取了
    sem_post(sem);
    printf("Writer: Data written and semaphore posted.\n");

    // 关闭
    sem_close(sem);
    munmap(ptr, SHM_SIZE);
    close(shm_fd);

    return 0;
}

读取方 (shm_reader.c):

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

#define SHM_NAME "/my_shm"
#define SEM_NAME "/my_shm_sem"
#define SHM_SIZE 4096

int main() {
    int shm_fd;
    void *ptr;
    sem_t *sem;

    // 打开共享内存对象
    shm_fd = shm_open(SHM_NAME, O_RDONLY, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        exit(EXIT_FAILURE);
    }

    // 将共享内存映射到进程地址空间
    ptr = mmap(0, SHM_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    // 打开信号量
    sem = sem_open(SEM_NAME, 0);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Reader: Waiting for writer...\n");
    // P 操作:等待信号量,直到有数据可用
    sem_wait(sem);

    printf("Reader: Data received: %s\n", (char *)ptr);

    // 关闭和清理
    sem_close(sem);
    sem_unlink(SEM_NAME); // 删除信号量
    munmap(ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink(SHM_NAME); // 删除共享内存对象

    return 0;
}

编译和运行gcc shm_writer.c -o shm_writer -lrt gcc shm_reader.c -o shm_reader -lrt (同样需要链接实时库 -lrt) 先运行 ./shm_writer,再运行 ./shm_reader


共计约2.4k字。于2025/09/15首次发布,最后更新于2025/09/15。

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

AI辅助创作:本文部分内容由 Gemini 2.5 Flash 生成,最终版本由作者审核与修改。了解该AI模型

#Linux | #IPC | #POSIX |
  1. 1. POSIX IPC:System V 的继任者
    1. 1.1. POSIX 消息队列
    2. 1.2. POSIX 信号量
    3. 1.3. POSIX 共享内存