(三)进程通信(二)-System V消息队列

目录

25System V消息队列(一)

消息队列

IPC对象数据结构

消息队列结构

消息队列在内核中的表示

消息队列函数

msgsnd函数

msgrcv函数

26System V消息队列(二)

msgsnd函数

msgrcv函数

27System V消息队列(三)

消息队列实现回射客户/服务器


25System V消息队列(一)

消息队列

  • 1、消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
  • 2、每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
  • 3、消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。
  • 4、消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的
     
#(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI),这三个参数都可以查看:

gsx@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ cat /proc/sys/kernel/msgmax
8192
gsx@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ cat /proc/sys/kernel/msgmnb
16384
gsx@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ cat /proc/sys/kernel/msgmni
1711


IPC对象数据结构

内核为每个IPC对象维护一个数据结构

struct ipc_perm {
   key_t          __key;       /* Key supplied to xxxget(2) */
   uid_t          uid;         /* Effective UID of owner */
   gid_t          gid;         /* Effective GID of owner */
   uid_t          cuid;        /* Effective UID of creator */
   gid_t          cgid;        /* Effective GID of creator */
   unsigned short mode;        /* Permissions */
   unsigned short __seq;       /* Sequence number */
};

消息队列,共享内存和信号量都有这样一个共同的数据结构。


消息队列结构

struct msqid_ds {
   struct ipc_perm msg_perm;     /* Ownership and permissions */
   time_t     msg_stime;    /* Time of last msgsnd(2) */
   time_t     msg_rtime;    /* Time of last msgrcv(2) */
   time_t     msg_ctime;    /* Time of last change */
   unsigned long    __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */
   msgqnum_t     msg_qnum;     /* Current number of messages in queue */
   msglen_t     msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
   pid_t                  msg_lspid;      /* PID of last msgsnd(2) */
   pid_t                  msg_lrpid;      /* PID of last msgrcv(2) */
};


可以看到第一个条目就是IPC结构体,即是共有的,后面的都是消息队列所私有的成员。


消息队列在内核中的表示

消息队列是用链表实现的,这里需要提出的是MSGMAX指的是一条消息的纯数据大小的上限,上图是一个消息队列,则其纯数据总和不能超过MSGMNB,像这样一条消息队列,系统含有的总数不能超过MSGMNI 个。 

消息队列函数

msgsnd函数

函数原型

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

函数功能

//用来创建和访问一个消息队列

返回值

成功返回一个非负整数,即该消息队列的标识码;失败返回-1

参数说明

//key: 某个消息队列的名字
//msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

 

创建流程如下图所示:

注意,IPC_PRIVATE 不是一个msgflg 而是一个key_t 类型,如果指定key = IPC_PRIVATE,则无论某个 key_value 这个消息队列是否存在,都会再创建一个key_value 消息队列,但他们的标识码msqid是不一样的,且指定IPC_PRIVATE 产生的是私有的消息队列。


msgrcv函数

函数原型

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

函数功能

消息队列的控制函数

返回值

成功返回0,失败返回-1

参数说明

msqid: 由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)

cmd 的取值如下:


26System V消息队列(二)

 

msgsnd函数

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

函数功能

把一条消息添加到消息队列中

返回值

成功返回0;失败返回-1

参数说明

msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息结构体
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情

 

msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。为0表示阻塞等待
消息结构在两方面受到制约。

  • 首先,它的具体数据必须小于系统规定的上限值MSGMAX;
  • 其次,它必须以一个long int长整数开始,接收者函数将利用这个长整数确定消息的类型。

消息结构参考形式如下:

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

即mtex 这块区域可以是个数组或者结构体,大小由参数msgsz 指明。


msgrcv函数

函数原型

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

函数功能

是从一个消息队列接收消息

返回值

成功返回实际放到接收缓冲区里去的字符个数,失败返回-1

参数说明

/*
msgid:  由msgget函数返回的消息队列标识码
msgp:   是一个指针,指针指向准备接收的消息结构体
msgsz:  是msgp指向的最大消息长度,这个长度不含保存消息类型的那个long int长整型
msgtype:它可以实现接收优先级的简单形式
msgflg: 控制着队列中没有相应类型的消息可供接收时将要发生的事
*/
/*
msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息 
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。
*/

 

 

27System V消息队列(三)

消息队列实现回射客户/服务器

对于服务器端来说,接收到一个消息结构体的类型如果为1,表示是客户请求,而mtex 字段的前4个字节存放着不同进程的pid ,后续字节才是真正的数据,服务器回射客户端时,将pid 作为类型,mtex 为实际数据,客户端只接收对应类型的数据,故可以区分不同客户端。 

echoser.c

#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define MSGMAX 8192
struct msgbuf
{
    long mtype;
    char mtext[MSGMAX];
};


void echo_ser(int msgid)
{
    struct msgbuf msg;
    memset(&msg, 0, sizeof(msg));
    int nrcv = 0;
    while (1)
    {

        if ((nrcv = msgrcv(msgid, &msg, MSGMAX, 1, 0)) < 0);
        int pid = *((int *)msg.mtext);
        fputs(msg.mtext + 4, stdout);
        msg.mtype = pid;
        msgsnd(msgid, &msg, nrcv, 0);
        memset(&msg, 0, sizeof(msg));

    }
}

int main(int argc, char *argv[])
{
    int msgid;
    msgid = msgget(1234, IPC_CREAT | 0666);
    if (msgid == -1)
        ERR_EXIT("msgget");

    echo_ser(msgid);


    return 0;
}

echocli.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define MSGMAX 8192

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

void echo_cli(int msgid)
{
    int nrcv;
    int pid = getpid();
    struct msgbuf msg;
    memset(&msg, 0, sizeof(msg));
    msg.mtype = 1;
    *((int *)msg.mtext) = pid;
    while (fgets(msg.mtext + 4, MSGMAX, stdin) != NULL)
    {

        if (msgsnd(msgid, &msg, 4 + strlen(msg.mtext + 4), IPC_NOWAIT) < 0)
            ERR_EXIT("msgsnd");

        memset(msg.mtext + 4, 0, MSGMAX - 4);
        if ((nrcv = msgrcv(msgid, &msg, MSGMAX, pid, 0)) < 0)
            ERR_EXIT("msgsnd");
        fputs(msg.mtext + 4, stdout);
        memset(msg.mtext + 4, 0, MSGMAX - 4);

    }
}

int main(int argc, char *argv[])
{

    int msgid;
    msgid = msgget(1234, 0);
    if (msgid == -1)
        ERR_EXIT("msgget");

    echo_cli(msgid);

    return 0;
}

但上述程序是存在死锁的风险的,当开了多个客户端,将队列写满了,此时服务器端想要写入就会阻塞,而因为客户端一旦发送了数据就阻塞等待服务器端回射类型为pid的消息,即队列的消息不会减少,此时就会形成死锁,即使服务器端是非阻塞地写入,此时会返回EAGAIN 的错误,程序逻辑来说我们也会使其不断地尝试去写入,而不是粗暴地将其退出进程,这样还是会死锁。

对此问题可以多开几个私有的队列进行服务,如下:

即某个客户端先创建一个私有消息队列,然后将私有消息队列标识符和具体数据发到共享的队列,服务器fork 出一个子进程,此时根据私有队列标识符就可以将数据回射到这个队列,这个客户端就可以从私有队列读取到回射的数据。 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
蛋白质是生物体中普遍存在的一类重要生物大分子,由天然氨基酸通过肽键连接而成。它具有复杂的分子结构和特定的生物功能,是表达生物遗传性状的一类主要物质。 蛋白质的结构可分为四级:一级结构是组成蛋白质多肽链的线性氨基酸序列;级结构是依靠不同氨基酸之间的C=O和N-H基团间的氢键形成的稳定结构,主要为α螺旋和β折叠;级结构是通过多个级结构元素在维空间的排列所形成的一个蛋白质分子的维结构;四级结构用于描述由不同多肽链(亚基)间相互作用形成具有功能的蛋白质复合物分子。 蛋白质在生物体内具有多种功能,包括提供能量、维持电解质平衡、信息交流、构成人的身体以及免疫等。例如,蛋白质分解可以为人体提供能量,每克蛋白质能产生4千卡的热能;血液里的蛋白质能帮助维持体内的酸碱平衡和血液的渗透压;蛋白质是组成人体器官组织的重要物质,可以修复受损的器官功能,以及维持细胞的生长和更新;蛋白质也是构成多种生理活性的物质,如免疫球蛋白,具有维持机体正常免疫功能的作用。 蛋白质的合成是指生物按照从脱氧核糖核酸(DNA)转录得到的信使核糖核酸(mRNA)上的遗传信息合成蛋白质的过程。这个过程包括氨基酸的活化、多肽链合成的起始、肽链的延长、肽链的终止和释放以及蛋白质合成后的加工修饰等步骤。 蛋白质降解是指食物中的蛋白质经过蛋白质降解酶的作用降解为多肽和氨基酸然后被人体吸收的过程。这个过程在细胞的生理活动中发挥着极其重要的作用,例如将蛋白质降解后成为小分子的氨基酸,并被循环利用;处理错误折叠的蛋白质以及多余组分,使之降解,以防机体产生错误应答。 总的来说,蛋白质是生物体内不可或缺的一类重要物质,对于维持生物体的正常生理功能具有至关重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值