linux用函数输出进程信息,进程间通信之-消息队列(Message)--linux内核剖析(十一)...

消息队列

消息队列 (也叫做报文队列)是Unix系统V版本中3种进程间通信机制之一。另外两种是信号量和共享内存。

这些IPC机制使用共同的授权方法。只有通过系统调用将标志符传递给核心之后,进程才能存取这些资源。这种系统IPC对象使用的控制方法和文件系统非常类似。使用对象的引用标志符作为资源表中的索引。

Linux采用消息队列的方式来实现消息传递。这种消息的发送方式是

发送方不必等待接收方检查它所收到的消息就可以继续工作下去,

而接收方如果没有收到消息也不需等待。

这种通信机制相对简单,但是应用程序使用起来就需要使用相对复杂的方式来应付了。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。

IPC

进程间通信通过IPC对象,每个IPC对象都有唯一的ID号,通信双方需要获取该ID,创建者通过创建函数可以得到该值,可是另外的进程不能随意访问创建者的空间,于是约定利用相同的KEY值对于相同的ID,

IPC标识符

每一个I P C目标都有一个唯一的I P C标识符。这里所指的I P C目标是指一个单独的消息队列、一个信号量集或者一个共享的内存段。系统内核使用此标识符在系统内核中指明 I P C目标。

IPC 关键字

想要获得唯一的标识符,则必须使用一个 I P C关键字。客户端进程和服务器端进程必须双方都同意此关键字。这是建立一个客户机/服务器框架的第一步。在System V IPC机制中,建立两端联系的路由方法是和I P C关键字直接相关的。通过在应用程序中设置关键字值,每一次使用的关键字都可以是相同的。一般情况下,可以使用f t o k ( )函数为客户端和服务器端产生关键字值。

系统为每一个IPC对象保存一个ipc_perm结构体,该结构说明了IPC对象的权限和所有者,每一个版本的内核各有不用的ipc_perm结构成员。若要查看详细的定义请参阅文件

消息

通过消息队列将数据块从一个进程向另一个进程发送一个数据块。

消息实质上就是一些字或字节的序列(未必以空字符结尾)。它通过消息队列的方法在进程间传递。。

每个数据块或者序列都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

消息队列

消息队列是内核地址空间中的内部链表,通过linux内核在各个进程之间传递内容,消息顺序地发送到消息队列中,并且以几种不同的方式从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识,内核中的消息队列是通过IPC的标识符来区别的,不同的消息队列之间是相互独立的,每个消息队列中的消息又构成一个独立的链表.

消息队列是一个消息的链表。就是把消息看作一个记录,并且这个记录具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。

消息队列是随内核持续的并和进程相关,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构 (struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中中找到访问入口。

消息队列的数据结构

用户空间数据结构

ipc_perm内核数据结构

系统为每一个IPC对象保存一个ipc_perm结构体,该结构说明了IPC对象的权限和所有者,每一个版本的内核各有不用的ipc_perm结构成员。

系统使用ipc_perm 结构来保存每个IPC 对象权限信息

它定义在头文件linux/ipc.h中

/* Obsolete, used only for backwards compatibility and libc5 compiles */

struct ipc_perm

{

__kernel_key_t key;

__kernel_uid_t uid;

__kernel_gid_t gid;

__kernel_uid_t cuid;

__kernel_gid_t cgid;

__kernel_mode_t mode;

unsigned short seq;

};

结构里的前几个成员的含义是明显的,分别是IPC 对象的关键字,uid 和gid。

然后是IPC 对象的创建者的 uid 和gid。

接下来的是IPC 对象的存取权限。最后一个成员也许有点难于理解,不过不要担心,这是系统保存的IPC 对象的使用频率信息,我们完全可以不去理会它。

可以使用cat /usr/include/linux/ipc.h

A144738532-212249.png_small.png

结构体ipc_perm保存着消息队列的一些重要的信息,比如说消息队列关联的键值,消息队列的用户id组id等。

struct msqid_ds *msgque[MSGMNI]向量:

msgque[MSGMNI]是一个msqid_ds结构的指针数组,每个msqid_ds结构指针代表一个系统消息队列,msgque[MSGMNI]的大小为MSGMNI的值决定了系统中消息队列的数目

可以使用cat /proc/sys/kernel/msgmni来查看

A144740704-212249.png

struct ipc_ids msg_ids是内核中记录消息队列的全局数据结构,其具体结构如下

struct msqid_ds

Linux内核中,每个消息队列都维护一个结构体,此结构体保存着消息队列当前状态信息。

msgqid_ds 结构被系统内核用来保存消息队列对象有关数据。内核中存在的每个消息队列对象系统都保存一个msgqid_ds 结构的数据存放该对象的各种信息。

在Linux 的库文件linux/msg.h 中,它的定义是这样的:

struct msqid_ds

{

struct ipc_perm msg_perm;

struct msg *msg_first; /*消息队列头指针*/

struct msg *msg_last; /*消息队列尾指针*/

__kernel_time_t msg_stime; /*最后一次插入消息队列消息的时间*/

__kernel_time_t msg_rtime; /*最后一次接收消息即删除队列中一个消息的时间*/

__kernel_time_t msg_ctime;

struct wait_queue *wwait; /*发送消息等待进程队列*/

struct wait_queue *rwait;

unsigned short msg_cbytes;

unsigned short msg_qnum; /*消息队列中的消息个数*/

unsigned short msg_qbytes;

__kernel_ipc_pid_t msg_lspid; /*最后一次消息发送进程的pid*/

__kernel_ipc_pid_t msg_lrpid; /*最后一次消息发送进程的pid*/

};

msg_perm 成员保存了消息队列的存取权限以及其他一些信息(见上面关于ipc_perm结构的介绍)。

msg_first 成员指针保存了消息队列(链表)中第一个成员的地址。

msg_last 成员指针保存了消息队列中最后一个成员的地址。

msg_stime 成员保存了最近一次队列接受消息的时间。

msg_rtime 成员保存了最近一次从队列中取出消息的时间。

msg_ctime 成员保存了最近一次队列发生改动的时间。

wwait 和rwait 是指向系统内部等待队列的指针。

msg_cbytes 成员保存着队列总共占用内存的字节数。

msg_qnum 成员保存着队列里保存的消息数目。

msg_qbytes 成员保存着队列所占用内存的最大字节数。

msg_lspid 成员保存着最近一次向队列发送消息的进程的pid。

msg_lrpid 成员保存着最近一次从队列中取出消息的进程的pid。

可以使用cat /usr/include/linux/msg.h

A144742829-212249.png_small.png

struct msg 消息节点结构:

msqid_ds.msg_first,msg_last维护的链表队列中的一个链表节点

消息队列在系统内核中是以消息链表的形式出现的。而完成消息链表每个节点结构定义的就是msg 结构

struct msg

{

msg *msg_next; /*下一个msg*/

long msg_type; /*消息类型*/

*msg_spot; /*消息体开始位置指针*/

msg_ts; /*消息体长度*/

message; /*消息体*/

}

msg_next 成员是指向消息链表中下一个节点的指针,依靠它对整个消息链表进行访问。

msg_type 和msgbuf 中mtype 成员的意义是一样的。

msg_spot 成员指针指出了消息内容(就是msgbuf 结构中的mtext)在内存中的位置。

msg_ts 成员指出了消息内容的长度。

msgbuf消息内容结构:

msg 消息节点中的消息体,也是消息队列使用进程(消息队列发送接收进程)发送或者接收的消息

消息队列最大的灵活性在于,我们可以自己定义传递给队列的消息的数据类型的。

不过这个类型并不是随便定义的,msgbuf 结构给了我们一个这类数据类型的基本结构定义。

在Linux 的系统库linux/msg.h 中,它是这样定义的:

struct msgbuf

{

long mtype; --消息类型

char mtext[n]; --消息内容

}

同样定义在linux/msg.h中

A144745095-212249.png

它有两个成员:

mtype 是一个正的长整型量,通过它来区分不同的消息数据类型。

mtext 是消息数据的内容。

通过设定mtype 值,我们可以进行单个消息队列的多向通讯。如下图,client 可以给它向server 发送的信息赋于一个特定的mtype 值,而server 向client 的信息则用另一个mtype值来标志。

这样,通过mtype 值就可以区分这两向不同的数据。

利用相同的原理,可以实现更复杂的例子。

需要注意的是,虽然消息的内容mtext 在msgbuf 中只是一个字符数组,但事实上,在我们定义的结构中,和它对应的部分可以是任意的数据类型,甚至是多个数据类型的集合。

比如我们可以定义这样的一个消息类型:

struct my_msgbuf

{

long mtype; /* Message type */

long request_id; /* Request identifier */

struct client info; /* Client information structure */

};

在这里,与mtext 对应的是两个数据类型,其中一个还是struct 类型。由此可见消息队列在传送消息上的灵活性。

不过,虽然没有类型上的限制,但Linux 系统还是对消息类型的最大长度做出了限制。

在Linux 的库文件linux/msg.h 中定义了每个msgbuf 结构的最大长度:

#define MSGMAX8192 /* <= INT_MAX */ /* max size of message (bytes) */

也即是说,包括mtype 所占用的4 个字节,每个msgbuf 结构最多只能只能占用4056字节的空间

A144747345-212249.png_small.png

内核空间数据结构

在比较新的3.x到4.x的内核源代码中,

原来的消息队列结构的定义被安装到了/includeuapi/linux/msg.h

而内核空间结构定义在源码include/linux/msg.h

msg_msg

msg_msg,消息的基本数据结构

每个msg_msg将占据一个page的内容,其中一个page除了存储该结构体,空余的部分直接用来存储消息的内容。其中记录了一条消息的type和size,next指针用于指向msg_msgseg结构。

在消息队列的设计中,若消息内容大于一个page,则会使用一个类似链表的结构,但是后面的节点不需要再标记type和size等数据,后面的节点用msg_msgseg表示。

/* one msg_msg structure for each message */

struct msg_msg

{

struct list_head m_list;

long m_type;

int m_ts; /* message text size */

struct msg_msgseg* next;

void *security; /* the actual message follows immediately */

};

msg_msgseg

//msg_msgseg只需要存储指向下一链表块的指针就行了,每个msg_msgseg也将占据一个page的空间,其中一个page除了存储该结构体,剩下的部分都将用来存储message的数据。

struct msg_msgseg

{

struct msg_msgseg* next; /* the next part of the message follows immediately */

};

//msg_msg链表的结构,每个page下方空余的部分都将用来存massage的text。

struct msg_queue

消息队列的数据结构,其中包含message、receiver、sender队列的链表指针,同时还包含其他一些相关数据。

/* one msq_queue structure for each present queue on the system */

struct msg_queue

{

struct kern_ipc_perm q_perm;

time_t q_stime; /* last msgsnd time */

time_t q_rtime; /* last msgrcv time */

time_t q_ctime; /* last change time */

unsigned long q_cbytes; /* current number of bytes on queue */

unsigned long q_qnum; /* number of messages in queue */

unsigned long q_qbytes; /* max number of bytes on queue */

pid_t q_lspid; /* pid of last msgsnd */

pid_t q_lrpid; /* last receive pid */

struct list_head q_messages;

struct list_head q_receivers;

struct list_head q_senders;

};

函数接口

内核中实现消息传递机制的代码基本上都在文件ipc/msg.c中,消息队列的主要调用有下面4个,这里只作简单介绍:

msgget:调用者提供一个消息队列的键标 (用于表示个消息队列的唯一名字),当这个消息队列存在的时候, 这个消息调用负责返回这个队列的标识号;如果这个队列不存在,就创建一个消息队列,然后返回这个消息队列的标识号 ,主要由sys_msgget执行。

msgsnd:向一个消息队列发送一个消息,主要由sys_msgsnd执行。

msgrcv:从一个消息队列中收到一个消息,主要由sys_msgrcv执行。

msgctl:在消息队列上执行指定的操作。根据参数的不同和权限的不同,可以执行检索、删除等的操作,主要由sys_msgctl执行。

ftok文件名到键值

key_t ftok (char*pathname, char proj);

它返回与路径pathname相对应的一个键值。该函数不直接对消息队列操作,但在调用ipc(MSGGET,…)或msgget()来获得消息队列描述字前,往往要调用该函数。典型的调用代码是:

key=ftok(path_ptr, 'a');

ipc_id=ipc(MSGGET, (int)key, flags,0,NULL,0);

IPC同意用户接口

linux为操作系统V进程间通信的三种方式(消息队列、信号灯、共享内存区)提供了一个统一的用户界面:

int ipc(unsigned int call, int first, int second, int third, void * ptr, long fifth);

第一个参数指明对IPC对象的操作方式,对消息队列而言共有四种操作:MSGSND、MSGRCV、MSGGET以及MSGCTL,分别代表向消息队列发送消息、从消息队列读取消息、打开或创建消息队列、控制消息队列;first参数代表唯一的IPC对象;

下面将介绍四种操作。

int ipc( MSGGET, intfirst, intsecond, intthird, void*ptr, longfifth);

与该操作对应的系统V调用为:int msgget( (key_t)first,second)。

int ipc( MSGCTL, intfirst, intsecond, intthird, void*ptr, longfifth)

与该操作对应的系统V调用为:int msgctl( first,second, (struct msqid_ds*) ptr)。

int ipc( MSGSND, intfirst, intsecond, intthird, void*ptr, longfifth);

与该操作对应的系统V调用为:int msgsnd( first, (struct msgbuf*)ptr, second, third)。

int ipc( MSGRCV, intfirst, intsecond, intthird, void*ptr, longfifth);

与该操作对应的系统V调用为:int msgrcv( first,(struct msgbuf*)ptr, second, fifth,third),

注:本人不主张采用系统调用ipc(),而更倾向于采用系统V或者POSIX进程间通信API。

原因如下:

虽然该系统调用提供了统一的用户界面,但正是由于这个特性,它的参数几乎不能给出特定的实际意义(如以first、second来命名参数),在一定程度上造成开发不便。

正如ipc手册所说的:ipc()是linux所特有的,编写程序时应注意程序的移植性问题;

该系统调用的实现不过是把系统V IPC函数进行了封装,没有任何效率上的优势;

系统V在IPC方面的API数量不多,形式也较简洁。

创建新消息队列或取得已存在消息队列

int msgget(key_t key, int msgflg);

参数

描述

key

可以认为是一个端口号,也可以由函数ftok生成

msgflg

标识,可以取IPC_CREAT或者IPC_EXCL

调用返回:成功返回消息队列描述字,否则返回-1。

参数key是一个键值,由ftok获得;

msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。

在以下两种情况下,该调用将创建一个新的消息队列:

* 如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;

key参数为IPC_PRIVATE;

注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列。\

参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。

IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。

IPC_EXCL值,若没有该队列,则返回-1;若已存在,则返回0。

向队列读/写消息

msgrcv从队列中取用消息:

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

msgsnd将数据放到消息队列中:

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

参数

描述

msqid

消息队列的标识码

msgp

指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,

msgsz

消息的大小

msgtyp

从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。否则只接收相同类型的消息

msgflg

用来指明核心程序在队列没有数据的情况下所应采取的行动。

其中msgp是用户通用的结构,可以使用如下形式

struct msgstru{

long mtype; //大于0

char mtext[512];

};

如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。

读操作msgrcv

int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);

调用返回:成功返回读出消息的实际字节数,否则返回-1。

该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。

msqid为消息队列描述字;消息返回后存储在msgp指向的地址,

msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),

msgtyp为请求读取的消息类型;

读消息标志msgflg可以为以下几个常值的或:

IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG

IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息

IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的

msgsz字节,则把该消息截断,截断部分将丢失。

msgrcv手册中详细给出了消息类型取不同值时(>0; <0; =0),调用将返回消息队列中的哪个消息。

msgrcv()解除阻塞的条件有三个:

消息队列中有了满足条件的消息;

msqid代表的消息队列被删除;

调用msgrcv()的进程被信号中断;

写操作msgsnd

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);

调用返回:成功返回0,否则返回-1。

向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。

对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。

造成msgsnd()等待的条件有两种:

当前消息的大小与当前消息队列中的字节数之和超过了消息队列

的总容量;

当前消息队列的消息数(单位”个”)不小于消息队列的总容量(单位”字节数”),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。

msgsnd()解除阻塞的条件有三个:

不满足上述两个条件,即消息队列中有容纳该消息的空间;

msqid代表的消息队列被删除;

调用msgsnd()的进程被信号中断;

设置消息队列属性

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

调用返回:成功返回0,否则返回-1。

IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,返回的信息存贮在buf指向的msqid结构中;

IPC_SET : 该命令用来设置消息队列的属性,设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。

IPC_RMID : 从内核中删除 msqid 标识的消息队列。

消息队列的限制

每个消息队列的容量(所能容纳的字节数)都有限制,该值因系统不同而不同。在后面的应用实例中,输出了redhat 8.0的限制,结果参见 附录 3。

另一个限制是每个消息队列所能容纳的最大消息数:在redhad 8.0中,该限制是受消息队列容量制约的:消息个数要小于消息队列的容量(字节数)。

注:上述两个限制是针对每个消息队列而言的,系统对消息队列的限制还有系统范围内的最大消息队列个数,以及整个系统范围内的最大消息数。一般来说,实际开发过程中不会超过这个限制。

IPCS和IPCRM命令

IPCS 命令

命令ipcs用于读取System V IPC目标的状态。

ipcs -q: 只显示消息队列。

ipcs -s: 只显示信号量。

ipcs -m: 只显示共享内存。

ipcs –help: 其他的参数。

A144749454-212249.png_small.png

IPCRM命令

命令IPCRM用来删除System V IPC目标的状态。

移除一个消息对象。或者共享内存段,或者一个信号集,同时会将与ipc对象相关链的数据也一起移除。当然,只有超级管理员,或者ipc对象的创建者才有这项权利

ipcrm用法

ipcrm -M shmkey 移除用shmkey创建的共享内存段

ipcrm -m shmid 移除用shmid标识的共享内存段

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

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

ipcrm -S semkey 移除用semkey创建的信号

ipcrm -s semid 移除用semid标识的信号

A144751689-212249.png_small.png

示例

所以我们在这里将会编写两个程序,msg_receive和msg_send来表示接收和发送信息。

根据正常的情况,我们允许两个程序都可以创建消息,但只有接收者在接收完最后一个消息之后,它才把它删除。

发送端msg_send

#include

#include

#include

#include

#include

#include

#define MSG_KEY 1024

#define MAX_TEXT 512

struct msg_st

{

long int msg_type;

char text[MAX_TEXT];

};

int main()

{

int running = 1;

struct msg_st data;

char buffer[BUFSIZ];

int msgid = -1;

//建立消息队列

msgid = msgget(MSG_KEY, IPC_EXCL); /*检查消息队列是否存在*/

if(msgid < 0)

{

msgid = msgget((key_t)MSG_KEY, IPC_CREAT|0666);/*创建消息队列*/

if(msgid < 0)

{

printf("failed to create msq, errno=%d : %s\n",errno,strerror(errno));

exit(-1);

}

}

//向消息队列中写消息,直到写入end

while(running)

{

//输入数据

printf("Enter some text: ");

fgets(buffer, BUFSIZ, stdin);

data.msg_type = 1; //注意2

strcpy(data.text, buffer);

//向队列发送数据

if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)

{

fprintf(stderr, "msgsnd failed\n");

exit(EXIT_FAILURE);

}

//输入end结束输入

if(strncmp(buffer, "end", 3) == 0)

running = 0;

sleep(1);

}

return EXIT_SUCCESS;

}

接收端msg_receive

#include

#include

#include

#include

#include

#include

#define MSG_KEY 1024

struct msg_st

{

long int msg_type;

char text[BUFSIZ];

};

int main()

{

int running = 1;

int msgid = -1;

struct msg_st data;

long int msgtype = 0; //注意1

//建立消息队列

//建立消息队列

msgid=msgget(MSG_KEY, IPC_EXCL); /*检查消息队列是否存在*/

if(msgid < 0)

{

msgid = msgget(MSG_KEY, IPC_CREAT|0666);/*创建消息队列*/

if(msgid < 0)

{

printf("failed to create msq, errno=%d : %s\n",errno,strerror(errno));

exit(-1);

}

msgid = msgget((key_t)MSG_KEY, 0666 | IPC_CREAT);

if(msgid == -1)

{

fprintf(stderr, "msgget failed with error: %d\n", errno);

exit(EXIT_FAILURE);

}

}

//从队列中获取消息,直到遇到end消息为止

while(running)

{

if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)

{

fprintf(stderr, "msgrcv failed with errno: %d\n", errno);

exit(EXIT_FAILURE);

}

printf("You wrote: %s\n", data.text);

//遇到end结束

if(strncmp(data.text, "end", 3) == 0)

{

running = 0;

}

}

//删除消息队列

if(msgctl(msgid, IPC_RMID, 0) == -1)

{

fprintf(stderr, "msgctl(IPC_RMID) failed\n");

exit(EXIT_FAILURE);

}

return EXIT_SUCCESS;

}

运行结果

A144754032-212249.png_small.png

这里主要说明一下消息类型是怎么一回事

注意msg_receive.c文件main函数中定义的变量msgtype(注释为注意1),它作为msgrcv函数的接收信息类型参数的值,其值为0,表示获取队列中第一个可用的消息。

再来看看msg_send.c文件中while循环中的语句data.msg_type = 1(注释为注意2),它用来设置发送的信息的信息类型,即其发送的信息的类型为1。所以程序msg_receive能够接收到程序msg_send发送的信息。

msgtype如果值为零,则表示消息队列中的所有消息都会被读取。否则只接收相同类型的消息

如果把注意1,即msg_receive.c文件main函数中的语句由long int msgtype = 0;改变为long int msgtype = 2;会发生什么情况,msgreceive将不能接收到程序msgsend发送的信息。因为在调用msgrcv函数时,如果msgtype(第四个参数)大于零,则将只获取具有相同消息类型的第一个消息,修改后获取的消息类型为2,而msgsend发送的消息类型为1,所以不能被msgreceive程序接收。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值