IPC_NOWAIT ——没有可以接收的消息时,立即返回-1 MSG_EXCEPT ——返回第一个类型不为msgtyp的消息
MSG_NOERROR——消息正文长度超过msgsz字节时,将直接截去其
中多余的部分
③功能: 如果传递给参数msgflg的值为IPC_NOWAIT,并且没有可取的消息,
那么给调用进程返回ENOMSG错误码,否则,调用进程阻塞,直到一条满足要求的消息到达消息队列。如果进程正在等待消息,而相应的消息队列被删除,则返回EIDRM。如果当进程正在等待消息时,捕获到了一个信号,则返回EINTR
④返回值: 接收成功,返回实际接收到的消息正文的字节数;否则返回-1,同
时error中存有错误代码
⑤错误代码:E2BIG ——消息长度超过msgsz,且MSG_NOERROR标志没被使用
EACCESS——无权限读取消息队列
EFAULT ——参数msgp指向的地址无法访问
EIDRM ——标识符为msqid的消息队列已被删除 EINTR ——等待消息的情况下,被信号唤醒
EINVAL ——无效的参数msqid、或msgsz为负数 ENOMSG ——参数msgflg设为IPC_NOWAIT,但无满足要求的消息
可接收
(4)msgctl系统调用
①函数原型:
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
②参数:
msqid 消息队列的标识数 cmd IPC_STAT ——将对应消息队列结构体的值复制到一份到buf所
指的结构体中,调用者必须有读消息队列的权限
IPC_SET —— 将buf所指结构体中的部分信息:
msg_perm.uid,msg_perm.gid,mst_perm.mode,msg_qbytes写到消息队列结构体中,并且更新消息队列结构体msg_ctime成员的值。调用者必须有相应的权限。
IPC_RMD —— 删除消息队列,并唤醒该消息队列上等待读或等待写
的进程。调用者必须有相应的权限。
③功能: 获取或设置消息队列的属性信息,或者删除消息队列 ④返回值: 成功,返回0;否则返回-1,同时error中存有错误代码
⑤错误代码:EACCESS—— cmd为IPC_STAT,但调用进程无读消息队列的权限
EFAULT —— 参数cmd为IPC_STAT或IPC_SET ,但buf指向的
地址无法访问
EIDRM ——标识符为msqid的消息队列已被删除 EINTR ——等待消息的情况下,被信号唤醒 EINVAL ——cmd或msqid为无效的参数
EPERM —— 参数cmd为IPC_RMD或IPC_SET ,但调用进程无足
够的权限
3.实例 //sender.c
#include
#define MSG_FILE \#define BUFFER 255
#define PERM S_IRUSR|S_IWUSR struct msgbuf {
long mtype;
char mtext[BUFFER+1]; };
char *message[3]= {\ \ \ }; int main() {
struct msgbuf msg; key_t key; int msgid; int i;
if((key=ftok(MSG_FILE,66))==-1) {
fprintf(stderr,\:%s \\n\ exit(EXIT_FAILURE); }
if((msgid=msgget(key,PERM|IPC_CREAT))==-1) {
fprintf(stderr,\:%s \\n\ exit(EXIT_FAILURE); }
msg.mtype=1;
for(i=0; i<3; i++) {
strncpy(msg.mtext,message[i],BUFFER);
msgsnd(msgid,&msg,sizeof(struct msgbuf),0); }
memset(&msg,'\\0',sizeof(struct msgbuf));
msgrcv(msgid,&msg,sizeof(struct msgbuf),2,0); printf(\ if(msgctl(msgid, IPC_RMID, 0) == -1) {
fprintf(stderr, \ exit(EXIT_FAILURE); }
exit(EXIT_SUCCESS); }
//receiver.c
#include
#define MSG_FILE \#define BUFFER 255
#define PERM S_IRUSR|S_IWUSR struct msgbuf {
long mtype;
char mtext[BUFFER+1]; };
int main() {
struct msgbuf msg; key_t key; int msgid; int i;
char *myask=\ if((key=ftok(MSG_FILE,66))==-1) {
fprintf(stderr,\:%s \\n\ exit(EXIT_FAILURE); }
if((msgid=msgget(key,PERM|IPC_CREAT))==-1) {
fprintf(stderr,\:%s \\n\ exit(EXIT_FAILURE); }
for(i=0; i<3; i++) {
msgrcv(msgid,&msg,sizeof(struct msgbuf),1,0); printf(\ }
msg.mtype=2;
strncpy(msg.mtext,myask,BUFFER);
msgsnd(msgid,&msg,sizeof(struct msgbuf),0);
exit(EXIT_SUCCESS); }
程序的运行结果如下:
上述程序运行时,第三行命令名后的添加的‘&’,可以使命令“./receiver”在后台运行,shell会立即显示命令提示符,等待用户输入下一条命令,再输入“./sender”便可让上述两个程序同时运行。请思考:
(1) 如果不使用后台进程,那应如何运行上述程序,达到进程通信的目的?
(2) 发送给sender的消息、和发送给receiver的消息是存放在相同的消息队列
中,还是存放在各自的消息队列中?
(3) Sender、receiver获取/创建消息队列时使用的键值是否必须相同,如果键值
不同会发生什么情况?
(4) 程序中函数调用msgctl(msgid, IPC_RMID, 0)的目的是什么?如果没有执行该
函数调用,那程序多次运行会不会创建多个消息队列?
<四>编程实现下述功能
1.由父进程创建一个管道,然后再创建2个子进程,并由这两个兄弟进程利用管道进行进程通信:子进程1使用管道的写端,子进程2使用管道的读端。通信的具体内容可根据自己的需要随意设计,要求能试验阻塞型读写过程中的各种情况。运行程序,观察各种情况下,进程实际读写的字节数以及进程阻塞唤醒的情况。
2.编写程序sender,它创建一个消息队列;然后,循环等待用户通过终端输入一串字符,将这串字符通过消息队列发送给receiver,直到用户输入“exit”为止;最后,它向receiver进程发送消息“end”,并且等待receiver的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,删除消息队列,结束程序的运行。编写receiver程序,它通过消息队列接收来自sender的消息,将消息显示在终端屏幕上,直至收到内容为“end”的消息为止,此时,它向sender发送一个应答消息“over”,结束程序的运行。
<五> ipcs和ipcrm命令
通过man命令查阅ipcs和ipcrm命令的格式和使用方式,查看目前系统中是否存在消息队列,如果没有,想办法创建若干消息队列,再次使用ipcs查看它们的情况,最后,请将所有的消息队列全部删除。