LINUX 进程间通信程序设计-2

来源:互联网 发布:面纱3.0做丝数据 八戒 编辑:程序博客网 时间:2024/05/04 10:18

4、消息队列

        消息队列就是一个消息的链表。可以把消息看作一个记录(具有特定的格式)。进程可以向消息队列按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息。

分类:

目前主要有两种类型的消息队列:

1)POSIX消息队列

2)系统V消息队列,系统V消息队列目前被大量使用。


持续性:
系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。

1)键值:

消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,必须提供该消息队列的键值。


#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char*pathname, char proj)
功能:
返回文件名对应的键值。
pathname:文件名,一般使用当前目录,如:

key_t key;
key = ftok(".", 1); 这样就是将pathname设为当前目录。

proj:项目名(不为0即可),是子序号

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

如指定文件的索引节点号为65538,换算成16进制为0x10002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x2610002。

查询文件索引节点号的方法是: ls -i当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。

如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值,比如:

#define IPCKEY 0x111char path[256];sprintf( path, "%s/etc/config.ini", (char*)getenv("HOME") );msgid=ftok( path, IPCKEY );

同一段程序,用于保证两个不同用户下的两组相同程序获得互不干扰的IPC键值。由于etc/config.ini(假定)为应用系统的关键配置文件,因此不存在被轻易删除的问题——即使被删,也会很快被发现并重建(此时应用系统也将被重启)。ftok()的设计目的也在于此。

2)打开/创建:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
key:键值,由ftok获得。
msgflg:标志位。
返回值:与健值key相对应的消息队列描述字。

msgget功能描述:

获取与某个键关联的消息队列标识。消息队列被建立的情况有两种:
1.如果键的值是IPC_PRIVATE。
2.键的值不是IPC_PRIVATE,并且键所对应的消息队列不存在,同时标志中指定IPC_CREAT。
如果新的消息队列被建立,它关联的msqid_ds数据结构初始化如下:
msg_perm.cuid 和 msg_perm.uid设为调用者的有效用户标志;
msg_perm.cgid 和 msg_perm.gid设为调用者的有效用户组标志;
msg_perm.mode最低9个位设为msgflg的最低9个位;
msg_qnum,msg_lspid,msg_lrpid,msg_stime和msg_rtime设为0;
msg_ctime设为当前时间;
msg_qbytes设为系统限制MSGMNB。


msgget()详细描述:

1)是消息队列对象的关键字(key),函数将它与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建。

2)函数进行的具体操作是由第二个参数,msgflg 控制的。它可以取下面的几个值:

  IPC_CREAT :
  如果消息队列对象不存在,则创建之,否则则进行打开操作;
  IPC_EXCL:
  和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建之,否则产生一个错误并返回。
  如果单独使用IPC_CREAT 标志,msgget()函数要么返回一个已经存在的消息队列对象的标识符,要么返回一个新建立的消息队列对象的标识符。

        如果将IPC_CREAT 和IPC_EXCL标志一起使用,msgget()将返回一个新建的消息对象的标识符,或者返回-1 

        如果消息队列对象已存在。IPC_EXCL 标志本身并没有太大的意义,但和IPC_CREAT 标志一起使用可以用来保证所得的消息队列对象是新创建的而不是打开的已有的对象。
  除了以上的两个标志以外,在msgflg 标志中还可以有存取权限控制符。这种控制符的意义和文件系统中的权限控制符是类似的。

参数:
key:消息队列关联的键。
msgflg:消息队列的建立标志和存取权限。
IPC_CREAT如果内核中没有此队列,则创建它。
IPC_EXCL当和IPC_CREAT一起使用时,如果队列已经存在,则失败。
如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。
返回说明:
成功执行时,返回消息队列标识值。失败返回-1,errno被设为以下的某个值 ,有时也会返回0,这个时候也是可以正常使用的
EACCES:指定的消息队列已存在,但调用进程没有权限访问它,而且不拥有CAP_IPC_OWNER权能
EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
ENOENT:key指定的消息队列不存在同时msgflg中不指定IPC_CREAT标志
ENOMEM:需要建立消息队列,但内存不足
ENOSPC:需要建立消息队列,但已达到系统的最大消息队列容量

int open_queue(key_t keyval){    int qid;    if((qid=msgget(keyval,IPC_CREAT))==-1)    {        return(-1);    }    return(qid);}

3)发送消息

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg)
功能:向消息队列中发送一条消息。

参数:

msqid
已打开的消息队列id
msgp
存放消息的结构
msgsz
消息数据长度
msgflg
发送标志,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。

消息格式:

struct msgbuf
{
     long mtype;/* 消息类型 > 0 */
     char mtext[1]; /* 消息数据的首地址 */
};

4)接收消息

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long
msgtyp, int msgflg)
功能:从msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指向的msgbuf结构中。在成功地读取了一条消息以后,队列中的这条消息将被删除。

int read_message(int qid,long type,struct mymsgbuf*qbuf){    int result,length;    length=sizeof(struct mymsgbuf)-sizeof(long);    if((result=msgrcv(qid,qbuf,length,type,0))==-1)        return(-1);    return(result);}

msg1.c

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>struct my_msg_st{    long int my_msg_type;    char some_text[BUFSIZ];};int main(void){        int running=1;int msgid;struct my_msg_st some_data;long int msg_to_receive=0;/*创建消息队列*/msgid=msgget((key_t)1234,0666 | IPC_CREAT);if(msgid==-1){    fprintf(stderr,"msgget failed with error: %d\n",errno);    exit(EXIT_FAILURE);}/*循环从消息队列中接收消息*/while(running){/*读取消息*/        if(msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_receive,0)==-1){    fprintf(stderr,"msgrcv failed with error: %d\n",errno);exit(EXIT_FAILURE);}printf("You wrote: %s",some_data.some_text);/*接收到的消息为“end”时结束循环*/if(strncmp(some_data.some_text,"end",3)==0){    running=0;}}/*从系统内核中移走消息队列*/if(msgctl(msgid,IPC_RMID,0)==-1){    fprintf(stderr,"msgctl(IPC_RMID) failed\n");    exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}

5、信号量

信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。

分类:

1)二值信号灯:信号灯的值只能取0或1,类似于互斥锁。 但两者有不同:信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
2) 计数信号灯:信号灯的值可以取任意非负值。

1)创建/打开

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg)
key:键值,由ftok获得
nsems:指定打开或者新创建的信号灯集中将包含信号灯的数目
semflg:标识,同消息队列

2)操作

int semop(int semid, struct sembuf *sops, unsigned nsops)
功能:对信号量进行控制。
semid:信号量集的ID
sops:是一个操作数组,表明要进行什么操作
nsops:sops所指向的数组的元素个数。

struct sembuf {    unsigned short sem_num; /* semaphore    index in array */    short sem_op; /* semaphore operation */    short sem_flg; /* operation flags */};

sem_num:要操作的信号量在信号量集中的编号,第一个信号的编号是0。
sem_op:如果其值为正数,该值会加到现有的信号量值中,通常用于释放信号量;

如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值,通常用于获取信号量;

如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。

Sem_flg:信号操作标志,可能的选择有两种:
IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
IPC_UNDO:程序结束时(不论正常或不正常)释放信号量,这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

msg2.c

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#define MAX_TEXT 512struct my_msg_st{    long int my_msg_type;    char some_text[MAX_TEXT];};int main(void){        int running=1;struct my_msg_st some_data;int msgid;char buffer[BUFSIZ];/*创建消息队列*/msgid=msgget((key_t)1234,0666 | IPC_CREAT);if(msgid==-1){    fprintf(stderr,"msgget failed with error:%d\n",errno);    exit(EXIT_FAILURE);        }/*循环向消息队列中添加消息*/while(running){        printf("Enter some text:");fgets(buffer,BUFSIZ,stdin);some_data.my_msg_type=1;strcpy(some_data.some_text,buffer);/*添加消息*/if(msgsnd(msgid,(void *)&some_data,MAX_TEXT,0)==-1){    fprintf(stderr,"msgsed failed\n");exit(EXIT_FAILURE);}/*用户输入的为“end”时结束循环*/if(strncmp(buffer,"end",3)==0){    running=0;}}exit(EXIT_SUCCESS);}

原创粉丝点击