首頁 > 軟體

Linux核心情景分析之訊息佇列

2020-06-16 17:29:49

早期的Unix通訊只有管道與信號,管道的缺點:

所載送的資訊是無格式的位元組流,不知道分界線在哪,也沒通訊規範,另外缺乏控制手段,比如保溫優先順序,管道機制的大小只有1頁,管道很容易寫滿而讀取沒有及時,傳送者只能休眠,強化了管道機制同步要求.另外管道機制開銷不少,尤其是當傳送資訊量很少時,平均每個位元組所耗費的代價非常高
 
Linux核心為系統IPC提供了一個統一的系統呼叫ipc()
  1. int ipc(unsignedint call,int firtst,int second,int third,void*ptr,int firth);
其中第一個引數為具體的操作碼
  1. #define SEMOP 1
  2. #define SEMGET 2
  3. #define SEMCTL 3
  4. #define MSGSND 11
  5. #define MSGRCV 12
  6. #define MSGGET 13
  7. #define MSGCTL 14
  8. #define SHMAT 21
  9. #define SHMDT 22
  10. #define SHMGET 23
  11. #define SHMCTL 24
操作碼SEM開頭的都是號誌設定,MSG開頭則是訊息佇列,"SHM"開頭則是共用記憶體區設定,其他引數因具體操作而不同,不過為了方便使用,c語言庫函數分別提供了semget,msgget,msgsnd等庫函數,這些庫函數把其呼叫統一成了系統呼叫ipc.
核心入口為sys_ipc()
 
 
訊息佇列並沒有納入檔案系統範疇,所以並不佔用開啟檔案號,核心有個全域性的資料結構msg_ids.專門用來管理報文佇列
static struct ipc_ids msg_ids;
  1. struct ipc_ids {
  2. int size;
  3. int in_use;
  4. int max_id;
  5. unsignedshort seq;
  6. unsignedshort seq_max;
  7. struct semaphore sem;
  8. spinlock_t ary;
  9. struct ipc_id* entries;//指向一個結構陣列
  10. };
//ipc_id如下定義
  1. struct ipc_id {
  2. struct kern_ipc_perm* p;
  3. };
kern_ipc_perm結構如下定義
  1. /* used by in-kernel data structures */
  2. struct kern_ipc_perm
  3. {
  4. key_t key;//建值
  5. uid_t uid;
  6. gid_t gid;
  7. uid_t cuid;
  8. gid_t cgid;
  9. mode_t mode;
  10. unsignedlong seq;
  11. };
 
 
每個報文佇列都有個佇列頭,msg_queue資料結構
  1. /* one msq_queue structure for each present queue on the system */
  2. struct msg_queue {
  3. struct kern_ipc_perm q_perm;
  4. time_t q_stime;/* last msgsnd time */
  5. time_t q_rtime;/* last msgrcv time */
  6. time_t q_ctime;/* last change time */
  7. unsignedlong q_cbytes;/* current number of bytes on queue */
  8. unsignedlong q_qnum;/* number of messages in queue */
  9. unsignedlong q_qbytes;/* max number of bytes on queue */
  10. pid_t q_lspid;/* pid of last msgsnd */
  11. pid_t q_lrpid;/* last receive pid */
  12. struct list_head q_messages;
  13. struct list_head q_receivers;
  14. struct list_head q_senders;
  15. };
每個msg_queue資料結構唯一對應著一個報文佇列,這些資料結構以及結構之間的關係可以如下總結:
全域性ipc_ids資料結構的msg_ids是系統中所有報文佇列的總根,總跟的指標entries指向一個ipc_id結構陣列,陣列中的每個元素都是ipc_id資料結構,結構中有個指標p指向一個kern_ipc_perm資料結構,由於kern_ipc_perm資料結構是報文佇列頭msg_queue的第一個成分,陣列元素的指標p實際指向一個報文對哦,陣列大小已經決定了可以建立的報文佇列數目
訊息佇列的建立或獲取(msgget)
  1. //可以用於2個目的,通過給定的key建立佇列,通過給定的key查詢已存在佇列
  2. asmlinkage long sys_msgget (key_t key,int msgflg)
  3. {
  4. int id, ret =-EPERM;
  5. struct msg_queue *msq;//佇列
  6. down(&msg_ids.sem);
  7. if(key == IPC_PRIVATE)//自己私用,無條件建立一個報文佇列
  8. ret = newque(key, msgflg);//根據key與msgflg
  9. elseif((id = ipc_findkey(&msg_ids, key))==-1){/* key沒有找到key not used */
  10. if(!(msgflg & IPC_CREAT))//沒找到,但沒設定IPC_CREAT那就返回錯誤
  11. ret =-ENOENT;
  12. else//設定了就建立報文佇列
  13. ret = newque(key, msgflg);
  14. }elseif(msgflg & IPC_CREAT && msgflg & IPC_EXCL){//同時設定了IPC_CREAT與IPC_EXCL返回錯誤
  15. ret =-EEXIST;
  16. }else{
  17. msq = msg_lock(id);
  18. if(msq==NULL)
  19. BUG();
  20. if(ipcperms(&msq->q_perm, msgflg))//檢查存取許可權是否符合規則
  21. ret =-EACCES;
  22. else
  23. ret = msg_buildid(id, msq->q_perm.seq);//將陣列下標轉換一體化的標識號
  24. msg_unlock(id);
  25. }
  26. up(&msg_ids.sem);
  27. return ret;//返回標識號
  28. }
檢視具體的佇列建立函數newque()
  1. staticint newque (key_t key,int msgflg)
  2. {
  3. int id;
  4. struct msg_queue *msq;//佇列頭
  5. msq =(struct msg_queue *) kmalloc (sizeof(*msq), GFP_KERNEL);//分配結構
  6. if(!msq)
  7. return-ENOMEM;
  8. id = ipc_addid(&msg_ids,&msq->q_perm, msg_ctlmni);//分配一個標識號
  9. if(id ==-1){
  10. kfree(msq);
  11. return-ENOSPC;
  12. }//以下是報文佇列頭各種初始化
  13. msq->q_perm.mode =(msgflg & S_IRWXUGO);
  14. msq->q_perm.key = key;//key值
  15. msq->q_stime = msq->q_rtime =0;
  16. msq->q_ctime = CURRENT_TIME;
  17. msq->q_cbytes = msq->q_qnum =0;
  18. msq->q_qbytes = msg_ctlmnb;
  19. msq->q_lspid = msq->q_lrpid =0;
  20. INIT_LIST_HEAD(&msq->q_messages);
  21. INIT_LIST_HEAD(&msq->q_receivers);
  22. INIT_LIST_HEAD(&msq->q_senders);
  23. msg_unlock(id);
  24. //將標識號轉換為一個一體化的標識號,因為實際分配的id實際是陣列下標會重複使用
  25. return msg_buildid(id,msq->q_perm.seq);
  26. }
每個已建立的報文佇列由一個標識號來代表,報文佇列的標識號是全域性的,所以必須把這全域性範圍內的唯一性,標識號由ipc_addid()分配,
  1. /**
  2. * ipc_addid - add an IPC identifier
  3. * @ids: IPC identifier set
  4. * @new: new IPC permission set
  5. * @size: new size limit for the id array
  6. *
  7. * Add an entry 'new' to the IPC arrays. The permissions object is
  8. * initialised and the first free entry is set up and the id assigned
  9. * is returned. The list is returned in a locked state on success.
  10. * On failure the list is not locked and -1 is returned.
  11. */
  12. //全域性佇列管理結構 新建立的佇列頭的這個結構
  13. int ipc_addid(struct ipc_ids* ids,struct kern_ipc_perm*new,int size)
  14. {
  15. int id;
  16. size = grow_ary(ids,size);//增加管理陣列的大小
  17. for(id =0; id < size; id++){
  18. if(ids->entries[id].p == NULL)//總根看管理陣列的哪個為空
  19. goto found;
  20. }
  21. return-1;
  22. found:
  23. ids->in_use++;//已使用++
  24. if(id > ids->max_id)//最大的key值
  25. ids->max_id = id;
  26. new->cuid =new->uid = current->euid;//uid賦值
  27. new->gid =new->cgid = current->egid;//gid賦值
  28. new->seq = ids->seq++;//序列號
  29. if(ids->seq > ids->seq_max)//超過了最大的限定
  30. ids->seq =0;//從0開始繼續
  31. spin_lock(&ids->ary);
  32. ids->entries[id].p =new;//掛鉤,新建立的報文頭與總跟掛鉤成功
  33. return id;
  34. }
庫函數msgsnd-報文傳送
參與通訊雙方通過mssget建立了一個報文佇列後或取得該佇列的標識號以後就可以向該佇列傳送或接收報文了
傳送格式為msgbuf
  1. /* message buffer for msgsnd and msgrcv calls */
  2. struct msgbuf {
  3. long mtype;/* type of message */
  4. char mtext[1];/* message text */
  5. };
核心儲存訊息佇列的格式
  1. /* one msg_msg structure for each message */
  2. struct msg_msg {
  3. struct list_head m_list;
  4. long m_type;//型別
  5. int m_ts;/*長度 message text size */
  6. struct msg_msgseg* next;
  7. /* the actual message follows immediately */
  8. };
  1. struct msg_msgseg {
  2. struct msg_msgseg* next;
  3. /* the next part of the message follows immediately */
  4. };
分配一頁,第一個除去msg_msg其他的用於儲存訊息,如果不夠繼續分配一頁除去msg_msg_seq以此類推
 
  1. //標識號 傳送格式 //大小 //標誌位
  2. asmlinkage long sys_msgsnd (int msqid,struct msgbuf *msgp,size_t msgsz,int msgflg)
  3. {
  4. struct msg_queue *msq;//佇列頭
  5. struct msg_msg *msg;//核心儲存資訊的格式
  6. long mtype;
  7. int err;
  8. //訊息不可以超過8k
  9. if(msgsz > msg_ctlmax ||(long) msgsz <0|| msqid <0)
  10. return-EINVAL;
  11. if(get_user(mtype,&msgp->mtype))//從使用者空間拷貝到核心
  12. return-EFAULT;
  13. if(mtype <1)
  14. return-EINVAL;
  15. msg = load_msg(msgp->mtext, msgsz);//分配緩衝區儲存訊息(從使用者拷貝到核心)
  16. if(IS_ERR(msg))
  17. return PTR_ERR(msg);
  18. msg->m_type = mtype;//訊息型別
  19. msg->m_ts = msgsz;//訊息大小
  20. msq = msg_lock(msqid);//根據給定的標號msg_msg找到相應的報文佇列,將其資料結構上鎖
  21. err=-EINVAL;
  22. if(msq==NULL)
  23. goto out_free;
  24. retry:
  25. err=-EIDRM;
  26. if(msg_checkid(msq,msqid))//驗證下id號
  27. goto out_unlock_free;
  28. err=-EACCES;
  29. if(ipcperms(&msq->q_perm, S_IWUGO))//檢查是否有許可權向這個佇列傳送報文
  30. goto out_unlock_free;
  31. //當前報文大小+當前佇列統計的位元組數超過了報文佇列的總容量.或者報文的個數超過了限制,那就不可以傳送了
  32. if(msgsz + msq->q_cbytes > msq->q_qbytes ||
  33. 1+ msq->q_qnum > msq->q_qbytes){
  34. struct msg_sender s;
  35. if(msgflg&IPC_NOWAIT){//是否等待,不等待直接退出
  36. err=-EAGAIN;
  37. goto out_unlock_free;
  38. }
  39. ss_add(msq,&s);//掛載到報文佇列q_sender鏈,這樣可以通過此鏈找到休眠正在等待傳送的進程
  40. msg_unlock(msqid);
  41. schedule();//排程
  42. current->state= TASK_RUNNING;
  43. msq = msg_lock(msqid);
  44. err =-EIDRM;
  45. if(msq==NULL)
  46. goto out_free;
  47. ss_del(&s);//刪除
  48. if(signal_pending(current)){
  49. err=-EINTR;
  50. goto out_unlock_free;
  51. }
  52. goto retry;//重新執行一遍
  53. }
  54. if(!pipelined_send(msq,msg)){//如果有相關進程正在讀這個報文就不用放入佇列了
  55. /* noone is waiting for this message, enqueue it */
  56. list_add_tail(&msg->m_list,&msq->q_messages);//鏈入佇列
  57. msq->q_cbytes += msgsz;//總數++
  58. msq->q_qnum++;//數目++
  59. atomic_add(msgsz,&msg_bytes);
  60. atomic_inc(&msg_hdrs);
  61. }
  62. err =0;
  63. msg = NULL;
  64. msq->q_lspid = current->pid;
  65. msq->q_stime = CURRENT_TIME;
  66. out_unlock_free:
  67. msg_unlock(msqid);
  68. out_free:
  69. if(msg!=NULL)
  70. free_msg(msg);
  71. return err;
  72. }
load_msg用於訊息分割,在核心儲存,見上圖
  1. //報文的源
  2. staticstruct msg_msg* load_msg(void* src,int len)
  3. {
  4. struct msg_msg* msg;
  5. struct msg_msgseg** pseg;
  6. int err;
  7. int alen;
  8. alen = len;
  9. if(alen > DATALEN_MSG)//一頁減去msg結構大小
  10. alen = DATALEN_MSG;
  11. msg =(struct msg_msg *) kmalloc (sizeof(*msg)+ alen, GFP_KERNEL);//一個報文的頭
  12. if(msg==NULL)
  13. return ERR_PTR(-ENOMEM);
  14. msg->next = NULL;
  15. if(copy_from_user(msg+1, src, alen)){//從msg的尾部開始拷貝
  16. err =-EFAULT;
  17. goto out_err;
  18. }
  19. len -= alen;//剩餘長度
  20. src =((char*)src)+alen;//剩餘源頭
  21. pseg =&msg->next;//指向下一個頁
  22. while(len >0){//還有剩餘長度
  23. struct msg_msgseg* seg;
  24. alen = len;
  25. if(alen > DATALEN_SEG)//是否超過page-msgseg
  26. alen = DATALEN_SEG;
  27. seg =(struct msg_msgseg *) kmalloc (sizeof(*seg)+ alen, GFP_KERNEL);//獲取一頁
  28. if(seg==NULL){
  29. err=-ENOMEM;
  30. goto out_err;
  31. }
  32. *pseg = seg;//連結
  33. seg->next = NULL;
  34. if(copy_from_user (seg+1, src, alen)){//繼續拷貝
  35. err =-EFAULT;
  36. goto out_err;
  37. }
  38. pseg =&seg->next;
  39. len -= alen;
  40. src =((char*)src)+alen;
  41. }
  42. return msg;
  43. out_err:
  44. free_msg(msg);
  45. return ERR_PTR(err);
  46. }
  1. /* one msg_sender for each sleeping sender */
  2. struct msg_sender {
  3. struct list_head list;
  4. struct task_struct* tsk;
  5. };
 
 
ss_add(msq, &s);//掛載到報文佇列q_sender鏈,這樣可以通過此鏈找到休眠正在等待傳送的進程
  1. staticinlinevoid ss_add(struct msg_queue* msq,struct msg_sender* mss)
  2. {
  3. mss->tsk=current;//獲取當前佇列的進程執行的進程
  4. current->state=TASK_INTERRUPTIBLE;//設定為可中斷睡眠狀態
  5. list_add_tail(&mss->list,&msq->q_senders);//新增到隊尾
  6. }
如果有相關進程正在讀這個報文就不用放入佇列了
  1. intinline pipelined_send(struct msg_queue* msq,struct msg_msg* msg)
  2. {
  3. struct list_head* tmp;
  4. tmp = msq->q_receivers.next;//聚集正在睡眠等待接收的讀進程
  5. while(tmp !=&msq->q_receivers){//表示有
  6. struct msg_receiver* msr;
  7. msr = list_entry(tmp,struct msg_receiver,r_list);
  8. tmp = tmp->next;
  9. if(testmsg(msg,msr->r_msgtype,msr->r_mode)){//型別是否匹配
  10. list_del(&msr->r_list);
  11. if(msr->r_maxsize < msg->m_ts){//讀的緩衝區是否夠用
  12. msr->r_msg = ERR_PTR(-E2BIG);
  13. wake_up_process(msr->r_tsk);//不夠用則將進程喚醒,讓其出錯返回
  14. }else{
  15. msr->r_msg = msg;//有的話,直接讀取
  16. msq->q_lspid = msr->r_tsk->pid;
  17. msq->q_rtime = CURRENT_TIME;
  18. wake_up_process(msr->r_tsk);
  19. return1;
  20. }
  21. }
  22. }
  23. return0;
  24. }
 
 
訊息佇列接收msgrcv
  1. //標識號 使用者空間緩衝區 大小
  2. asmlinkage long sys_msgrcv (int msqid,struct msgbuf *msgp,size_t msgsz,
  3. long msgtyp,int msgflg)//msgtyp訊息型別,
  4. {
  5. struct msg_queue *msq;//佇列頭
  6. struct msg_receiver msr_d;//接收的需要睡眠的進程
  7. struct list_head* tmp;
  8. struct msg_msg* msg,*found_msg;
  9. int err;
  10. int mode;
  11. if(msqid <0||(long) msgsz <0)
  12. return-EINVAL;
  13. mode = convert_mode(&msgtyp,msgflg);
  14. msq = msg_lock(msqid);//根據報文佇列標識號,找到具體佇列
  15. if(msq==NULL)
  16. return-EINVAL;
  17. retry:
  18. err=-EACCES;
  19. if(ipcperms (&msq->q_perm, S_IRUGO))//檢測是否具有許可權
  20. goto out_unlock;
  21. tmp = msq->q_messages.next;
  22. found_msg=NULL;
  23. while(tmp !=&msq->q_messages){//遍歷完
  24. msg = list_entry(tmp,struct msg_msg,m_list);//根據佇列當前項找到其指標
  25. if(testmsg(msg,msgtyp,mode)){
  26. found_msg = msg;//查詢到了訊息
  27. if(mode == SEARCH_LESSEQUAL && msg->m_type !=1){
  28. found_msg=msg;
  29. msgtyp=msg->m_type-1;//將type減到比這個報文的型別值更小,看能否找到更小的
  30. }else{
  31. found_msg=msg;
  32. break;
  33. }
  34. }
  35. tmp = tmp->next;
  36. }
  37. if(found_msg){
  38. msg=found_msg;
  39. if((msgsz < msg->m_ts)&&!(msgflg & MSG_NOERROR)){//如果接收大小小於報文大小,出錯
  40. err=-E2BIG;
  41. goto out_unlock;
  42. }
  43. list_del(&msg->m_list);//否則將該報文從佇列脫鏈
  44. msq->q_qnum--;
  45. msq->q_rtime = CURRENT_TIME;
  46. msq->q_lrpid = current->pid;
  47. msq->q_cbytes -= msg->m_ts;
  48. atomic_sub(msg->m_ts,&msg_bytes);
  49. atomic_dec(&msg_hdrs);
  50. ss_wakeup(&msq->q_senders,0);//將傳送的睡眠等待進程全部喚醒,因為拿出了一個報文
  51. msg_unlock(msqid);
  52. out_success:
  53. msgsz =(msgsz > msg->m_ts)? msg->m_ts : msgsz;
  54. if(put_user (msg->m_type,&msgp->mtype)||//實際接收的報文型別,通過put_user送回使用者空間
  55. store_msg(msgp->mtext, msg, msgsz)){//將實際接收到的複製到使用者空間
  56. msgsz =-EFAULT;
  57. }
  58. free_msg(msg);//釋放核心快取
  59. return msgsz;
  60. }else//報文佇列還沒有報文可供接收
  61. {
  62. struct msg_queue *t;
  63. /* no message waiting. Prepare for pipelined
  64. * receive.
  65. */
  66. if(msgflg & IPC_NOWAIT){//不等待直接返回錯誤
  67. err=-ENOMSG;
  68. goto out_unlock;
  69. }
  70. list_add_tail(&msr_d.r_list,&msq->q_receivers);//鏈入等待接收佇列進程
  71. msr_d.r_tsk = current;//儲存各種資訊
  72. msr_d.r_msgtype = msgtyp;
  73. msr_d.r_mode = mode;
  74. if(msgflg & MSG_NOERROR)
  75. msr_d.r_maxsize = INT_MAX;
  76. else
  77. msr_d.r_maxsize = msgsz;
  78. msr_d.r_msg = ERR_PTR(-EAGAIN);
  79. current->state = TASK_INTERRUPTIBLE;
  80. msg_unlock(msqid);//解鎖並排程
  81. //當前進程一旦睡下,以下需要等待進程通過pipelined_send()向其傳送報文,並且選擇這個進程作為接收進程才會被喚醒
  82. schedule();
  83. current->state = TASK_RUNNING;
  84. msg =(struct msg_msg*) msr_d.r_msg;
  85. if(!IS_ERR(msg))//表示已經成功接收
  86. goto out_success;
  87. //以下是因為緩衝區太小,喚醒了睡眠進程依舊無法接收,而是被信號喚醒的錯誤處理
  88. t = msg_lock(msqid);//對報文加鎖,隱藏著等待,可能被其他進程搶先鎖住該佇列
  89. if(t==NULL)
  90. msqid=-1;
  91. msg =(struct msg_msg*)msr_d.r_msg;
  92. if(!IS_ERR(msg)){//在鎖住佇列之前,還有可能接收到其他進程pipelined_send發來的報文
  93. /* our message arived while we waited for
  94. * the spinlock. Process it.
  95. *///所以還需要檢查下是否成功接收到報文
  96. if(msqid!=-1)
  97. msg_unlock(msqid);
  98. goto out_success;
  99. }
  100. err = PTR_ERR(msg);
  101. if(err ==-EAGAIN){//要將本進程的msg_receiver結構拖鏈,並且看是否有信號處理
  102. if(msqid==-1)
  103. BUG();
  104. list_del(&msr_d.r_list);
  105. if(signal_pending(current))//如果沒有信號處理,則跳轉到retry重新開始
  106. err=-EINTR;
  107. else
  108. goto retry;
  109. }
  110. }
  111. out_unlock:
  112. if(msqid!=-1)
  113. msg_unlock(msqid);
  114. return err;
  115. }
msgctl()報文機制的控制與設定
管道的缺點在於缺乏對管道的控制手段,也缺乏獲取其狀態的資訊(例如,有多少個位元組已經在管道中等待讀取)的手段,msgctl就是為報文佇列提供了這樣的手段
  1. long sys_msgctl(int msqid,int cmd,struct msqid_ds*buf);
cmd為具體的操作碼
  1. /*
  2. * Control commands used with semctl, msgctl and shmctl
  3. * see also specific commands in sem.h, msg.h and shm.h
  4. */
  5. #define IPC_RMID 0/* remove resource關閉報文佇列 */
  6. #define IPC_SET 1/* set ipc_perm options 改變ipc設施的相關狀態與屬性*/
  7. #define IPC_STAT 2/* get ipc_perm options 獲取狀態*/
  8. #define IPC_INFO 3/* see ipcs 統計資訊*/
以上命令碼並不是專門為報文佇列設定,也適用於sysv ipc的其他兩種機制,對於具體的機制還可能補充其他專用命令.
msg專用命令
  1. /* ipcs ctl commands */
  2. #define MSG_STAT 11
  3. #define MSG_INFO 12
呼叫引數buf是一個msgid_ds結構指標,此結構適用於ipc_stat與ipc_set.
  1. /* Obsolete, used only for backwards compatibility and libc5 compiles */
  2. struct msqid_ds {
  3. struct ipc_perm msg_perm;
  4. struct msg *msg_first;/* first message on queue,unused */
  5. struct msg *msg_last;/* last message in queue,unused */
  6. __kernel_time_t msg_stime;/* last msgsnd time */
  7. __kernel_time_t msg_rtime;/* last msgrcv time */
  8. __kernel_time_t msg_ctime;/* last change time */
  9. unsignedlong msg_lcbytes;/* Reuse junk fields for 32 bit */
  10. unsignedlong msg_lqbytes;/* ditto */
  11. unsignedshort msg_cbytes;/* current number of bytes on queue */
  12. unsignedshort msg_qnum;/* number of messages in queue */
  13. unsignedshort msg_qbytes;/* max number of bytes on queue */
  14. __kernel_ipc_pid_t msg_lspid;/* pid of last msgsnd */
  15. __kernel_ipc_pid_t msg_lrpid;/* last receive pid */
  16. };
當命令為ipc_info時,指向一個msginfo結構
  1. /* buffer for msgctl calls IPC_INFO, MSG_INFO */
  2. struct msginfo {
  3. int msgpool;
  4. int msgmap;
  5. int msgmax;
  6. int msgmnb;
  7. int msgmni;
  8. int msgssz;
  9. int msgtql;
  10. unsignedshort msgseg;
  11. };
 
  1. asmlinkage long sys_msgctl (int msqid,int cmd,struct msqid_ds *buf)
  2. {
  3. int err, version;
  4. struct msg_queue *msq;
  5. struct msq_setbuf setbuf;
  6. struct kern_ipc_perm *ipcp;
  7. if(msqid <0|| cmd <0)
  8. return-EINVAL;
  9. version = ipc_parse_version(&cmd);//判斷是64位元版本還是32位元版本
  10. switch(cmd){//根據不同型別選擇不同操作
  11. case IPC_INFO://合在一起
  12. case MSG_INFO:
  13. {
  14. struct msginfo msginfo;
  15. int max_id;
  16. if(!buf)
  17. return-EFAULT;
  18. /* We must not return kernel stack data.
  19. * due to padding, it's not enough
  20. * to set all member fields.
  21. */
  22. memset(&msginfo,0,sizeof(msginfo));
  23. msginfo.msgmni = msg_ctlmni;
  24. msginfo.msgmax = msg_ctlmax;
  25. msginfo.msgmnb = msg_ctlmnb;
  26. msginfo.msgssz = MSGSSZ;
  27. msginfo.msgseg = MSGSEG;
  28. down(&msg_ids.sem);
  29. if(cmd == MSG_INFO){
  30. msginfo.msgpool = msg_ids.in_use;
  31. msginfo.msgmap = atomic_read(&msg_hdrs);
  32. msginfo.msgtql = atomic_read(&msg_bytes);
  33. }else{
  34. msginfo.msgmap = MSGMAP;
  35. msginfo.msgpool = MSGPOOL;
  36. msginfo.msgtql = MSGTQL;
  37. }
  38. max_id = msg_ids.max_id;
  39. up(&msg_ids.sem);
  40. if(copy_to_user (buf,&msginfo,sizeof(struct msginfo)))//從核心拷貝到使用者空間
  41. return-EFAULT;
  42. return(max_id <0)?0: max_id;
  43. }
  44. case MSG_STAT://stat狀態相關操作
  45. case IPC_STAT:
  46. {
  47. struct msqid64_ds tbuf;
  48. int success_return;
  49. if(!buf)
  50. return-EFAULT;
  51. if(cmd == MSG_STAT && msqid > msg_ids.size)
  52. return-EINVAL;
  53. memset(&tbuf,0,sizeof(tbuf));
  54. msq = msg_lock(msqid);
  55. if(msq == NULL)
  56. return-EINVAL;
  57. if(cmd == MSG_STAT){
  58. success_return = msg_buildid(msqid, msq->q_perm.seq);
  59. }else{
  60. err =-EIDRM;
  61. if(msg_checkid(msq,msqid))//id檢查
  62. goto out_unlock;
  63. success_return =0;
  64. }
  65. err =-EACCES;
  66. if(ipcperms (&msq->q_perm, S_IRUGO))//許可權判斷
  67. goto out_unlock;
  68. kernel_to_ipc64_perm(&msq->q_perm,&tbuf.msg_perm);
  69. tbuf.msg_stime = msq->q_stime;
  70. tbuf.msg_rtime = msq->q_rtime;
  71. tbuf.msg_ctime = msq->q_ctime;
  72. tbuf.msg_cbytes = msq->q_cbytes;
  73. tbuf.msg_qnum = msq->q_qnum;
  74. tbuf.msg_qbytes = msq->q_qbytes;
  75. tbuf.msg_lspid = msq->q_lspid;
  76. tbuf.msg_lrpid = msq->q_lrpid;
  77. msg_unlock(msqid);
  78. if(copy_msqid_to_user(buf,&tbuf, version))//從核心拷貝到使用者
  79. return-EFAULT;
  80. return success_return;
  81. }
  82. case IPC_SET://set命令操作
  83. if(!buf)
  84. return-EFAULT;
  85. if(copy_msqid_from_user (&setbuf, buf, version))//從使用者拷貝到核心
  86. return-EFAULT;
  87. break;
  88. case IPC_RMID:
  89. break;
  90. default:
  91. return-EINVAL;
  92. }
  93. down(&msg_ids.sem);
  94. msq = msg_lock(msqid);
  95. err=-EINVAL;
  96. if(msq == NULL)
  97. goto out_up;
  98. err =-EIDRM;
  99. if(msg_checkid(msq,msqid))
  100. goto out_unlock_up;
  101. ipcp =&msq->q_perm;
  102. err =-EPERM;
  103. if(current->euid != ipcp->cuid &&
  104. current->euid != ipcp->uid &&!capable(CAP_SYS_ADMIN))//是否有許可權操作
  105. /* We _could_ check for CAP_CHOWN above, but we don't */
  106. goto out_unlock_up;
  107. switch(cmd){
  108. case IPC_SET:
  109. {
  110. if(setbuf.qbytes > msg_ctlmnb &&!capable(CAP_SYS_RESOURCE))
  111. goto out_unlock_up;
  112. msq->q_qbytes = setbuf.qbytes;
  113. ipcp->uid = setbuf.uid;
  114. ipcp->gid = setbuf.gid;
  115. ipcp->mode =(ipcp->mode &~S_IRWXUGO)|
  116. (S_IRWXUGO & setbuf.mode);
  117. msq->q_ctime = CURRENT_TIME;
  118. /* sleeping receivers might be excluded by
  119. * stricter permissions.
  120. */
  121. expunge_all(msq,-EAGAIN);//使所有正在等待此佇列接收報文的進程都出錯返回
  122. /* sleeping senders might be able to send
  123. * due to a larger queue size.
  124. */
  125. ss_wakeup(&msq->q_senders,0);//將所有正在等待此佇列傳送報文的進程都喚醒,進行新一輪嚐試
  126. msg_unlock(msqid);
  127. break;
  128. }
  129. case IPC_RMID:
  130. freeque (msqid);//將所有正在等待的傳送還是接收的進程全部喚醒,讓其出錯返回
  131. break;
  132. }
  133. err =0;
  134. out_up:
  135. up(&msg_ids.sem);
  136. return err;
  137. out_unlock_up:
  138. msg_unlock(msqid);
  139. goto out_up;
  140. out_unlock:
  141. msg_unlock(msqid);
  142. return err;
  143. }
ipc_rmid對應操作:freeque (msqid); //將所有正在等待的傳送還是接收的進程全部喚醒,讓其出錯返回
  1. staticvoid freeque (int id)
  2. {
  3. struct msg_queue *msq;
  4. struct list_head *tmp;
  5. msq = msg_rmid(id);
  6. expunge_all(msq,-EIDRM);//將所有讀寫的從連結串列拖鏈,讓其出錯返回
  7. ss_wakeup(&msq->q_senders,1);//喚醒
  8. msg_unlock(id);
  9. tmp = msq->q_messages.next;
  10. while(tmp !=&msq->q_messages){//將所有報文釋放
  11. struct msg_msg* msg = list_entry(tmp,struct msg_msg,m_list);
  12. tmp = tmp->next;
  13. atomic_dec(&msg_hdrs);
  14. free_msg(msg);
  15. }
  16. atomic_sub(msq->q_cbytes,&msg_bytes);
  17. kfree(msq);//是否佇列頭
  18. }

 


IT145.com E-mail:sddin#qq.com