嵌入式Linux C进程间通信(三)——消息队列

简介: 嵌入式Linux C进程间通信(三)——消息队列

前述


System V IPC

0a2653c851af460fa595bd959398a8f1.png

特点:

与管道有所不同,它完全使用了不同的实现机制,与文件没任何的关系,也就是说内核不再以文件的形式来管理System V IPC

System V IPC不再以文件的形式存在,因此没有文件描述符这个东西,但是它有类似的“标识符”

任何进程之间通信时,都可以使用System V IPC来通信

POSIX也可以支持消息队列、共享内存、信号量能在unix下运行


一、消息队列的创建和删除


消息队列的本质就是由内核创建的用于存放消息的链表,由于是存放消息的,所以我们就把这个链表称为消息队列。


1.1 消息的组成


1.消息编号:识别信息用

2.消息正文:真正的信息内容

0eacb84100b54626af849e6b562bf92a.png


1.2 创建


int msgget(key_t key, int msgflg);


0a2653c851af460fa595bd959398a8f1.png


使用ftok生成key值


0eacb84100b54626af849e6b562bf92a.png2d65d23f6d4748949b924e4057485923.png


1.2 查看系统的消息队列


0a2653c851af460fa595bd959398a8f1.png0eacb84100b54626af849e6b562bf92a.png


1.3 消息队列的创建


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <errno.h>
#define MSGNAME "./msg_file"
int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;
    key = ftok(MSGNAME, 'k');
    msgid = msgget(key, IPC_CREAT | 0655);
    if (msgid < 0)
    {
        perror("msgid error!");
        exit(1);
    }
    printf("msgid = %d\n",msgid);
    return 0;
}


1.4 获取属性及删除


进程结束时,system v ipc不会自动删除,进程结束后,使用ipcs依然能够查看到


长方形

重启OS

使用ipcrm命令删除

int msgctl 参数:int mspid,int cmd,struct mspid_ds *buf

ipcrm -Q msgkey移除用msqkey创建的消息队列

ipcrm -q msqid移除用msqid标识的消息队列

0a2653c851af460fa595bd959398a8f1.png

msgctl


0a2653c851af460fa595bd959398a8f1.png0eacb84100b54626af849e6b562bf92a.png2d65d23f6d4748949b924e4057485923.png2e9b90b2ca334476abebe75bafe6eeaa.png


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <errno.h>
#define MSGNAME "./msg_file"
int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;
    key = ftok(MSGNAME, 'k');
    msgid = msgget(key, IPC_CREAT | 0655);
    if (msgid < 0)
    {
        perror("msgid error!");
        exit(1);
    }
    printf("key = %x\n", key);
    printf("msgid = %d\n", msgid);
    msgctl(msgid,IPC_RMID,NULL);
    return 0;
}

0a2653c851af460fa595bd959398a8f1.png


二、消息队列的发送和接收


2.1 发送


int msgsnd(int msqid,const void *msgp, size_t msgsz,int msgflg);

0eacb84100b54626af849e6b562bf92a.png


2.2 接收


ssize_ t msgrcv(int msqid, void *msgp, size_ t msgsz, long msgtyp, int msgflg);

2d65d23f6d4748949b924e4057485923.png


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#define MSGNAME "./msg_file"
int msgid;
struct msgbuf
{
    long mtype;
    char mtext[1024];
};
void my_exit(int msg)
{
    msgctl(msgid,IPC_RMID,NULL);
}
int main(int argc, char const *argv[])
{
    key_t key;
    pid_t pid;
    signal(SIGINT, my_exit);
    key = ftok(MSGNAME, 'k');
    msgid = msgget(key, IPC_CREAT | 0655);
    if (msgid < 0)
    {
        perror("msgid error!");
        exit(1);
    }
    printf("key = %x\n", key);
    printf("msgid = %d\n", msgid);
    pid = fork();
    if (pid < 0)
    {
        perror("fork perror!\n");
        exit(1);
    }
    if (pid == 0)
    {
        long mtype;
        char buffer[1024];
        struct msgbuf pmsgbuf;
        while (1)
        {
            memset(buffer, 0, sizeof(buffer));
            memset(&pmsgbuf, 0, sizeof(struct msgbuf));
            printf("Please input send msg:\n");
            scanf("%s", buffer);
            strcpy(pmsgbuf.mtext, buffer);
            printf("Please input send msg id:\n");
            scanf("%ld", &mtype);
            pmsgbuf.mtype = mtype;
            if(msgsnd(msgid,(void *)&pmsgbuf,sizeof(pmsgbuf),IPC_NOWAIT) < 0)
            {
                perror("msg snd error!");
                exit(1);
            }
        }
    }
    else if(pid > 0)
    {
        size_t size;
        long mtype;
        char buffer[1024];
        struct msgbuf pmsgbuf;
        while (1)
        {
            memset(buffer, 0, sizeof(buffer));
            memset(&pmsgbuf, 0, sizeof(struct msgbuf));
            size = msgrcv(msgid, &pmsgbuf,sizeof(pmsgbuf),3,0);
            if (size < 0)
            {
                perror("msg rcv error!");
                exit(1);
            }
            printf("recv data:%s\n", pmsgbuf.mtext);
            sleep(1);
        }
    }
    return 0;
}


三、任意进程通信及特点


多个进程是如何共享到同一个消息队列?


1.fork前创建

2.创建者把ID保存到某文件,共享进程读出ID即可+

3.使用ftok函数,利用与创建者相同的“路径名”和8位整形数,生成相同的key值

消息队列的特点


1.传送有格式的消息流

2.多进程网状旁叉通信时,消息队列是上上之选

3.能实现大规模数据的通信


3.1 msg_read.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#define MSGNAME "./msg_file"
struct msgbuf
{
    long mtype;
    char mtext[1024];
};
int main(int argc, char const *argv[])
{
    key_t key;
    key = ftok(MSGNAME, 'k');
    int msgid = msgget(key, IPC_CREAT | 0777);
    if (msgid < 0)
    {
        perror("msgid error!");
        exit(1);
    }
    printf("key = %x\n", key);
    printf("msgid = %d\n", msgid);
    size_t size;
    long mtype;
    char buffer[1024];
    struct msgbuf pmsgbuf;
    while (1)
    {
        memset(buffer, 0, sizeof(buffer));
        memset(&pmsgbuf, 0, sizeof(struct msgbuf));
        size = msgrcv(msgid, &pmsgbuf,sizeof(pmsgbuf),3,0);
        if (size < 0)
        {
            perror("msg rcv error!");
            exit(1);
        }
        printf("recv data:%s\n", pmsgbuf.mtext);
        sleep(1);
    }
}


3.2 msg_write.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#define MSGNAME "./msg_file"
struct msgbuf
{
    long mtype;
    char mtext[1024];
};
int main(int argc, char const *argv[])
{
    key_t key;
    key = ftok(MSGNAME, 'k');
    int msgid = msgget(key, 0777 | IPC_CREAT);
    if (msgid < 0)
    {
        perror("msgid error!");
        exit(1);
    }
    printf("key = %x\n", key);
    printf("msgid = %d\n", msgid);
    size_t size;
    long mtype;
    char buffer[1024];
    struct msgbuf pmsgbuf;
    while (1)
    {
        memset(buffer, 0, sizeof(buffer));
        memset(&pmsgbuf, 0, sizeof(struct msgbuf));
        printf("Please input send msg:\n");
        scanf("%s", buffer);
        strcpy(pmsgbuf.mtext, buffer);
        printf("Please input send msg id:\n");
        scanf("%ld", &mtype);
        pmsgbuf.mtype = mtype;
        if(msgsnd(msgid,(void *)&pmsgbuf,sizeof(pmsgbuf),IPC_NOWAIT) < 0)
        {
            perror("msg snd error!");
            exit(1);
        }
    }
}


3.3 成果展示


2019082413041482.gif

相关文章
|
11月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
377 67
|
10月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
284 16
|
10月前
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
203 20
|
9月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
215 0
|
9月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
285 0
|
9月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
170 0
|
9月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
231 0
|
12月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
1154 5
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能