利用消息队列来实现IPC

来源:互联网 发布:小米5怎么刷windows 编辑:程序博客网 时间:2024/04/29 19:23
晚上在FC4下调试出了第一个linux程序,成功地运行,有点成就感,虽然这个程序是抄过来.
        本程序的功能是利用系统V(Sys V)消息队列来实现进程间的通信(IPC).
        简单地介绍程序:
        用到的一个数据结构-------消息,用一个结构体来表示:
struct msgbuf {
long  mtype; // 消息的类型
char  mtext[ 1 ]; // 消息的内容,并不是表示只存一个字符
}
;

几个API:
key_t ftok (char*pathname, char proj)
       它返回与路径pathname相对应的一个键值。该函数不直接对消息队列操作,但在调用msgget()来获得消息队列描述字前,往往要调用该函数。典型用法   key=ftok('.', 'a');

int msgget(key_t key, int msgflg)
     参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。
在以下两种情况下,该调用将创建一个新的消息队列:

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

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

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

int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);
该系统调用从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()解除阻塞的条件有三个:

  1. 消息队列中有了满足条件的消息;
  2. msqid代表的消息队列被删除;
  3. 调用msgrcv()的进程被信号中断;

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

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。

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

  • 当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量;
  • 当前消息队列的消息数(单位"个")不小于消息队列的总容量(单位"字节数"),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。

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

  1. 不满足上述两个条件,即消息队列中有容纳该消息的空间;
  2. msqid代表的消息队列被删除;
  3. 调用msgsnd()的进程被信号中断;

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

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。

  1. IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;
  2. IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
  3. IPC_RMID:删除msqid标识的消息队列;

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

#include  < stdio.h >
#include 
< stdlib.h >
#include 
< ctype.h >
#include 
< sys / ipc.h >
#include 
< sys / types.h >
#include 
< sys / msg.h >

#define  MAX_SEND_SIZE 80

struct  mymsgbuf {
    
long  mtype;
    
char  mtext[MAX_SEND_SIZE];
}
;
void  send_message(  int  qid, struct  mymsgbuf  * qbuf, long  type, char   * text);
void  read_message(  int  qid, struct  mymsgbuf  * qbuf, long  type);
void  remove_queue( int  qid);
void  change_queue_mode( int  qid, char   * mode);
void  usage( void );

int  main( int  argc, char   * argv[])
{
    key_t key;
    
int  msgqueue_id;
    
struct  mymsgbuf qbuf;
    
    
if ( 1 == argc)
        usage();
    key
= ftok( " . " , ' m ' );
    
if ((msgqueue_id = msgget(key,IPC_CREAT | 0660 )) ==- 1 ) {
        perror(
" msgget " );
        exit(
1 );
    }

    
switch (tolower(argv[ 1 ][ 0 ]))
    
{
        
case   ' s ' :send_message(msgqueue_id,( struct  mymsgbuf  * ) & qbuf,
                    atol(argv[
2 ]),argv[ 3 ]);
            
break ;
        
case   ' r ' :read_message(msgqueue_id, & qbuf,atol(argv[ 2 ]));
            
break ;
        
case   ' d ' :remove_queue(msgqueue_id);
            
break ;
        
case   ' m ' :change_queue_mode(msgqueue_id,argv[ 2 ]);
            
break ;
        
default :usage();
    }

    
return   0 ;
}

void  send_message( int  qid, struct  mymsgbuf  * qbuf, long  type, char   * text)
{
    printf(
" send a message /n " );
    qbuf
-> mtype = type;
    strcpy(qbuf
-> mtext,text);
    
if ((msgsnd(qid,( struct  msgbuf  * )qbuf,strlen(qbuf -> mtext) + 1 , 0 )) ==- 1 ) {
        perror(
" msgsnd " );
        exit(
1 );
    }

}

void  read_message( int  qid, struct  mymsgbuf  * qbuf, long  type)
{
    printf(
" reading a message./n " );
    qbuf
-> mtype = type;
    msgrcv(qid,(
struct  msgbuf  * )qbuf,MAX_SEND_SIZE,type, 0 );
    printf(
" Type: %1d Text: %s/n " ,qbuf -> mtype,qbuf -> mtext);
}

void  remove_queue( int  qid)
{
    msgctl(qid,IPC_RMID,
0 );
}

void  change_queue_mode( int  qid, char   * mode)
{
    
struct  msqid_ds myqueue_ds;
    msgctl(qid,IPC_STAT,
& myqueue_ds);
    sscanf(mode,
" %ho " , & myqueue_ds.msg_perm.mode);
    msgctl(qid,IPC_SET,
& myqueue_ds);
}

void  usage( void )
{
    fprintf(stderr,
" msgtool -A utility for tinkering with msg queue/n " );
    fprintf(stderr,
" /nUSAGE:msgtool (s)end <type> <messagetext>/n " );
    fprintf(stderr,
"               (r)ecv<type>/n " );
    fprintf(stderr,
"               (d)elete/n " );
    fprintf(stderr,
"               (m)ode<octal mode>/n " );
    exit(
1 );
}

以上程序编译能通过,只是出现了两个warning
   warning:内建函数strcpy不兼容的隐式声明
   warning:内建函数strlen不兼容的隐式声明

不知是什么原因?是不是gcc版本问题?

还有在写Makefile后,make时出现一个错误提示:
commands commence before first target
解决方法:去掉target的空格(tab键)


程序来是http://tldp.org/LDP/lpg/node45.html#SECTION00742640000000000000
API解释来自http://www.52wg.org/computer/chengxu/200511/computer_98386.html