萬盛學電腦網

 萬盛學電腦網 >> 服務器教程 >> linux環境編程-IPC 之 msg queue

linux環境編程-IPC 之 msg queue

   消息隊列

在UNIX的SystemV版本,AT&T引進了三種新形式的IPC功能(消息隊列、信號量、以及共享內存)。但BSD版本的UNIX使用套接口作為主要的IPC形式。Linux系統同時支持這兩個版本。

  系統調用msgget()

  如果希望創建一個新的消息隊列,或者希望存取一個已經存在的消息隊列,你可以使用系統調用msgget()。

  系統調用:msgget();

  原型:int msgget(key_t key, int msgflg);

  返回值:如果成功,返回消息隊列標識符

  如果失敗,則返回-1:errno=EACCESS(權限不允許)

  EEXIST(隊列已經存在,無法創建)

  EIDRM(隊列標志為刪除)

  ENOENT(隊列不存在)

  ENOMEM(創建隊列時內存不夠)

  ENOSPC(超出最大隊列限制)

  系統調用msgget()中的第一個參數是關鍵字值(通常是由ftok() 返回的)。然後此關鍵字值將會和其他已經存在於系統內核中的關鍵字值比較。這時,打開和存取操作是和參數msgflg中的內容相關的。

  IPC_CREAT如果內核中沒有此隊列,則創建它。

  IPC_EXCL當和IPC_CREAT一起使用時,如果隊列已經存在,則失敗。

  如果單獨使用IPC_CREAT,則msgget()要麼返回一個新創建的消息隊列的標識符,要麼返回具有相同關鍵字值的隊列的標識符。如果IPC_EXCL和IPC_CREAT一起使用,則msgget()要麼創建一個新的消息隊列,要麼如果隊列已經存在則返回一個失敗值-1。IPC_EXCL單獨使用是沒有用處的。

  下面看一個打開和創建一個消息隊列的例子:

  int open_queue(key_t keyval)

  {

  intqid;

  if((qid=msgget (keyval, IPC_CREAT|0660))==-1)

  {

  return(-1);

  }

  return(qid);

  }

  系統調用msgsnd()

  一旦我們得到了隊列標識符,我們就可以在隊列上執行我們希望的操作了。如果想要往隊列中發送一條消息,你可以使用系統調用msgsnd():

  系統調用:msgsnd();

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

  返回值:如果成功,0。

  如果失敗,-1:errno=EAGAIN(隊列已滿,並且使用了IPC_NOWAIT)

  EACCES(沒有寫的權限)

  EFAULT(msgp地址無效)

  EIDRM(消息隊列已經刪除)

  EINTR(當等待寫操作時,收到一個信號)

  EINVAL(無效的消息隊列標識符,非正數的消息類型,或

  者無效的消息長度)

  ENOMEM(沒有足夠的內存復制消息緩沖區)

  系統調用msgsnd()的第一個參數是消息隊列標識符,它是由系統調用msgget返回的。第二個參數是msgp,是指向消息緩沖區的指針。參數msgsz中包含的是消息的字節大小,但不包括消息類型的長度(4個字節)。

  參數msgflg可以設置為0(此時為忽略此參數),或者使用IPC_NOWAIT。

  如果消息隊列已滿,那麼此消息則不會寫入到消息隊列中,控制將返回到調用進程中。如果沒有指明,調用進程將會掛起,直到消息可以寫入到隊列中。

  下面是一個發送消息的程序:

  int send_message(int qid, struct mymsgbuf *qbuf)

  {

  intresult,length;

  /*The length is essentially the size of the structure minus sizeof(mtype)*/

  length=sizeof(structmymsgbuf)-sizeof(long);

  if((result = msgsnd (qid, qbuf, length, 0))==-1)

  {

  return(-1);

  }

  return(result);

  }

  系統調用:msgrcv();

  原型:int msgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmtype,intmsgflg);

  返回值:如果成功,則返回復制到消息緩沖區的字節數。

  如果失敗,則返回-1:errno=E2BIG(消息的長度大於msgsz,沒有MSG_NOERROR)

  EACCES(沒有讀的權限)

  EFAULT(msgp指向的地址是無效的)

  EIDRM(隊列已經被刪除)

  EINTR(被信號中斷)

  EINVAL(msgqid無效,或者msgsz小於0)

  ENOMSG(使用IPC_NOWAIT,同時隊列中的消息無法滿足要求)

  第一個參數用來指定將要讀取消息的隊列 。第二個參數代表要存儲消息的消息緩沖區 的地址。第三個參數是消息緩沖區的長度,不包括mtype的長度,它可以按照如下的方法計算:

  msgsz=sizeof(struct mymsgbuf)-sizeof(long);

  第四個參數是要從消息隊列中讀取的消息的類型。如果此參數的值為0,那麼隊列中最長時間的一條消息將返回,而不論其類型是什麼。

  如果調用中使用了IPC_NOWAIT作為標志,那麼當沒有數據可以使用時,調用將把ENOMSG返回到調用進程中。否則,調用進程將會掛起,直到隊列中的一條消息滿足msgrcv()的參數要求。如果當客戶端等待一條消息的時候隊列為空,將會返回EIDRM。如果進程在等待消息的過程中捕捉到一個信號,則返回EINTR。

  下面就是一個從隊列中讀取消息的程序:

  int read_message(int qid,long type,struct mymsgbuf*qbuf)

  {

  intresult,length;

  /*The length is essentially the size of the structure minus sizeof(mtype)*/

  length=sizeof(struct mymsgbuf)-sizeof(long);

  if((result=msgrcv(qid,qbuf,length,type,0))==-1)

  {

  return(-1);

  }

  return(result);

  }

  在成功地讀取了一條消息以後,隊列中的這條消息的入口將被刪除

  參數msgflg中的MSG_NOERROR位提供一種額外的用途。如果消息的實際長度大於msgsz,同時使用了MSG_NOERROR,那麼消息將會被截斷,只有與msgsz長度相等的消息返回。一般情況下,系統調用msgrcv()會返回-1,而這條消息將會繼續保存在隊列中。我們可以利用這個特點編制一個程序,利用這個程序可以查看消息隊列的情況,看看符合我們條件的消息是否已經到來:

  int peek_message(int qid,long type)

  {

  int result,length;

  if((result=msgrcv(qid,NULL,0,type,IPC_NOWAIT))==-1)

  {

  if(errno==E2BIG)

  return(TRUE);

  }

  return(FALSE);

  }

  在上面的程序中,我們忽略了緩沖區的地址和長度。這樣,系統調用將會失敗。盡管如此,我們可以檢查返回的E2BIG值,它說明符合條件的消息確實存在。

  系統調用msgctl()

  下面我們繼續討論如何使用一個給定的消息隊列的內部數據結構。我們可以使用系統調用msgctl ( )來控制對消息隊列的操作。

  系統調用: msgctl( ) ;

  調用原型: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

  返回值: 0 ,如果成功。

  - 1,如果失敗:errno = EACCES (沒有讀的權限同時cmd 是IPC_STAT )

  EFAULT (buf 指向的地址無效)

  EIDRM (在讀取中隊列被刪除)

  EINVAL (msgqid無效, 或者msgsz 小於0 )

  EPERM (IPC_SET或者IPC_RMID 命令被使用,但調用程序沒有寫的權限)

  下面我們看一下可以使用的幾個命令:

  IPC_STAT

  讀取消息隊列的數據結構msqid_ds,並將其存儲在b u f指定的地址中。

  IPC_SET

  設置消息隊列的數據結構msqid_ds中的ipc_perm元素的值。這個值取自buf參數。

  IPC_RMID

  從系統內核中移走消息隊列。

  我們在前面討論過了消息隊列的數據結構(msqid_ds)。系統內核中為系統中的每一個消息隊列保存一個此數據結構的實例。通過使用IPC_STAT命令,我們可以得到一個此數據結構的副本。下面的程序就是實現此函數的過程:

  int get_queue_ds( int qid, struct msgqid_ds *qbuf )

  {

  if( msgctl( qid, IPC_STAT, qbuf) == -1)

  {

  return(-1);

  }

  return(0);

  }

  如果不能復制內部緩沖區,調用進程將返回-1。如果調用成功,則返回0。緩沖區中應該包括消息隊列中的數據結構。

  消息隊列中的數據結構中唯一可以改動的元素就是ipc_perm。它包括隊列的存取權限和關於隊列創建者和擁有者的信息。你可以改變用戶的id、用戶的組id以及消息隊列的存取權限。

  下面是一個修改隊列存取模式的程序:

  int change_queue_mode(int qid, char *mode )

  {

  struct msqid_ds tmpbuf;

  /* Retrieve a current copy of the internal data structure */

  get_queue_ds( qid, &tmpbuf);

  /* Change the permissions using an old trick */

  sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);

  /* Update the internal data structure */

  if( msgctl( qid, IPC_SET, &tmpbuf) == -1)

  {

  return(-1);

  }

  return(

  }

copyright © 萬盛學電腦網 all rights reserved