当前位置: 首页 > news >正文

9、Linux C 消息队列和信号灯

一. 消息队列

1.1 发送端

  1. 申请 Key

  2. 打开/创建消息队列msgget

  3. 向消息队列发送消息msgsnd

1.2 接收端

  1. 打开/创建消息队列msgget

  2. 从消息队列接收消息msgrcv

  3. 控制(删除)消息队列msgctl


1.3 打开/创建消息队列

#include <sys/ipc.h>
#include <sys/msg.h>
​
int msgget(key_t key, int msgflg);

1.3.1 参数说明

  • key:和消息队列关联的 key,可使用 IPC_PRIVATEftok 生成。

  • msgflg:标志位,通常为 IPC_CREAT | 0666

    • IPC_CREAT:若消息队列不存在则创建,存在则打开。

1.3.2 返回值

  • 成功:返回消息队列的 ID。

  • 失败:返回 -1。

1.3.3 示例代码

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
​
int main() {
    key_t key = ftok("msgqueue", 'a'); // 生成 key
    if (key == -1) {
        perror("ftok failed");
        return 1;
    }
​
    int msgid = msgget(key, IPC_CREAT | 0666); // 打开/创建消息队列
    if (msgid == -1) {
        perror("msgget failed");
        return 1;
    }
​
    printf("消息队列已创建,ID: %d\n", msgid);
    return 0;
}

1.4 发送消息

#include <sys/ipc.h>
#include <sys/msg.h>
​
int msgsnd(int msgqid, const void *msgp, size_t size, int msgflg);

1.4.1 参数说明

  • msgqid:消息队列 ID。

  • msgp:消息缓冲区地址。

  • size:消息正文长度。

  • msgflg:标志位,可为 0IPC_NOWAIT

    • 0:消息队列满时阻塞,直到消息写入。

    • IPC_NOWAIT:消息队列满时立即返回。

1.4.2 返回值

  • 成功:返回 0。

  • 失败:返回 -1。

1.4.3 示例代码

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
​
typedef struct {
    long msg_type; // 消息类型
    char buf[128]; // 消息内容
} msgT;
​
int main() {
    key_t key = ftok("msgqueue", 'a'); // 生成 key
    if (key == -1) {
        perror("ftok failed");
        return 1;
    }
​
    int msgid = msgget(key, IPC_CREAT | 0666); // 打开/创建消息队列
    if (msgid == -1) {
        perror("msgget failed");
        return 1;
    }
​
    msgT msg;
    msg.msg_type = 1;
    strcpy(msg.buf, "你好,这是消息队列发送的消息!");
​
    if (msgsnd(msgid, &msg, sizeof(msg.buf), 0) == -1) {
        perror("msgsnd failed");
        return 1;
    }
​
    printf("消息已发送到消息队列\n");
    return 0;
}

1.5 消息格式

typedef struct {
    long msg_type; // 消息类型
    char buf[128]; // 消息内容
} msgT;

1.5.1 注意事项

  1. 消息结构必须包含 long 类型的 msg_type 字段。

  2. 消息长度不包括 msg_type 的长度。


1.6 消息的接收

#include <sys/ipc.h>
#include <sys/msg.h>
​
int msgrcv(int msgqid, void *msgp, size_t size, long msgtype, int msgflg);

1.6.1 参数说明

  • msgqid:消息队列 ID。

  • msgp:消息缓冲区地址。

  • size:指定接收的消息长度。

  • msgtype:指定接收的消息类型。

    • msgtype=0:接收第一条消息,任意类型。

    • msgtype>0:接收第一条指定类型的消息。

    • msgtype<0:接收类型小于等于绝对值的消息。

  • msgflg:标志位。

    • 0:阻塞式接收。

    • IPC_NOWAIT:如果没有返回条件的消息立即返回,此时错误码为 ENOMSG

    • IPC_EXCEPT:与 msgtype 配合使用返回队列中第一个类型不为 msgtype 的消息。

1.6.2 返回值

  • 成功:返回收到的消息长度。

  • 失败:返回 -1。

1.6.3 示例代码

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
​
typedef struct {
    long msg_type; // 消息类型
    char buf[128]; // 消息内容
} msgT;
​
int main() {
    key_t key = ftok("msgqueue", 'a'); // 生成 key
    if (key == -1) {
        perror("ftok failed");
        return 1;
    }
​
    int msgid = msgget(key, IPC_CREAT | 0666); // 打开/创建消息队列
    if (msgid == -1) {
        perror("msgget failed");
        return 1;
    }
​
    msgT msg;
    if (msgrcv(msgid, &msg, sizeof(msg.buf), 1, 0) == -1) {
        perror("msgrcv failed");
        return 1;
    }
​
    printf("收到消息:类型=%ld,内容=%s\n", msg.msg_type, msg.buf);
    return 0;
}

1.7 消息队列的控制

#include <sys/ipc.h>
#include <sys/msg.h>
​
int msgctl(int msgqid, int cmd, struct msqid_ds *buf);

1.7.1 参数说明

  • msgqid:消息队列 ID。

  • cmd:要执行的操作。

    • IPC_STAT:获取消息队列状态。

    • IPC_SET:设置消息队列权限。

    • IPC_RMID:删除消息队列。

  • buf:存放消息队列属性的地址。

1.7.2 返回值

  • 成功:返回 0。

  • 失败:返回 -1。

1.7.3 示例代码

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

int main() {
    key_t key = ftok("msgqueue", 'a'); // 生成 key
    if (key == -1) {
        perror("ftok failed");
        return 1;
    }

    int msgid = msgget(key, IPC_CREAT | 0666); // 打开/创建消息队列
    if (msgid == -1) {
        perror("msgget failed");
        return 1;
    }

    // 删除消息队列
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("msgctl failed");
        return 1;
    }

    printf("消息队列已删除\n");
    return 0;
}
二、信号灯/信号量(semaphore)

2.1 概念

信号灯是不同进程间或一个进程内部不同线程间同步的机制。类似我们的 PV 操作,适用于生产者和消费者场景。

2.1.1 PV 操作

  • P(S):申请资源。

    • 若信号量值大于 0,则减 1 并继续运行。

    • 若信号量值为 0,则任务阻塞。

  • V(S):释放资源。

    • 信号量值加 1。

    • 若有任务等待资源,则唤醒一个任务。


2.2 三种信号灯

  1. Posix 有名信号灯

  2. Posix 无名信号灯(Linux 只支持线程同步)

  3. System V 信号灯


2.3 有名信号灯

2.3.1 打开

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
2.3.1.1 参数说明
  • name:信号灯的名字。

  • oflag:打开方式,常用 O_CREAT

  • mode:文件权限,常用 0666

  • value:信号量值,二元信号灯值为 1。

2.3.2 关闭

int sem_close(sem_t *sem);

2.3.3 删除

int sem_unlink(const char *name);

2.3.4 示例代码

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

int main() {
    sem_t *sem = sem_open("mysem", O_CREAT, 0666, 1); // 打开信号灯
    if (sem == SEM_FAILED) {
        perror("sem_open failed");
        return 1;
    }

    // 使用信号灯...

    if (sem_close(sem) == -1) {
        perror("sem_close failed");
        return 1;
    }

    if (sem_unlink("mysem") == -1) {
        perror("sem_unlink failed");
        return 1;
    }

    printf("信号灯已创建并删除\n");
    return 0;
}

2.4 无名信号灯

2.4.1 初始化

int sem_init(sem_t *sem, int shared, unsigned int value);
2.4.1.1 参数说明
  • sem:需要初始化的信号灯变量。

  • shared:指定为 0,表示信号量只能由初始化进程使用。

  • value:信号量值。

2.4.2 销毁

int sem_destroy(sem_t *sem);

2.4.3 示例代码

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

int main() {
    sem_t sem;
    if (sem_init(&sem, 0, 1) == -1) { // 初始化信号灯
        perror("sem_init failed");
        return 1;
    }

    // 使用信号灯...

    if (sem_destroy(&sem) == -1) {
        perror("sem_destroy failed");
        return 1;
    }

    printf("无名信号灯已初始化并销毁\n");
    return 0;
}

2.5 信号灯操作

2.5.1 P 操作(获取资源)

int sem_wait(sem_t *sem);
  • 若信号量为 0,调用线程挂起,直到有空闲资源。

2.5.2 V 操作(释放资源)

int sem_post(sem_t *sem);
  • 若无等待线程,信号量值加 1。

  • 若有等待线程,唤醒一个线程。

2.5.3 示例代码

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

sem_t sem;

void *thread_function(void *arg) {
    printf("线程等待信号...\n");
    sem_wait(&sem); // P 操作
    printf("线程收到信号,继续执行...\n");
    return NULL;
}

int main() {
    if (sem_init(&sem, 0, 0) == -1) { // 初始化信号灯
        perror("sem_init failed");
        return 1;
    }

    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);

    sleep(1); // 主线程等待
    printf("主线程发送信号...\n");
    sem_post(&sem); // V 操作

    pthread_join(thread, NULL);
    sem_destroy(&sem);
    return 0;
}

2.6 System V 信号灯

2.6.1 创建/打开

int semget(key_t key, int nsems, int semflg);
2.6.1.1 参数说明
  • keyftok 生成的 key 值。

  • nsems:信号灯数目。

  • semflg:访问权限,常用 IPC_CREAT | 0666

2.6.1.2 返回值
  • 成功:返回信号灯集 ID。

  • 失败:返回 -1。

2.6.2 操作

int semop(int semid, struct sembuf *ops, size_t nops);
2.6.2.1 参数说明
  • semid:信号灯集 ID。

  • ops:操作数组。

  • nops:操作数目。

2.6.2.2 结构体 sembuf
struct sembuf {
    short sem_num; // 要操作的信号灯的编号
    short sem_op;  // 1:释放资源,V 操作;-1:分配资源,P 操作
    short sem_flg; // 0(阻塞), IPC_NOWAIT, SEM_UNDO
};

2.6.3 示例代码

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

int main() {
    key_t key = ftok("semaphore", 'a'); // 生成 key
    if (key == -1) {
        perror("ftok failed");
        return 1;
    }

    int semid = semget(key, 1, IPC_CREAT | 0666); // 创建信号灯
    if (semid == -1) {
        perror("semget failed");
        return 1;
    }

    struct sembuf sops;
    sops.sem_num = 0;
    sops.sem_op = -1; // P 操作
    sops.sem_flg = 0;

    if (semop(semid, &sops, 1) == -1) {
        perror("semop failed");
        return 1;
    }

    printf("信号灯 P 操作成功\n");

    sops.sem_op = 1; // V 操作
    if (semop(semid, &sops, 1) == -1) {
        perror("semop failed");
        return 1;
    }

    printf("信号灯 V 操作成功\n");

    // 删除信号灯
    if (semctl(semid, 0, IPC_RMID) == -1) {
        perror("semctl failed");
        return 1;
    }

    printf("信号灯已删除\n");
    return 0;
}


相关文章:

  • leetcode 2360. 图中的最长环 困难
  • 什么是动态代理?动态代理和静态代理的区别
  • 轮询、WebSocket 和 SSE:实时通信技术全面指南(含C#实现)
  • 从零开始打造HTML5拼图游戏:一个Canvas实战项目
  • hadoop集群配置-scp拓展使用
  • 基于WebSocket的金融数据实时推送系统架构设计对接多国金融数据API
  • SQL SELECT DISTINCT 语句详解:精准去重的艺术
  • Leetcode-100 二叉树引发的递归思考
  • SpringBoot整合Elasticsearch详细教程
  • [Html]overflow: auto 失效原因,flex 1却未设置min-height overflow的几个属性以及应用场景
  • 前沿技术有哪些改变生活新趋势
  • c#使用forms实现helloworld和login登录
  • java根据表达式获取对象中的值,设置值
  • UDP网络通信
  • PyTorch 深度学习实战(30):模型压缩与量化部署
  • 【doris】Apache Doris简介
  • 用 React + TypeScript + Antd 打造一个动态加载的树形穿梭选择组件
  • 深入理解指针(5)(C语言版)
  • Go 语言规范学习(7)
  • 使用FastAPI和google gemini打造一个多语言翻译网站
  • wordpress 代码 工具/东莞关键词seo优化
  • 岳各庄网站建设/优化大师免费下载
  • 福州网站建设流程/郑州纯手工seo
  • 徐汇网站推广/seo可以提升企业网站的
  • 广东省石油化工建设集团公司网站/网站seo外包公司
  • 网站运营与公司简介/做百度推广的网络公司广州