重新審視進程間的通信(二)

在UNIX網絡編程中有關消息隊列的內容分兩章,一章Posix標準,一章System V(包括之後的內容都是)。

先看System V。


struct msqid_ds
{
  struct ipc_perm msg_perm;	/* structure describing operation permission */
  __time_t msg_stime;		/* time of last msgsnd command */
#ifndef __x86_64__
  unsigned long int __glibc_reserved1;
#endif
  __time_t msg_rtime;		/* time of last msgrcv command */
#ifndef __x86_64__
  unsigned long int __glibc_reserved2;
#endif
  __time_t msg_ctime;		/* time of last change */
#ifndef __x86_64__
  unsigned long int __glibc_reserved3;
#endif
  __syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */
  msgqnum_t msg_qnum;		/* number of messages currently on queue */
  msglen_t msg_qbytes;		/* max number of bytes allowed on queue */
  __pid_t msg_lspid;		/* pid of last msgsnd() */
  __pid_t msg_lrpid;		/* pid of last msgrcv() */
  __syscall_ulong_t __glibc_reserved4;
  __syscall_ulong_t __glibc_reserved5;
};

Unix98不要求有msg_first、msg_last、msg_cbytes成員(我也沒找到)msg_first、msg_last兩個指針指向內核,對於應用基本沒用。


創建或者訪問一個隊列

#include <sys/msg.h>

int msgget(key_t key, int oflag);

返回值是一個整數標識符,代指該隊列

key可以使ftok()的返回值或者常數IPC_PRIVATE

oflag讀寫權限組合值,還可與IPC_CREATE(key不存在創建,存在則引用)和IPC_CREAT | IPC_EXCL(key不存在創建,存在則出錯)進行異或

當創建時,msg_perm中的uid和cuid成員被設置爲當前進程有效用戶ID,gid和cgid被設置當前進程的有效組ID


放置消息

#include <sys/msg.h>

int msgsnd(int msqid, const void *ptr, size_t length, int flag);

mspid是msgget的返回值


ptr是一個結構指針指向msgbuf,該結構沒有定式,但是有一定要求

struct msgbug {
    long mtype;
    char data[1];
}

該結構必須以一個長整型變量(表示類型)開始和數據,其他的自己看着辦。

length爲自定義的數據大小。

flag 爲0(如果沒有新消息的可用空間則阻塞)或者IPC_NOWAIT(如果數據放不下則返回錯誤)


讀消息

ssize_t msgrcv(int msgid, void *ptr, size_t length, long type, int flag);

ptr指向一個結構踢和msgsnd一樣,length爲緩衝區中數據大小(最多能返回的數據量)


type給定讀出什麼樣的消息

爲0,返回第一個消息(最早的消息)

大於0,返回類型值爲type的第一個消息

小於0,返回類型值小於或等於type絕對值的消息中類型值最小的那個消息。

flag:IPC_NOWAIT如果沒有所請求類型的消息則返回錯誤,否則阻塞

MSG_NOERROR如果數據長度大於length則截取不返回錯誤,否則返回E2BIG錯誤


控制操作

int msgctl(int msgid, int cmd, struct msqid_ds *buff);
IPC_RMID:刪除指定隊列

IPC_SET:給指定隊列設置msqid_ds結構中msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes

IPC_STAT:給調用者返回指定消息隊列的msqid_ds


再看Posix消息隊列


每個消息有如下屬性:

1:一個無符號整數優先級(Posix)或長整數類型(System V)

2:數據部分長度

3:數據本身


創建新消息隊列或打開一個隊列

#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag, .../*mode_t mode, struct attr *attr*/);
oflag標誌位

O_RDONLY(只讀) O_WRONLY(只寫) O_RDWR(可讀可寫)可與以下異或

O_CREAT(創建) O_EXCL (當消息已存在時,返回EEXIST錯誤到errno中)O_NONBLOCK(設置非阻塞)

mode參數

S_IRUSR用戶(屬主)讀

S_IWUSR用戶(屬主)讀

S_IRGRP組成員讀

S_IWGRP組成員寫

S_IROTH其他用戶讀

S_IWOTH其他用戶寫

attr用於給新隊列指定某些屬性,NULL爲默認屬性

返回值爲消息隊列描述符

關閉

int mq_close(mqd_t mqdes);
很簡單,當一個進程終止它所打開的消息隊列都關閉,就像調用mq_close一樣


int mq_unlink(const char *name);
每個消息隊列都有一個保存當前打開着描述符數的引用計數器,當一個消息隊列的引用計數大於0就可以用此函數刪除name,該隊列析構要到最後一個mq_close

消息隊列屬性

struct mq_attr

{

long int mq_flags;    /* Message queue flags0O_NONBLOCK */

long int mq_maxmsg;   /* Maximum number of messages.  */

long int mq_msgsize;  /* Maximum message size.  */

long int mq_curmsgs;  /* Number of messages currently queued.  */

long int __pad[4];

};

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);

mq_setattr函數只使用attr指向的結構的mq_flags成員,對oattr指向的結構進行設置

如果oattr非空,則隊列的先前屬性和當前狀態都返回到該數組中。


int mq_send(mqd_t mqdes, char *ptr, size_t len, unsigned int prio);
ssize_t mq_receiver(mqd_t mqdes, char *ptr, size_t len, unsigned int *priop);

發送和接收函數

mq_receive中的len不能小於能加到所指隊列中的消息的最大大小(mq_msgsize成員)

mq_send函數中prio是待發送消息的優先級,其值必須小於MQ_PRIO_MAX

如果mq_recceive中的priop非空則用來保存返回消息的優先級。

Posix消息隊列允許異步事件通知(asynchronous event notification),以告知何時有一個消息放置到了某個隊列中。

int mq_notify(mqd_t mqdes, const struct sigevent *notification);

該函數通知進程何時可以接受一條消息

sigevent結構體

struct sigevent {
    int           sigev_notify;            //Notification type. 
    int           sigev_signo;            //Signal number. 
    union sigval  sigev_value;             //Signal value. 
    void         (*sigev_notify_function)(union sigval); //Notification function. 
    pthread_attr_t *sigev_notify_attributes;  //Notification attributes. 
};
 sigev_notify 的取值:

SIGEV_NONE 事件發生時,什麼也不做. SIGEV_SIGNAL 事件發生時,將sigev_signo 指定的信號(A queued signal)發送給指定的進程. SIGEV_THREAD 事件發生時,內核會(在此進程內)以sigev_notification_attributes爲線程屬性創建一個線程,並且讓它執行sigev_notify_function 傳入sigev_value作爲爲一個參數.
union sigval
{
    int sival_int;
    void *sival_ptr;
};
mq_notify函數有一下適用規則

(1)如果notification參數非空,那麼當前進程希望在有一個消息達到所指定的先前爲空的隊列時得到通知

(2)如果notification參數爲空,而且當前進程目前被註冊爲接收所指定隊列的通知,那麼已存在的註冊將被撤銷

(3)任意時刻只有一個進程可以被註冊爲接收某個給定隊列的通知

(4)當有一個消息到達某個先前爲空的隊列,而且已有一個進程被註冊爲接收該隊列的通知時,只有在沒有任何線程阻塞在該隊列的mq_receive調用中的前提下,通知纔會發出。mq_receive調用比任何通知的註冊都優先。

(5)當該通知被髮送給它的註冊過程時,其註冊即被註銷。必須再次調用mq_notify以重新註冊。


Posix消息隊列和System V消息隊列差別

(1)Posix消息隊列總是返回優先級最高的最早消息,System V消息隊列可以返回指定的任意消息;

(2)當往空隊列中添加消息時,Posix消息隊列允許產生一個信號或者啓動一個線程。


PSSSSSSSSSSSS:爲啥子CSDN博客總是無法正常顯示呢,好尷尬。。。。修改幾次了





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章