SlideShare una empresa de Scribd logo
1 de 29
Linux 源代码分析报告




Linux 源代码分析


内容: msg.c

作者:欧阳杨

单位:浙江大学混合 974


 1999 年 12 月 10 日于求是园




          Page1
Linux 源代码分析报告




Linux 源代码分析 ----ipc/msg.c


 前言:
    在操作系统中, 有些进程存在着相互制约的关系,这些

制约关系来源于并行进程的相互合作和资源共享。为了使合

作进程和资源共享进程能协调一致的向前推进,必须使他

们保持联系,一边相互了解。进程相互间需要交换一定数量

的信息, 以便协调一致共同完成指定的任务. 这种机制就叫

做进程间通信,或 IP C . 在 linux 中支持 UNIX S Y S T EM V

的三种通信机制 : 消息队列 , 信号量和共享内存 . 现就消息

队列这种机制进行分析.

 包含的头文件:
    #include <linux/malloc.h>
    #include <linux/msg.h>
    #include <linux/interrupt.h>
    #include <linux/smp_lock.h>
    #include <linux/init.h>
    #include <asm/uaccess.h>
   m s g . c 中包含的函数模块:
                                   Page2
Linux 源代码分析报告




 有关进程间通信资源的属性:
         键 (key): 一个由用户提供的整数,用来标志某个消息。

         创 建 者 (creator): 创 建 这 个 消 息 的 进 程 的 用 户 ID(UID )

          和组 ID(GID).

         所有者 (owner): 消息所有者的 UID 和 GID. 资源创建时 ,

          资源的创建者就是资源的所有者。资源的创建者进程、

          当前的所有者进程和超级用户具有改变资源所有者的

          权力。

 参数类型的说明:
     1.   struct ipc_perm
          {
            key_t key; // 整型 , 0 表示 private, 非 0 表示 public

              ushort uid; // 资源拥有者的有效标识

              ushort gid; // 资源拥有者所在组的有效标识

              ushort cuid; // 资源创建者的有效标识

              ushort cgid; // 资源创建者所在组的有效标识

              ushort mode; // 访问模式

              ushort seq; // 序列号 , 计算标识符
];

            系统在创建消息队列的同时设定了访问权限 , 并返

     回一个标识. 进程通信时必须先传递该标识, 待函数

     i p c p er m s ( ) 确 认 权 限 后 才 可 以 访 问 通 信 资 源 . 访 问 权 限

     由 ip c_ p erm 结 构 描 述 . 通 过 key 可 以 得 到 引 用 标 识 ,


                                Page3
Linux 源代码分析报告




从而访问 通信资源. Key 为 pu bli c , 则任何进程都可以

通过 key 得到引用标识.
2.     struct msg
  {
           struct msg *msg_next; //消息队列中的下一个

       long msg_type;        //消息的类型

       char *msg_spot;       //存放消息内容的地址

       time_t msg_time;      //消息发送的时间

       short msg_ts;      //消息的长度
  };

m s g 结构用来存放消息的有关信息.
3.     struct msqid_ds
       {
           struct ipc_perm msg_perm;
         struct msg *msg_first;      //指向消息队列的第一条消息

         struct msg *msg_last;       //指向消息队列的最后一条消息

         time_t msg_stime;         // 最后发送时间

         time_t msg_rtime;         //最后接收时间

         time_t msg_ctime;         //最后修改时间

         struct wait_queue *wwait;        //写消息进程的等待队列

         struct wait_queue *rwait;    //读消息进程的等待队列

         ushort msg_cbytes;        //队列中消息的字节数

         ushort msg_qnum;          //队列中的消息数

         ushort msg_qbytes;        //队列中消息的最大字节数

         ushort msg_lspid;        // 最后一个发送消息的进程的标识号

                                  Page4
Linux 源代码分析报告




        ushort msg_lrpid;   //最后一个接收消息的进程的标识号
   };

         每一个 m s qid_d s 结构代表一个消息队列, 是进

   程读写的信息的存储空间。

static struct msqid_ds *msgque[MSGMNI];

         定义了一个消息队列数组 m s g q u e , 数组的元素类

   型是指向 m s qid_d s 结构的指针。消息在队列中是按

   到来的顺序维护。进程读消息时,这些消息按 FIFO

   从队列中移去。



   msgque            msgid_ds      msg           msg
                     msg_perms   msg_next   msg_next
                     msg_first                msg_type
                     msg_last                 msg_spot
                                              msg_stime
                                              msg_ts

                                              message




                            图: Linux 消息队列



   以下是消息传递的示意图:




                             Page5
Linux 源代码分析报告




                                    Struct msgqid_ds
             receiver                                          sender




                            msg           msg          msg




             receiver
                                                               sender



    4.        struct msgbuf
              {
                    long mtype;        //消息的类型

                   char mtext[1];      //消息的内容
              };

                   存放消息的信息。
    5.       struct wait_queue
         {
                        struct wait_queue *next; //指针指向等待队列的下一个
                    struct task_struct *task;
                    /*task_struct 存放的是进程控制块的信息 */

         };

             wait_queu e 代表各种各样的进程等待队列。

    初始化变量:
             static int msgbytes = 0; //代表消息的字节数
             static int msghdrs = 0;
             static unsigned short msg_seq = 0; //代表消息的序列号

             static int used_queues = 0; //代表使用的消息数



                                          Page6
Linux 源代码分析报告




         static int max_msqid = 0;    //代表最大的消息序列号
         static struct wait_queue *msg_lock = NULL;

  函数列表:
 1.  void msg_init(void)
 2.  static int real_msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int
     msgflg)
 3. static int real_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz,
     long msgtyp, int msgflg)
 4. asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t
     msgsz, int msgflg)
 5. asmlinkage int sys_msgrcv(int msqid, struct msgbuf *msgp, size_t
     msgsz,
 6. static int findkey(key_t key)
 7. static int newque (key_t key, int msgflg)
 8. asmlinkage int sys_msgget(key_t key, int msgflg)
 9. static void free_que (int id)
 10. asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)

 函数模块的说明与分析:
 1. 初始化模块:

      void __init msg_init (void)
 函数参数:无
 函数返回类型:空
 函数功能:对消息队列及各变量初始化
 {
   int id;
       for (id = 0; id < MSGMNI; id++)
           msgque [id] = (struct msqid_ds *) IPC_UNUSED;


        /* 给 指 向 消 息 队 列 的 指 针 分 配 空 间 , 标 志 为 IPC_UNUSED, 定

 义见
   linux/ipc.h : #define IPC_UNUSED ((void ) -1)*/

        msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
        msg_lock = NULL;
        return;
 }

2. 发送消息模块:

                                     Page7
Linux 源代码分析报告




      static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t

       msgsz, int msgflg)
 函数参数:
     msqid: 整型, 消息的序列号;

     msgp: 指向 msgbuf 结构的指针;

     msgsz: 消息的大小;

     msgflg: 消息的标志符。

     函 数 返 回 类 型 : 若 消 息 正 确 的 放 入 消 息 队 列 , 则 返 回 0, 否 则 返
回各种    错误信息。详情请看 返回错误类型一览表。
 函 数功 能: 将 msgp 所 指向 的消 息缓 冲区 的内 容放 入消 息队 列中 ,
 且将标志号设为 msqid。其他调用可通过 msqid 访问消息队列。

 {
        int id;
         struct msgque*msq;
         struct ipc_perm*ipcp;
         struct msg*msgh;
         long mtype;

        if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)


      /* 见 msg.h 中 #define MSGMAX 4056 消息的最大字节数,

       消息的大小不符和要求或无序列号, 则出错 */


         return -EINVAL;
        if (get_user(mtype, &msgp->mtype))
          /*用户获取消息的类型,正常返回 0*/
             return -EFAULT;
        if (mtype < 1) //若消息类型 <1,则用户无法发送此消息
             return -EINVAL;
        id = (unsigned int) msqid % MSGMNI;



                                 Page8
Linux 源代码分析报告




      /*见 msg.h 中 #define MSGMNI 128 消息队列的最大序列号

       id 即消息队列中对应的下标 */


           msq = msgque [id];
           if (msq == IPC_UNUSED || msq == IPC_NOID)


/*特殊的 shmsegs[id], msgque[id] or semary[id]值 。见 ipc.h

    #define IPC_UNUSED ((void *) -1) //初始化时设置
    #define IPC_NOID ((void *) -2) */

              return -EINVAL;
         ipcp = &msq->msg_perm;
     slept:
         if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)


           /*将消息的序列号与 ipc_perm 中的进行比较,若不一致则表示

           此消
           息以被从消息队列中删去,返回出错信息 */
     return -EIDRM;

        /*调用函数 int ipcperms (struct ipc_perm *ipcp, short flag)
              进 程 通 信 时 先 传 递 flag
     if ( ipcperm(ipcp, S_IWUGO)) 标 识 , 然 后 由 此 函 数 确 认 访 问 通 信
        资源的权限。
  /*stat.h 中 S_IWUGO 则权限没有被确认。
           此函数若返回-1 定 义 为 S_IWUSR|S_IWURP|S_IWOTH, 即
            { int requested_mode, granted_mode;
                requested_mode = (flag >> 6) | (flag >> 3) | flag; 00020 表示
00200|00020|00002。     其中 00200 表示资源创建者的写权限,
                granted_mode = ipcp->mode;
              if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
资源创建组的写权限, 00002 表示所有用户写权限 */
               /*各 id 号一致*/
             granted_mode >>= 6;
       else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
             return -EACCES;

               /*调 用 函 数 in_group_p, 见 /kernel/sys.c 判 断 资 源 拥 有 者 的 组
               号或资源创建者的组号是否存在*/
                 granted_mode >>= 3;
           if ((requested_mode & ~granted_mode & 0007) &&
             !capable(CAP_IPC_OWNER))
             /*判断是否有在 requested_mode 但不在 granted_mode 中的位 *
/
              return -1;
                                Page9
           return 0;}
     }*/
Linux 源代码分析报告




if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
         if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
           /* 如果队列中无足够空间 */
           if (msgflg & IPC_NOWAIT)
           /*msgflg 中 IPC_NOWAIT 置位,则进程不进入的等待队列,返

           回错误 EAGAIN*/
                 return -EAGAIN;
           if (signal_pending(current))
           /*如果此进程某个必须处理的信号已经到达,则返回 EINTR*/
            return -EINTR;
        interruptible_sleep_on (&msq->wwait);
           调用函数 void interruptible_sleep_on(struct wait_queue **p)
   /*调度其他等待队列上的进程,直到此进程被唤醒 */




     {
               SLEEP_ON_VAR
               /*见 sched.c 中 #define      SLEEP_ON_VAR
                               unsigned long flags;
                               struct wait_queue wait;      */


                                    Page10
Linux 源代码分析报告



             current->state = TASK_INTERRUPTIBLE;


             /*将当前任务的状态置为 TASK_INTERRUPTIBLE,表示处

             于             等待队列中的进程,待资源有效时唤醒,或
             由其它进程通过信号和定时中断唤醒后进入就绪队列 */


             SLEEP_ON_HEAD
             Schedule();   //对等待队列上的进程进行调度
             SLEEP_ON_TAIL
         }


     goto slept; /*重新发送 */
     }
}


/*以下分配消息的指针和存放内容的空间 */
msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL);
if (!msgh) /*系统无足够空间存放消息缓存区 */
   return -ENOMEM;
msgh->msg_spot = (char *) (msgh + 1);

if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz))


/*调用 copy_from_user, 将 msgz 大小的内容为 msgp->mtext

    放入 msgh->msg_spot 指向的空间,正常则返回 0*/


{
     kfree(msgh);
     /*调用函数 kfree,见 mm/slab.c,释放掉 msgh 的 空间 */
     return -EFAULT;
}

if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
     || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {


                               Page11
Linux 源代码分析报告




  /*msgque 的值为 IPC_UNUSED 或 IPC_NOID,或序列号不符,表示此

消息已从消息队列中删去 */


       kfree(msgh);
       return -EIDRM;
 }
/*设置 msgh 所指的消息的各项属性 */
 msgh->msg_next = NULL;
 msgh->msg_ts = msgsz;
 msgh->msg_type = mtype;
 msgh->msg_stime = CURRENT_TIME;


 if (!msq->msg_first) //消息队列中无成员
      msq->msg_first = msq->msg_last = msgh;
 else {
/*将 msgh 插入消息队列 msq*/
       msq->msg_last->msg_next = msgh;
       msq->msg_last = msgh;
 }
 /*msg 中加入 msgh 后,修改各项相应的属性 */
msq->msg_cbytes += msgsz;
 msgbytes += msgsz;
 msghdrs++;
 msq->msg_qnum++;
 msq->msg_lspid = current->pid;
 msq->msg_stime = CURRENT_TIME;
 wake_up (&msq->rwait);
/*调用函数 wake_up ,唤醒等待获得消息的进程 */




       调用函数 void wake_up_process(struct task_struct * p)
       {
       unsigned long flags;

     spin_lock_irqsave(&runqueue_lock, flags);
        spin_unlock_irqrestore(&runqueue_lock, flags);

  }                                Page12
Linux 源代码分析报告




          /* 调 用 函 数 spin_lock_irqsave , 将 以 下 部 分 上 锁 , 防 止 其 他 进 程 访

问 */

        p->state = TASK_RUNNING; //将进程设为运行状态
        if (!p->next_run) {
            add_to_runqueue(p); //将此任务加入运行队列
            reschedule_idle(p);
    }


               /*解除锁机制 */




return 0;
}


   asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t

    msgsz, int msgflg)
    函数参数:
        msqid: 整型, 消息的序列号;

        msgp: 指向 msgbuf 结构的指针;

        msgsz: 消息的大小;

        msgtyp: 消息的类型;

        msgflg: 消息的标志符。

    函数返回类型:有关汇编连接的类型
    函数功能:系统调用,发送消息
{
    int ret;

    lock_kernel();



                                  Page13
Linux 源代码分析报告




    /*调用函数 lock_kernel{ do{}while(0);}, 此函数用于多 cpu 处理,当一个

cpu 处理进程时,不允许其他 cpu 访问 */
    ret = real_msgsnd(msqid, msgp, msgsz, msgflg);
    unlock_kernel();
    /*调用函数 unlock_kernel{do{}while(0);},功能与 lock_kernel 相反 */
    return ret;
}

 返回错误类型一览表:
返回的类型               代表的意思
EACCES 发送消息的进程没有往消息队列写的权限。
EFAULT Msgp 所指的地址无法访问。
EIDRM  消息已被从消息队列中删去。
EINTR  消息队列已满,正在发送消息的进程获得某个信号。
EINVAL 错误的 msqid 或非正的 mtype,或错误的 msgsz。
ENOMEN 系统没有足够的空间给消息缓冲区。
EAGAIN 若消息队列已满,且 IPC _NOWAIT 置位。


                       返回

3 . 接收消息模块;
     static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t

        msgsz, long msgtyp, int msgflg)
    函数参数:
     msqid: 整型, 消息的序列号;

     msgp: 指向 msgbuf 结构的指针;

     msgsz: 消息的大小;

     msgtyp: 消息的类型;

     msgflg: 消息的标志符。

  函数返回类型:若接收正确,返回接收到的消息字节数。否则,返回
  错误信息,详情请看 返回错误一览表。
函数功能:从消息队列中接收标示号为 msqid,内容为 msgp 所指向的信
息。

                                    Page14
Linux 源代码分析报告




{
    struct msqid_ds*msq;
    struct ipc_perm *ipcp;
    struct msg *tmsg, *leastp = NULL;
    struct msg *nmsg = NULL;
    int id;


    if (msqid < 0 || (long) msgsz < 0) //消息的序列号或大小不符和要求
       return -EINVAL;

    id = (unsigned int) msqid % MSGMNI;
    msq = msgque [id];    //将消息队列中下标为 id 的成员赋给 msq
    if (msq == IPC_NOID || msq == IPC_UNUSED)
         return -EINVAL;
    ipcp = &msq->msg_perm;

    while (!nmsg) {
        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
     /*序列号不符,表示此消息已被删去 */
           return -EIDRM;
       }
       if (ipcperms (ipcp, S_IRUGO)) {


    /* stat.h 中 S_IRUGO 定 义 为 S_IRUSR|S_IRGRP|S_IROTH , 即 00400|

00040|00004。 00400 表示资源创建者的读权限, 00040 表示资源创建组的读

权限, 00004 表示所有用户的读权限。函数确认访问通信资源的权限 。此

函数若返回 -1 则权限没有被确认 */


           return -EACCES;

       }
    /*以下寻找类型正确的消息 */

       if (msgtyp == 0)   // msgtyp=0 取消息队列的第一个
           nmsg = msq->msg_first;

                                    Page15
Linux 源代码分析报告



     else if (msgtyp > 0) {
          if (msgflg & MSG_EXCEPT) {
         /* 若 msgtyp>0 且 MSG_EXCEPT 置 位 , 则 取 消 息 队 列 的 第 一 个

         类型不符和的 */
              for (tmsg = msq->msg_first; tmsg;
                  tmsg = tmsg->msg_next)
                   if (tmsg->msg_type != msgtyp)
                        break;
              nmsg = tmsg;
         } else {
         /*否则取第一个类型符合的 */
                for (tmsg = msq->msg_first; tmsg;
                   tmsg = tmsg->msg_next)
                     if (tmsg->msg_type == msgtyp)
                          break;
                nmsg = tmsg;
          }
     } else {
/* msgtyp<0 取 type 值小于 msgtyp 的绝对值中最小的一个 */
         for (leastp = tmsg = msq->msg_first; tmsg;
             tmsg = tmsg->msg_next)
               if (tmsg->msg_type < leastp->msg_type)
                    leastp = tmsg;
         if (leastp && leastp->msg_type <= - msgtyp)
               nmsg = leastp;
     }

     if (nmsg) {
         /* 确实找到了符合条件的消息 */
         if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {


         /* 若 找 到 的 消 息 大 小 超 过 允 许 的 最 大 长 度 且 msgflg 和

         MSG_NOERROR 没有置位, 则溢出 */


            return -E2BIG;
         }
         msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;

                                    Page16
Linux 源代码分析报告




        /*若 msgflg 和 MSG_NOERROR 置位,则取消息的前 msgsz 个字

         节 */


    /*以下处理消息取出后队列的调整 */

     if (nmsg == msq->msg_first) //取出的消息为第一个
              msq->msg_first = nmsg->msg_next;
         else {
              for (tmsg = msq->msg_first; tmsg;
                  tmsg = tmsg->msg_next)
                   if (tmsg->msg_next == nmsg)
                        break;
              tmsg->msg_next = nmsg->msg_next;
              if (nmsg == msq->msg_last)
                   msq->msg_last = tmsg;
         }
         if (!(--msq->msg_qnum)) //若取出消息后,消息队列中无剩余消

息
            msq->msg_last = msq->msg_first = NULL;
         /*设置改动后,各变量的值 */
         msq->msg_rtime = CURRENT_TIME;
         msq->msg_lrpid = current->pid;
         msgbytes -= nmsg->msg_ts;
         msghdrs--;
         msq->msg_cbytes -= nmsg->msg_ts;
         wake_up (&msq->wwait);


       /*调用函数 wake_up, 唤醒等待发送消息的进程 */


         if (put_user (nmsg->msg_type, &msgp->mtype) ||
            copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz))


         /* 调 用 copy_to_user, 将 msgz 大 小 的 内 容 为 msgp->mtext 放 入

         msgh->msg_spot 指向的空间,正常则返回 0*/



                                Page17
Linux 源代码分析报告



                msgsz = -EFAULT;
            kfree(nmsg);
            return msgsz;
        } else { /*若没有找到符合的消息 */
            if (msgflg & IPC_NOWAIT) {
            /*若 IPC_NOWAIT 置位,则进程不进入等待队列,返回错误信

息 */
                 return -ENOMSG;
            }
            if (signal_pending(current)) {


            /*调用 函数 sigal _pending, 判断 若当 前进 程所 需的 信号 已到 且

进程
            未被堵塞 */


                 return -EINTR;
            }
            interruptible_sleep_on (&msq->rwait);


            /*调用函数 interruptible_sleep_on,对等待发送消息的队列中的

进
            程进行调度 */
        }
    }
    return -1;
}



       asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp,

        size_t msgsz, long msgtyp, int msgflg)
    函数参数:
       msqid: 整型, 消息的序列号;

       msgp: 指向 msgbuf 结构的指针;

                                      Page18
Linux 源代码分析报告




     msgsz: 消息的大小;

     msgtyp: 消息的类型;

     msgflg: 消息的标志符。

    函数返回类型:有关汇编链接的类型
    函数功能:系统调用,获得消息
{
    int ret;


    lock_kernel(); //含义与 sys_msgsnd 相同
    ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);
    unlock_kernel();
    return ret;
}
 返回错误类型一览表;
返回类型                        代表的意思
E2BIG  消息大小超过 msgsz,且 msgflg 中 MSG_NOERROR 没有置位
EACCES 接收消息的进程没有从消息队列读的权限。
EFAULT Msgp 所指的地址无法访问。
EIDRM  当进程处于 sleep 状态时,消息已被从消息队列中删去。
EINTR  当进程处于 sleep 状态时,消息队列已满,正在接收消息的进程获
       得某个信号。
EINVAL 错误的 msqid 或非正的 mtype,或错误的 msgsz。
ENOMSG Msgflg 中 IPC_NOWAIT 置位,且消息队列中没有需要的消息。


                            返回


4 . 创建或获得消息的控制模块:
     asmlinkage int sys_msgget (key_t key, int msgflg)
    函数参数:
      key:代表访问通信资源的权限

     msgflg:标志位。

    函数返回类型:整型
    函数功能:传递键值,标志位以及其他的参数,获得消息。
{
    int id, ret = -EPERM;

                                    Page19
Linux 源代码分析报告



    struct msqid_ds *msq;

    lock_kernel();
    if (key == IPC_PRIVATE)


/*若键值为 IPC_RPIVATE,则创建一个新的消息队列,此消息队列不能通

过 其他 get 调用访问。调用者有独占权,通过 fork 系统调用,子进程继承

这个消息对俄,属主可以和它的子进程共享 */


         ret = newque(key, msgflg);
    else if ((id = findkey (key)) == -1) {
        /*没有找到相应的 key */
        if (!(msgflg & IPC_CREAT))
        /*IPC_CREAT 置位表示创建尚不存在的新的消息 */
               ret = -ENOENT;
        else
               ret = newque(key, msgflg);
    } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {


    /*若 msgflg 中 IPC_CREAT 置位且相应的消息已存在,而且 IPC_EXCL

置位,返回错误 */
         ret = -EEXIST;
    } else {
    /*若没有制定标志位,则内核试着查找有相同键值的已经存在的消息 *
/
        msq = msgque[id];
        if (msq == IPC_UNUSED || msq == IPC_NOID)//消息不存在
               ret = -EIDRM;
        else if (ipcperms(&msq->msg_perm, msgflg)) //权限被否认
               ret = -EACCES;
        else
               ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
    /*取得该消息的 ID 号 */

                                         Page20
Linux 源代码分析报告



        }
    unlock_kernel();
    return ret;
}


5. 消息传递的控制模块:

     asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds

        *buf)
函数参数:
    msqid:消息序列号

    cmd:处理消息的命令

    buf:指向 msqid_ds 的指针

函 数 返 回 类 型 : 有 关 汇 编 连 接 的 类 型 , 有 关 错 误 返 回 的 信 息 请 看 返回错误
类型一览表。
函数功能: 系统调用。处理各种有关消息处理的命令,详情请看命令览
表
{
   int id, err = -EINVAL;
   struct msqid_ds*msq;
   struct msqid_dstbuf;
   struct ipc_perm*ipcp;

    lock_kernel();
    if (msqid < 0 || cmd < 0)
         goto out;
    err = -EFAULT;
    switch (cmd) {


/*开始处理命令 */


    case IPC_INFO:


    /*IPC_INFO 输出 msginfo 结构中有关消息队列的相关值的最大值 */


    case MSG_INFO:


                                Page21
Linux 源代码分析报告




/*MSG_INFO 输 出 的 与 ICP_INFOU 有 所 不 同 , 它 给 出 msgpool 中 使 用

过 的 等 待 队 列 的 数 目 , msgmap 中 消 息 的 数 目 和 系 统 存 储 在 msgtql 中

的总的消息数 */


    if (!buf)
         goto out;
{
/ 得到消息的数量信息 */
         struct msginfo msginfo;
    msginfo.msgmni = MSGMNI;
    msginfo.msgmax = MSGMAX;
    msginfo.msgmnb = MSGMNB;
    msginfo.msgmap = MSGMAP;
    msginfo.msgpool = MSGPOOL;
    msginfo.msgtql = MSGTQL;
    msginfo.msgssz = MSGSSZ;
    msginfo.msgseg = MSGSEG;
    if (cmd == MSG_INFO) {
         msginfo.msgpool = used_queues;
         msginfo.msgmap = msghdrs;
         msginfo.msgtql = msgbytes;
    }

    err = -EFAULT;
    if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))


    /*调用函数 copy_to_user 将 msginfo 所指的内容放入 buf 中 */


        goto out;
    err = max_msqid;
    goto out;
}
case MSG_STAT:


/* MSG_STAT 变量允许参数传递系统内部消息队列表的索引 */


    if (!buf)
         goto out;

                                  Page22
Linux 源代码分析报告



        err = -EINVAL;
        if (msqid > max_msqid)
             goto out;
        msq = msgque[msqid];
        if (msq == IPC_UNUSED || msq == IPC_NOID)
             goto out;
        err = -EACCES;
        if (ipcperms (&msq->msg_perm, S_IRUGO)) //权限被否认
            goto out;
        id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
    /*设置 tbuf 的各项值 */
        tbuf.msg_perm = msq->msg_perm;
        tbuf.msg_stime = msq->msg_stime;
        tbuf.msg_rtime = msq->msg_rtime;
        tbuf.msg_ctime = msq->msg_ctime;
        tbuf.msg_cbytes = msq->msg_cbytes;
        tbuf.msg_qnum = msq->msg_qnum;
        tbuf.msg_qbytes = msq->msg_qbytes;
        tbuf.msg_lspid = msq->msg_lspid;
        tbuf.msg_lrpid = msq->msg_lrpid;
        err = -EFAULT;
        if (copy_to_user (buf, &tbuf, sizeof(*buf)))
    /*将 tbuf 的内容拷贝 buf 中去 */
            goto out;
        err = id;
        goto out;
    case IPC_SET:


    /*IPC_SET 允许消息队列的拥有者,模式和最大允许的字节数被改变 *
/

        if (!buf)
              goto out;
        err = -EFAULT;
        if (!copy_from_user (&tbuf, buf, sizeof (*buf)))
              err = 0;
        break;
    case IPC_STAT:



                                      Page23
Linux 源代码分析报告




   /*IPC_STAT 将 有 关 的 消 息 队 列 的 msqid_id 结 构 拷 贝 到 用 户 的 存 储 区

域 */


       if (!buf)
            goto out;
       break;
   }


   id = (unsigned int) msqid % MSGMNI; // 获得消息的 ID 号
   msq = msgque [id];
   err = -EINVAL;
   if (msq == IPC_UNUSED || msq == IPC_NOID) //消息不存在
       goto out;
   err = -EIDRM;
   if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) //权限被否认
       goto out;
   ipcp = &msq->msg_perm;

   switch (cmd) {
   case IPC_STAT:
       err = -EACCES;
       if (ipcperms (ipcp, S_IRUGO)) //确认访问权限
           goto out;
   /*IPC_STAT 将 有 关 的 消 息 队 列 的 msqid_id 结 构 拷 贝 到 用 户 的 存 储 区

域 */
       tbuf.msg_perm = msq->msg_perm;
       tbuf.msg_stime = msq->msg_stime;
       tbuf.msg_rtime = msq->msg_rtime;
       tbuf.msg_ctime = msq->msg_ctime;
       tbuf.msg_cbytes = msq->msg_cbytes;
       tbuf.msg_qnum = msq->msg_qnum;
       tbuf.msg_qbytes = msq->msg_qbytes;
       tbuf.msg_lspid = msq->msg_lspid;
       tbuf.msg_lrpid = msq->msg_lrpid;
       err = -EFAULT;
       if (!copy_to_user (buf, &tbuf, sizeof (*buf)))

                                     Page24
Linux 源代码分析报告



               err = 0;
           goto out;
       case IPC_SET:


       /*IPC_SET 允许消息队列的拥有者,模式和最大允许的字节数被改变 *
/

           err = -EPERM;
           if (current->euid != ipcp->cuid &&
              current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
              /* We _could_ check for CAP_CHOWN above, but we don't */
                goto out;
           if(tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE))
                goto out;
           msq->msg_qbytes = tbuf.msg_qbytes;
           ipcp->uid = tbuf.msg_perm.uid;
           ipcp->gid = tbuf.msg_perm.gid;
           ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
                (S_IRWXUGO & tbuf.msg_perm.mode);
           msq->msg_ctime = CURRENT_TIME;
           err = 0;
           goto out;
       case IPC_RMID:


       /*IPC_RMID 允许超级用户和消息队列的创建者或拥有者删除队列 */


           err = -EPERM;
           if (current->euid != ipcp->cuid &&
              current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
           /*无删除的权限 */
                          goto out;


           freeque (id); //删除此队列
           err = 0;
           goto out;
       default:
           err = -EINVAL;
           goto out;
       }
out:

                                      Page25
Linux 源代码分析报告



     unlock_kernel();
     return err;
}
 返回错误类型一览表:
错误类型 代表的意思
EDIRM  将消息删去
EINVAL 错误的 cmd 或 msgqid
EPERM  Cmd 参数值为 IPC_SET 或 IPC_RMID,但是,调用的进程的 有
       效的 UID 没有足够的权限执行此项命令.


                                    返回


6.   其他相关的函数:

      static int findkey (key_t key)

     函数参数: key:代表访问通信资源的权限

     函数返回类型:找到了者返回 id 号,否则返回 -1;

     函数功能:找到所需的 key 值


{
     int id;
     struct msqid_ds*msq;

     for (id = 0; id <= max_msqid; id++) {
          while ((msq = msgque[id]) == IPC_NOID)
               interruptible_sleep_on(&msg_lock);
          if (msq == IPC_UNUSED)
               continue;
         if (key == msq->msg_perm.key) //找到相应的 key 值
             return id;
     }
     return -1;
}


      static int newque (key_t key, int msgflg)
     函数参数:


                                     Page26
Linux 源代码分析报告




          key:代表访问通信资源的权限

          msgflg:消息的标志符

      函数返回类型:整型
      函数功能:寻找可获得的消息队列资源。
{
      int id;
      struct msqid_ds *msq;
      struct ipc_perm *ipcp;

      for (id = 0; id < MSGMNI; id++)
           if (msgque[id] == IPC_UNUSED) {
                msgque[id] = (struct msqid_ds ) IPC_NOID;
               /*给消息队列置位为 IPC_NOID*/
               goto found;
           }
      return -ENOSPC; //没有空间可以分配


found:
    msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
      if (!msq) { //若分配空间失败
           msgque[id] = (struct msqid_ds ) IPC_UNUSED;
     /*置位为 IPC_UNUSED*/
           wake_up (&msg_lock);

      /*调用 wake_up 函数,唤醒被阻塞的队列上的进程 */
           return -ENOMEM;
      }
      /*设置 ipc_perm 结构的内容 */
      ipcp = &msq->msg_perm;
      ipcp->mode = (msgflg & S_IRWXUGO);
      ipcp->key = key;
      ipcp->cuid = ipcp->uid = current->euid;
      ipcp->gid = ipcp->cgid = current->egid;
/*    设置 msq 的各项内容 */
      msq->msg_perm.seq = msg_seq;
      msq->msg_first = msq->msg_last = NULL;

                                      Page27
Linux 源代码分析报告



    msq->rwait = msq->wwait = NULL;
    msq->msg_cbytes = msq->msg_qnum = 0;
    msq->msg_lspid = msq->msg_lrpid = 0;
    msq->msg_stime = msq->msg_rtime = 0;
    msq->msg_qbytes = MSGMNB;
    msq->msg_ctime = CURRENT_TIME;
    if (id > max_msqid)
          max_msqid = id;
    msgque[id] = msq; //将新成员加入消息队列
    used_queues++;
    wake_up (&msg_lock); //唤醒被阻塞的进程
    return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
}
 static void freeque (int id)

    函数参数: id 消息序列号
    函数返回类型:空
    函数功能:从 msgque 数组中删去下标为 id 的消息
{
    struct msqid_ds *msq = msgque[id];
    struct msg*msgp, *msgh;

    msq->msg_perm.seq++;
    msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI);


    /* 空闲的消息序列数增 1, 且避免溢出 */


    msgbytes -= msq->msg_cbytes;
    if (id == max_msqid)
          while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
    msgque[id] = (struct msqid_ds *) IPC_UNUSED;
    used_queues--; //使用的消息队列数减 1
    while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
        wake_up (&msq->rwait); //唤醒等待发送消息的队列上的进程

        wake_up (&msq->wwait);//唤醒等待获取消息的队列上的进程



                                     Page28
Linux 源代码分析报告




        schedule(); //对等待队列上的进程进行进程调度
    }
    for (msgp = msq->msg_first; msgp; msgp = msgh ) {
    /*释放掉 msq 所指的空间 */
        msgh = msgp->msg_next;
        msghdrs--;
        kfree(msgp);
    }
    kfree(msq);
}

 讨论:
       在消息传递机制中,当读取一个消息后,消息将从

        队列移去,其他进程不能读到。若因为接收的缓冲区

        太小造成消息被截断,截断的部分将永远丢失。
       进程必须通过带有 IPC_RMID 的 sys _msgctl 调用,来

        显示的删除消息队列。如果不是这样则消息队列可以

        长久的存在。则样就回导致系统很难判断,消息是为

        了将来进程访问而留下来还是被无意的抛弃了,或

        是由于想要释放它的进程不正常终止了,这样导致

        系统无限的保存这消息。如果这种情况经常发生,这

        种消息资源就会用光。
     总的说来,消息队列传递数据时是以一种不连续消

        息的方式,这样就可以更加灵活的处理数据。




                                   Page29

Más contenido relacionado

Destacado

Pellerey dimensione soggettiva
Pellerey dimensione soggettivaPellerey dimensione soggettiva
Pellerey dimensione soggettivaRenza Cambini
 
3. simple present tense 1
3. simple present tense 13. simple present tense 1
3. simple present tense 1Jorge Zavarce
 
Hpm solutions for industry, transportation and marine
Hpm solutions for industry, transportation and marineHpm solutions for industry, transportation and marine
Hpm solutions for industry, transportation and marineHPM Engineering srl
 
Urdu Tutor for Beginners
Urdu Tutor for BeginnersUrdu Tutor for Beginners
Urdu Tutor for BeginnersSaad Manzoor
 
Geography of Hong Kong
Geography of Hong KongGeography of Hong Kong
Geography of Hong Kongnvssleaders
 
Beleza.com presentation team f
Beleza.com presentation team fBeleza.com presentation team f
Beleza.com presentation team fUrusha Hada
 
L’estructura d’un ordinador 2 (1)
L’estructura d’un ordinador 2 (1)L’estructura d’un ordinador 2 (1)
L’estructura d’un ordinador 2 (1)AlbetaElisabeta
 
Presentation1
Presentation1Presentation1
Presentation1kk11711
 
INTRODUCTION TO Monarch Advisory Group
INTRODUCTION TO Monarch Advisory GroupINTRODUCTION TO Monarch Advisory Group
INTRODUCTION TO Monarch Advisory GroupTim Entwisle
 
Roca Groups presentation on BIMobject LIVe 2014
Roca Groups presentation on BIMobject LIVe 2014Roca Groups presentation on BIMobject LIVe 2014
Roca Groups presentation on BIMobject LIVe 2014BIMobject
 
BIMobject dkt oslo 2012
BIMobject dkt oslo 2012BIMobject dkt oslo 2012
BIMobject dkt oslo 2012BIMobject
 
Семейное образование: права и роль родителей в обучении детей
Семейное образование: права и роль родителей в обучении детейСемейное образование: права и роль родителей в обучении детей
Семейное образование: права и роль родителей в обучении детейfpolicy_ru
 

Destacado (20)

Montvale Volunteer Day March 2014
Montvale Volunteer Day March 2014Montvale Volunteer Day March 2014
Montvale Volunteer Day March 2014
 
Pellerey dimensione soggettiva
Pellerey dimensione soggettivaPellerey dimensione soggettiva
Pellerey dimensione soggettiva
 
3. simple present tense 1
3. simple present tense 13. simple present tense 1
3. simple present tense 1
 
Hpm solutions for industry, transportation and marine
Hpm solutions for industry, transportation and marineHpm solutions for industry, transportation and marine
Hpm solutions for industry, transportation and marine
 
Urdu Tutor for Beginners
Urdu Tutor for BeginnersUrdu Tutor for Beginners
Urdu Tutor for Beginners
 
Geography of Hong Kong
Geography of Hong KongGeography of Hong Kong
Geography of Hong Kong
 
Beleza.com presentation team f
Beleza.com presentation team fBeleza.com presentation team f
Beleza.com presentation team f
 
L’estructura d’un ordinador 2 (1)
L’estructura d’un ordinador 2 (1)L’estructura d’un ordinador 2 (1)
L’estructura d’un ordinador 2 (1)
 
Hjc higher ed
Hjc higher edHjc higher ed
Hjc higher ed
 
Presentation1
Presentation1Presentation1
Presentation1
 
INTRODUCTION TO Monarch Advisory Group
INTRODUCTION TO Monarch Advisory GroupINTRODUCTION TO Monarch Advisory Group
INTRODUCTION TO Monarch Advisory Group
 
03 2013 alumnesp3p4p5_sils
03 2013 alumnesp3p4p5_sils03 2013 alumnesp3p4p5_sils
03 2013 alumnesp3p4p5_sils
 
Grafica Active
Grafica ActiveGrafica Active
Grafica Active
 
Roca Groups presentation on BIMobject LIVe 2014
Roca Groups presentation on BIMobject LIVe 2014Roca Groups presentation on BIMobject LIVe 2014
Roca Groups presentation on BIMobject LIVe 2014
 
BIMobject dkt oslo 2012
BIMobject dkt oslo 2012BIMobject dkt oslo 2012
BIMobject dkt oslo 2012
 
Redes de emergencia
Redes de emergenciaRedes de emergencia
Redes de emergencia
 
Семейное образование: права и роль родителей в обучении детей
Семейное образование: права и роль родителей в обучении детейСемейное образование: права и роль родителей в обучении детей
Семейное образование: права и роль родителей в обучении детей
 
Harmony - We've Come a Long Way!
Harmony - We've Come a Long Way!Harmony - We've Come a Long Way!
Harmony - We've Come a Long Way!
 
Tizenについて
TizenについてTizenについて
Tizenについて
 
Examen final
Examen finalExamen final
Examen final
 

Similar a Linux 源代码分析 消息管理

MySQL源码分析.01.代码结构与基本流程
MySQL源码分析.01.代码结构与基本流程MySQL源码分析.01.代码结构与基本流程
MySQL源码分析.01.代码结构与基本流程Lixun Peng
 
Analysis on tcp ip protocol stack
Analysis on tcp ip protocol stackAnalysis on tcp ip protocol stack
Analysis on tcp ip protocol stackYueshen Xu
 
Message Queue Service
Message Queue ServiceMessage Queue Service
Message Queue ServiceSnoopyChen
 
MPI use c language
MPI use c languageMPI use c language
MPI use c languageZongYing Lyu
 
intro syslog syslogng
intro syslog syslogngintro syslog syslogng
intro syslog syslogngjuruntang
 
Syslog Ng
Syslog NgSyslog Ng
Syslog Ngflytod
 
syslog&syslog-ng
syslog&syslog-ngsyslog&syslog-ng
syslog&syslog-ngjurntang
 
Mysql proxy+mysql-mmm
Mysql proxy+mysql-mmmMysql proxy+mysql-mmm
Mysql proxy+mysql-mmmYiwei Ma
 
Mysql展示功能与源码对应
Mysql展示功能与源码对应Mysql展示功能与源码对应
Mysql展示功能与源码对应zhaolinjnu
 
Mysql 101014202926-phpapp01
Mysql 101014202926-phpapp01Mysql 101014202926-phpapp01
Mysql 101014202926-phpapp01Bob Huang
 
高效能執行緒
高效能執行緒高效能執行緒
高效能執行緒Rick Wu
 
希望科技研发部变量命名及编码规范
希望科技研发部变量命名及编码规范希望科技研发部变量命名及编码规范
希望科技研发部变量命名及编码规范Hongjian Wang
 
181201_CoAP_coding365
181201_CoAP_coding365181201_CoAP_coding365
181201_CoAP_coding365Peter Yi
 
Dm create message new
Dm create message newDm create message new
Dm create message newChris Wang
 
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack FirmwareSimen Li
 
Erlang开发及应用
Erlang开发及应用Erlang开发及应用
Erlang开发及应用litaocheng
 
Php extension开发
Php extension开发Php extension开发
Php extension开发thinkinlamp
 
MySQL协议分析
MySQL协议分析MySQL协议分析
MySQL协议分析ruoyi ruan
 
Kafka的设计与实现
Kafka的设计与实现Kafka的设计与实现
Kafka的设计与实现wang xing
 
2011 06-12-lamp-mysql-顾春江
2011 06-12-lamp-mysql-顾春江2011 06-12-lamp-mysql-顾春江
2011 06-12-lamp-mysql-顾春江thinkinlamp
 

Similar a Linux 源代码分析 消息管理 (20)

MySQL源码分析.01.代码结构与基本流程
MySQL源码分析.01.代码结构与基本流程MySQL源码分析.01.代码结构与基本流程
MySQL源码分析.01.代码结构与基本流程
 
Analysis on tcp ip protocol stack
Analysis on tcp ip protocol stackAnalysis on tcp ip protocol stack
Analysis on tcp ip protocol stack
 
Message Queue Service
Message Queue ServiceMessage Queue Service
Message Queue Service
 
MPI use c language
MPI use c languageMPI use c language
MPI use c language
 
intro syslog syslogng
intro syslog syslogngintro syslog syslogng
intro syslog syslogng
 
Syslog Ng
Syslog NgSyslog Ng
Syslog Ng
 
syslog&syslog-ng
syslog&syslog-ngsyslog&syslog-ng
syslog&syslog-ng
 
Mysql proxy+mysql-mmm
Mysql proxy+mysql-mmmMysql proxy+mysql-mmm
Mysql proxy+mysql-mmm
 
Mysql展示功能与源码对应
Mysql展示功能与源码对应Mysql展示功能与源码对应
Mysql展示功能与源码对应
 
Mysql 101014202926-phpapp01
Mysql 101014202926-phpapp01Mysql 101014202926-phpapp01
Mysql 101014202926-phpapp01
 
高效能執行緒
高效能執行緒高效能執行緒
高效能執行緒
 
希望科技研发部变量命名及编码规范
希望科技研发部变量命名及编码规范希望科技研发部变量命名及编码规范
希望科技研发部变量命名及编码规范
 
181201_CoAP_coding365
181201_CoAP_coding365181201_CoAP_coding365
181201_CoAP_coding365
 
Dm create message new
Dm create message newDm create message new
Dm create message new
 
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
 
Erlang开发及应用
Erlang开发及应用Erlang开发及应用
Erlang开发及应用
 
Php extension开发
Php extension开发Php extension开发
Php extension开发
 
MySQL协议分析
MySQL协议分析MySQL协议分析
MySQL协议分析
 
Kafka的设计与实现
Kafka的设计与实现Kafka的设计与实现
Kafka的设计与实现
 
2011 06-12-lamp-mysql-顾春江
2011 06-12-lamp-mysql-顾春江2011 06-12-lamp-mysql-顾春江
2011 06-12-lamp-mysql-顾春江
 

Linux 源代码分析 消息管理

  • 1. Linux 源代码分析报告 Linux 源代码分析 内容: msg.c 作者:欧阳杨 单位:浙江大学混合 974 1999 年 12 月 10 日于求是园 Page1
  • 2. Linux 源代码分析报告 Linux 源代码分析 ----ipc/msg.c  前言: 在操作系统中, 有些进程存在着相互制约的关系,这些 制约关系来源于并行进程的相互合作和资源共享。为了使合 作进程和资源共享进程能协调一致的向前推进,必须使他 们保持联系,一边相互了解。进程相互间需要交换一定数量 的信息, 以便协调一致共同完成指定的任务. 这种机制就叫 做进程间通信,或 IP C . 在 linux 中支持 UNIX S Y S T EM V 的三种通信机制 : 消息队列 , 信号量和共享内存 . 现就消息 队列这种机制进行分析.  包含的头文件: #include <linux/malloc.h> #include <linux/msg.h> #include <linux/interrupt.h> #include <linux/smp_lock.h> #include <linux/init.h> #include <asm/uaccess.h>  m s g . c 中包含的函数模块: Page2
  • 3. Linux 源代码分析报告  有关进程间通信资源的属性:  键 (key): 一个由用户提供的整数,用来标志某个消息。  创 建 者 (creator): 创 建 这 个 消 息 的 进 程 的 用 户 ID(UID ) 和组 ID(GID).  所有者 (owner): 消息所有者的 UID 和 GID. 资源创建时 , 资源的创建者就是资源的所有者。资源的创建者进程、 当前的所有者进程和超级用户具有改变资源所有者的 权力。  参数类型的说明: 1. struct ipc_perm { key_t key; // 整型 , 0 表示 private, 非 0 表示 public ushort uid; // 资源拥有者的有效标识 ushort gid; // 资源拥有者所在组的有效标识 ushort cuid; // 资源创建者的有效标识 ushort cgid; // 资源创建者所在组的有效标识 ushort mode; // 访问模式 ushort seq; // 序列号 , 计算标识符 ]; 系统在创建消息队列的同时设定了访问权限 , 并返 回一个标识. 进程通信时必须先传递该标识, 待函数 i p c p er m s ( ) 确 认 权 限 后 才 可 以 访 问 通 信 资 源 . 访 问 权 限 由 ip c_ p erm 结 构 描 述 . 通 过 key 可 以 得 到 引 用 标 识 , Page3
  • 4. Linux 源代码分析报告 从而访问 通信资源. Key 为 pu bli c , 则任何进程都可以 通过 key 得到引用标识. 2. struct msg { struct msg *msg_next; //消息队列中的下一个 long msg_type; //消息的类型 char *msg_spot; //存放消息内容的地址 time_t msg_time; //消息发送的时间 short msg_ts; //消息的长度 }; m s g 结构用来存放消息的有关信息. 3. struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; //指向消息队列的第一条消息 struct msg *msg_last; //指向消息队列的最后一条消息 time_t msg_stime; // 最后发送时间 time_t msg_rtime; //最后接收时间 time_t msg_ctime; //最后修改时间 struct wait_queue *wwait; //写消息进程的等待队列 struct wait_queue *rwait; //读消息进程的等待队列 ushort msg_cbytes; //队列中消息的字节数 ushort msg_qnum; //队列中的消息数 ushort msg_qbytes; //队列中消息的最大字节数 ushort msg_lspid; // 最后一个发送消息的进程的标识号 Page4
  • 5. Linux 源代码分析报告 ushort msg_lrpid; //最后一个接收消息的进程的标识号 }; 每一个 m s qid_d s 结构代表一个消息队列, 是进 程读写的信息的存储空间。 static struct msqid_ds *msgque[MSGMNI]; 定义了一个消息队列数组 m s g q u e , 数组的元素类 型是指向 m s qid_d s 结构的指针。消息在队列中是按 到来的顺序维护。进程读消息时,这些消息按 FIFO 从队列中移去。 msgque msgid_ds msg msg msg_perms msg_next msg_next msg_first msg_type msg_last msg_spot msg_stime msg_ts message 图: Linux 消息队列 以下是消息传递的示意图: Page5
  • 6. Linux 源代码分析报告 Struct msgqid_ds receiver sender msg msg msg receiver sender 4. struct msgbuf { long mtype; //消息的类型 char mtext[1]; //消息的内容 }; 存放消息的信息。 5. struct wait_queue { struct wait_queue *next; //指针指向等待队列的下一个 struct task_struct *task; /*task_struct 存放的是进程控制块的信息 */ }; wait_queu e 代表各种各样的进程等待队列。  初始化变量: static int msgbytes = 0; //代表消息的字节数 static int msghdrs = 0; static unsigned short msg_seq = 0; //代表消息的序列号 static int used_queues = 0; //代表使用的消息数 Page6
  • 7. Linux 源代码分析报告 static int max_msqid = 0; //代表最大的消息序列号 static struct wait_queue *msg_lock = NULL;  函数列表: 1. void msg_init(void) 2. static int real_msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) 3. static int real_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) 4. asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) 5. asmlinkage int sys_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, 6. static int findkey(key_t key) 7. static int newque (key_t key, int msgflg) 8. asmlinkage int sys_msgget(key_t key, int msgflg) 9. static void free_que (int id) 10. asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)  函数模块的说明与分析: 1. 初始化模块: void __init msg_init (void) 函数参数:无 函数返回类型:空 函数功能:对消息队列及各变量初始化 { int id; for (id = 0; id < MSGMNI; id++) msgque [id] = (struct msqid_ds *) IPC_UNUSED; /* 给 指 向 消 息 队 列 的 指 针 分 配 空 间 , 标 志 为 IPC_UNUSED, 定 义见 linux/ipc.h : #define IPC_UNUSED ((void ) -1)*/ msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0; msg_lock = NULL; return; } 2. 发送消息模块: Page7
  • 8. Linux 源代码分析报告  static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) 函数参数: msqid: 整型, 消息的序列号; msgp: 指向 msgbuf 结构的指针; msgsz: 消息的大小; msgflg: 消息的标志符。 函 数 返 回 类 型 : 若 消 息 正 确 的 放 入 消 息 队 列 , 则 返 回 0, 否 则 返 回各种 错误信息。详情请看 返回错误类型一览表。 函 数功 能: 将 msgp 所 指向 的消 息缓 冲区 的内 容放 入消 息队 列中 , 且将标志号设为 msqid。其他调用可通过 msqid 访问消息队列。 { int id; struct msgque*msq; struct ipc_perm*ipcp; struct msg*msgh; long mtype; if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0) /* 见 msg.h 中 #define MSGMAX 4056 消息的最大字节数, 消息的大小不符和要求或无序列号, 则出错 */ return -EINVAL; if (get_user(mtype, &msgp->mtype)) /*用户获取消息的类型,正常返回 0*/ return -EFAULT; if (mtype < 1) //若消息类型 <1,则用户无法发送此消息 return -EINVAL; id = (unsigned int) msqid % MSGMNI; Page8
  • 9. Linux 源代码分析报告 /*见 msg.h 中 #define MSGMNI 128 消息队列的最大序列号 id 即消息队列中对应的下标 */ msq = msgque [id]; if (msq == IPC_UNUSED || msq == IPC_NOID) /*特殊的 shmsegs[id], msgque[id] or semary[id]值 。见 ipc.h #define IPC_UNUSED ((void *) -1) //初始化时设置 #define IPC_NOID ((void *) -2) */ return -EINVAL; ipcp = &msq->msg_perm; slept: if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) /*将消息的序列号与 ipc_perm 中的进行比较,若不一致则表示 此消 息以被从消息队列中删去,返回出错信息 */ return -EIDRM; /*调用函数 int ipcperms (struct ipc_perm *ipcp, short flag) 进 程 通 信 时 先 传 递 flag if ( ipcperm(ipcp, S_IWUGO)) 标 识 , 然 后 由 此 函 数 确 认 访 问 通 信 资源的权限。 /*stat.h 中 S_IWUGO 则权限没有被确认。 此函数若返回-1 定 义 为 S_IWUSR|S_IWURP|S_IWOTH, 即 { int requested_mode, granted_mode; requested_mode = (flag >> 6) | (flag >> 3) | flag; 00020 表示 00200|00020|00002。 其中 00200 表示资源创建者的写权限, granted_mode = ipcp->mode; if (current->euid == ipcp->cuid || current->euid == ipcp->uid) 资源创建组的写权限, 00002 表示所有用户写权限 */ /*各 id 号一致*/ granted_mode >>= 6; else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid)) return -EACCES; /*调 用 函 数 in_group_p, 见 /kernel/sys.c 判 断 资 源 拥 有 者 的 组 号或资源创建者的组号是否存在*/ granted_mode >>= 3; if ((requested_mode & ~granted_mode & 0007) && !capable(CAP_IPC_OWNER)) /*判断是否有在 requested_mode 但不在 granted_mode 中的位 * / return -1; Page9 return 0;} }*/
  • 10. Linux 源代码分析报告 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { /* 如果队列中无足够空间 */ if (msgflg & IPC_NOWAIT) /*msgflg 中 IPC_NOWAIT 置位,则进程不进入的等待队列,返 回错误 EAGAIN*/ return -EAGAIN; if (signal_pending(current)) /*如果此进程某个必须处理的信号已经到达,则返回 EINTR*/ return -EINTR; interruptible_sleep_on (&msq->wwait); 调用函数 void interruptible_sleep_on(struct wait_queue **p) /*调度其他等待队列上的进程,直到此进程被唤醒 */ { SLEEP_ON_VAR /*见 sched.c 中 #define SLEEP_ON_VAR unsigned long flags; struct wait_queue wait; */ Page10
  • 11. Linux 源代码分析报告 current->state = TASK_INTERRUPTIBLE; /*将当前任务的状态置为 TASK_INTERRUPTIBLE,表示处 于 等待队列中的进程,待资源有效时唤醒,或 由其它进程通过信号和定时中断唤醒后进入就绪队列 */ SLEEP_ON_HEAD Schedule(); //对等待队列上的进程进行调度 SLEEP_ON_TAIL } goto slept; /*重新发送 */ } } /*以下分配消息的指针和存放内容的空间 */ msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL); if (!msgh) /*系统无足够空间存放消息缓存区 */ return -ENOMEM; msgh->msg_spot = (char *) (msgh + 1); if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz)) /*调用 copy_from_user, 将 msgz 大小的内容为 msgp->mtext 放入 msgh->msg_spot 指向的空间,正常则返回 0*/ { kfree(msgh); /*调用函数 kfree,见 mm/slab.c,释放掉 msgh 的 空间 */ return -EFAULT; } if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { Page11
  • 12. Linux 源代码分析报告 /*msgque 的值为 IPC_UNUSED 或 IPC_NOID,或序列号不符,表示此 消息已从消息队列中删去 */ kfree(msgh); return -EIDRM; } /*设置 msgh 所指的消息的各项属性 */ msgh->msg_next = NULL; msgh->msg_ts = msgsz; msgh->msg_type = mtype; msgh->msg_stime = CURRENT_TIME; if (!msq->msg_first) //消息队列中无成员 msq->msg_first = msq->msg_last = msgh; else { /*将 msgh 插入消息队列 msq*/ msq->msg_last->msg_next = msgh; msq->msg_last = msgh; } /*msg 中加入 msgh 后,修改各项相应的属性 */ msq->msg_cbytes += msgsz; msgbytes += msgsz; msghdrs++; msq->msg_qnum++; msq->msg_lspid = current->pid; msq->msg_stime = CURRENT_TIME; wake_up (&msq->rwait); /*调用函数 wake_up ,唤醒等待获得消息的进程 */ 调用函数 void wake_up_process(struct task_struct * p) { unsigned long flags; spin_lock_irqsave(&runqueue_lock, flags); spin_unlock_irqrestore(&runqueue_lock, flags); } Page12
  • 13. Linux 源代码分析报告 /* 调 用 函 数 spin_lock_irqsave , 将 以 下 部 分 上 锁 , 防 止 其 他 进 程 访 问 */ p->state = TASK_RUNNING; //将进程设为运行状态 if (!p->next_run) { add_to_runqueue(p); //将此任务加入运行队列 reschedule_idle(p); } /*解除锁机制 */ return 0; }  asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) 函数参数: msqid: 整型, 消息的序列号; msgp: 指向 msgbuf 结构的指针; msgsz: 消息的大小; msgtyp: 消息的类型; msgflg: 消息的标志符。 函数返回类型:有关汇编连接的类型 函数功能:系统调用,发送消息 { int ret; lock_kernel(); Page13
  • 14. Linux 源代码分析报告 /*调用函数 lock_kernel{ do{}while(0);}, 此函数用于多 cpu 处理,当一个 cpu 处理进程时,不允许其他 cpu 访问 */ ret = real_msgsnd(msqid, msgp, msgsz, msgflg); unlock_kernel(); /*调用函数 unlock_kernel{do{}while(0);},功能与 lock_kernel 相反 */ return ret; }  返回错误类型一览表: 返回的类型 代表的意思 EACCES 发送消息的进程没有往消息队列写的权限。 EFAULT Msgp 所指的地址无法访问。 EIDRM 消息已被从消息队列中删去。 EINTR 消息队列已满,正在发送消息的进程获得某个信号。 EINVAL 错误的 msqid 或非正的 mtype,或错误的 msgsz。 ENOMEN 系统没有足够的空间给消息缓冲区。 EAGAIN 若消息队列已满,且 IPC _NOWAIT 置位。 返回 3 . 接收消息模块;  static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) 函数参数: msqid: 整型, 消息的序列号; msgp: 指向 msgbuf 结构的指针; msgsz: 消息的大小; msgtyp: 消息的类型; msgflg: 消息的标志符。 函数返回类型:若接收正确,返回接收到的消息字节数。否则,返回 错误信息,详情请看 返回错误一览表。 函数功能:从消息队列中接收标示号为 msqid,内容为 msgp 所指向的信 息。 Page14
  • 15. Linux 源代码分析报告 { struct msqid_ds*msq; struct ipc_perm *ipcp; struct msg *tmsg, *leastp = NULL; struct msg *nmsg = NULL; int id; if (msqid < 0 || (long) msgsz < 0) //消息的序列号或大小不符和要求 return -EINVAL; id = (unsigned int) msqid % MSGMNI; msq = msgque [id]; //将消息队列中下标为 id 的成员赋给 msq if (msq == IPC_NOID || msq == IPC_UNUSED) return -EINVAL; ipcp = &msq->msg_perm; while (!nmsg) { if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { /*序列号不符,表示此消息已被删去 */ return -EIDRM; } if (ipcperms (ipcp, S_IRUGO)) { /* stat.h 中 S_IRUGO 定 义 为 S_IRUSR|S_IRGRP|S_IROTH , 即 00400| 00040|00004。 00400 表示资源创建者的读权限, 00040 表示资源创建组的读 权限, 00004 表示所有用户的读权限。函数确认访问通信资源的权限 。此 函数若返回 -1 则权限没有被确认 */ return -EACCES; } /*以下寻找类型正确的消息 */ if (msgtyp == 0) // msgtyp=0 取消息队列的第一个 nmsg = msq->msg_first; Page15
  • 16. Linux 源代码分析报告 else if (msgtyp > 0) { if (msgflg & MSG_EXCEPT) { /* 若 msgtyp>0 且 MSG_EXCEPT 置 位 , 则 取 消 息 队 列 的 第 一 个 类型不符和的 */ for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_type != msgtyp) break; nmsg = tmsg; } else { /*否则取第一个类型符合的 */ for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_type == msgtyp) break; nmsg = tmsg; } } else { /* msgtyp<0 取 type 值小于 msgtyp 的绝对值中最小的一个 */ for (leastp = tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_type < leastp->msg_type) leastp = tmsg; if (leastp && leastp->msg_type <= - msgtyp) nmsg = leastp; } if (nmsg) { /* 确实找到了符合条件的消息 */ if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) { /* 若 找 到 的 消 息 大 小 超 过 允 许 的 最 大 长 度 且 msgflg 和 MSG_NOERROR 没有置位, 则溢出 */ return -E2BIG; } msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz; Page16
  • 17. Linux 源代码分析报告 /*若 msgflg 和 MSG_NOERROR 置位,则取消息的前 msgsz 个字 节 */ /*以下处理消息取出后队列的调整 */ if (nmsg == msq->msg_first) //取出的消息为第一个 msq->msg_first = nmsg->msg_next; else { for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_next == nmsg) break; tmsg->msg_next = nmsg->msg_next; if (nmsg == msq->msg_last) msq->msg_last = tmsg; } if (!(--msq->msg_qnum)) //若取出消息后,消息队列中无剩余消 息 msq->msg_last = msq->msg_first = NULL; /*设置改动后,各变量的值 */ msq->msg_rtime = CURRENT_TIME; msq->msg_lrpid = current->pid; msgbytes -= nmsg->msg_ts; msghdrs--; msq->msg_cbytes -= nmsg->msg_ts; wake_up (&msq->wwait); /*调用函数 wake_up, 唤醒等待发送消息的进程 */ if (put_user (nmsg->msg_type, &msgp->mtype) || copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz)) /* 调 用 copy_to_user, 将 msgz 大 小 的 内 容 为 msgp->mtext 放 入 msgh->msg_spot 指向的空间,正常则返回 0*/ Page17
  • 18. Linux 源代码分析报告 msgsz = -EFAULT; kfree(nmsg); return msgsz; } else { /*若没有找到符合的消息 */ if (msgflg & IPC_NOWAIT) { /*若 IPC_NOWAIT 置位,则进程不进入等待队列,返回错误信 息 */ return -ENOMSG; } if (signal_pending(current)) { /*调用 函数 sigal _pending, 判断 若当 前进 程所 需的 信号 已到 且 进程 未被堵塞 */ return -EINTR; } interruptible_sleep_on (&msq->rwait); /*调用函数 interruptible_sleep_on,对等待发送消息的队列中的 进 程进行调度 */ } } return -1; }  asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) 函数参数: msqid: 整型, 消息的序列号; msgp: 指向 msgbuf 结构的指针; Page18
  • 19. Linux 源代码分析报告 msgsz: 消息的大小; msgtyp: 消息的类型; msgflg: 消息的标志符。 函数返回类型:有关汇编链接的类型 函数功能:系统调用,获得消息 { int ret; lock_kernel(); //含义与 sys_msgsnd 相同 ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg); unlock_kernel(); return ret; }  返回错误类型一览表; 返回类型 代表的意思 E2BIG 消息大小超过 msgsz,且 msgflg 中 MSG_NOERROR 没有置位 EACCES 接收消息的进程没有从消息队列读的权限。 EFAULT Msgp 所指的地址无法访问。 EIDRM 当进程处于 sleep 状态时,消息已被从消息队列中删去。 EINTR 当进程处于 sleep 状态时,消息队列已满,正在接收消息的进程获 得某个信号。 EINVAL 错误的 msqid 或非正的 mtype,或错误的 msgsz。 ENOMSG Msgflg 中 IPC_NOWAIT 置位,且消息队列中没有需要的消息。 返回 4 . 创建或获得消息的控制模块:  asmlinkage int sys_msgget (key_t key, int msgflg) 函数参数: key:代表访问通信资源的权限 msgflg:标志位。 函数返回类型:整型 函数功能:传递键值,标志位以及其他的参数,获得消息。 { int id, ret = -EPERM; Page19
  • 20. Linux 源代码分析报告 struct msqid_ds *msq; lock_kernel(); if (key == IPC_PRIVATE) /*若键值为 IPC_RPIVATE,则创建一个新的消息队列,此消息队列不能通 过 其他 get 调用访问。调用者有独占权,通过 fork 系统调用,子进程继承 这个消息对俄,属主可以和它的子进程共享 */ ret = newque(key, msgflg); else if ((id = findkey (key)) == -1) { /*没有找到相应的 key */ if (!(msgflg & IPC_CREAT)) /*IPC_CREAT 置位表示创建尚不存在的新的消息 */ ret = -ENOENT; else ret = newque(key, msgflg); } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) { /*若 msgflg 中 IPC_CREAT 置位且相应的消息已存在,而且 IPC_EXCL 置位,返回错误 */ ret = -EEXIST; } else { /*若没有制定标志位,则内核试着查找有相同键值的已经存在的消息 * / msq = msgque[id]; if (msq == IPC_UNUSED || msq == IPC_NOID)//消息不存在 ret = -EIDRM; else if (ipcperms(&msq->msg_perm, msgflg)) //权限被否认 ret = -EACCES; else ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id; /*取得该消息的 ID 号 */ Page20
  • 21. Linux 源代码分析报告 } unlock_kernel(); return ret; } 5. 消息传递的控制模块:  asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) 函数参数: msqid:消息序列号 cmd:处理消息的命令 buf:指向 msqid_ds 的指针 函 数 返 回 类 型 : 有 关 汇 编 连 接 的 类 型 , 有 关 错 误 返 回 的 信 息 请 看 返回错误 类型一览表。 函数功能: 系统调用。处理各种有关消息处理的命令,详情请看命令览 表 { int id, err = -EINVAL; struct msqid_ds*msq; struct msqid_dstbuf; struct ipc_perm*ipcp; lock_kernel(); if (msqid < 0 || cmd < 0) goto out; err = -EFAULT; switch (cmd) { /*开始处理命令 */ case IPC_INFO: /*IPC_INFO 输出 msginfo 结构中有关消息队列的相关值的最大值 */ case MSG_INFO: Page21
  • 22. Linux 源代码分析报告 /*MSG_INFO 输 出 的 与 ICP_INFOU 有 所 不 同 , 它 给 出 msgpool 中 使 用 过 的 等 待 队 列 的 数 目 , msgmap 中 消 息 的 数 目 和 系 统 存 储 在 msgtql 中 的总的消息数 */ if (!buf) goto out; { / 得到消息的数量信息 */ struct msginfo msginfo; msginfo.msgmni = MSGMNI; msginfo.msgmax = MSGMAX; msginfo.msgmnb = MSGMNB; msginfo.msgmap = MSGMAP; msginfo.msgpool = MSGPOOL; msginfo.msgtql = MSGTQL; msginfo.msgssz = MSGSSZ; msginfo.msgseg = MSGSEG; if (cmd == MSG_INFO) { msginfo.msgpool = used_queues; msginfo.msgmap = msghdrs; msginfo.msgtql = msgbytes; } err = -EFAULT; if (copy_to_user (buf, &msginfo, sizeof(struct msginfo))) /*调用函数 copy_to_user 将 msginfo 所指的内容放入 buf 中 */ goto out; err = max_msqid; goto out; } case MSG_STAT: /* MSG_STAT 变量允许参数传递系统内部消息队列表的索引 */ if (!buf) goto out; Page22
  • 23. Linux 源代码分析报告 err = -EINVAL; if (msqid > max_msqid) goto out; msq = msgque[msqid]; if (msq == IPC_UNUSED || msq == IPC_NOID) goto out; err = -EACCES; if (ipcperms (&msq->msg_perm, S_IRUGO)) //权限被否认 goto out; id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid; /*设置 tbuf 的各项值 */ tbuf.msg_perm = msq->msg_perm; tbuf.msg_stime = msq->msg_stime; tbuf.msg_rtime = msq->msg_rtime; tbuf.msg_ctime = msq->msg_ctime; tbuf.msg_cbytes = msq->msg_cbytes; tbuf.msg_qnum = msq->msg_qnum; tbuf.msg_qbytes = msq->msg_qbytes; tbuf.msg_lspid = msq->msg_lspid; tbuf.msg_lrpid = msq->msg_lrpid; err = -EFAULT; if (copy_to_user (buf, &tbuf, sizeof(*buf))) /*将 tbuf 的内容拷贝 buf 中去 */ goto out; err = id; goto out; case IPC_SET: /*IPC_SET 允许消息队列的拥有者,模式和最大允许的字节数被改变 * / if (!buf) goto out; err = -EFAULT; if (!copy_from_user (&tbuf, buf, sizeof (*buf))) err = 0; break; case IPC_STAT: Page23
  • 24. Linux 源代码分析报告 /*IPC_STAT 将 有 关 的 消 息 队 列 的 msqid_id 结 构 拷 贝 到 用 户 的 存 储 区 域 */ if (!buf) goto out; break; } id = (unsigned int) msqid % MSGMNI; // 获得消息的 ID 号 msq = msgque [id]; err = -EINVAL; if (msq == IPC_UNUSED || msq == IPC_NOID) //消息不存在 goto out; err = -EIDRM; if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) //权限被否认 goto out; ipcp = &msq->msg_perm; switch (cmd) { case IPC_STAT: err = -EACCES; if (ipcperms (ipcp, S_IRUGO)) //确认访问权限 goto out; /*IPC_STAT 将 有 关 的 消 息 队 列 的 msqid_id 结 构 拷 贝 到 用 户 的 存 储 区 域 */ tbuf.msg_perm = msq->msg_perm; tbuf.msg_stime = msq->msg_stime; tbuf.msg_rtime = msq->msg_rtime; tbuf.msg_ctime = msq->msg_ctime; tbuf.msg_cbytes = msq->msg_cbytes; tbuf.msg_qnum = msq->msg_qnum; tbuf.msg_qbytes = msq->msg_qbytes; tbuf.msg_lspid = msq->msg_lspid; tbuf.msg_lrpid = msq->msg_lrpid; err = -EFAULT; if (!copy_to_user (buf, &tbuf, sizeof (*buf))) Page24
  • 25. Linux 源代码分析报告 err = 0; goto out; case IPC_SET: /*IPC_SET 允许消息队列的拥有者,模式和最大允许的字节数被改变 * / err = -EPERM; if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) /* We _could_ check for CAP_CHOWN above, but we don't */ goto out; if(tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE)) goto out; msq->msg_qbytes = tbuf.msg_qbytes; ipcp->uid = tbuf.msg_perm.uid; ipcp->gid = tbuf.msg_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (S_IRWXUGO & tbuf.msg_perm.mode); msq->msg_ctime = CURRENT_TIME; err = 0; goto out; case IPC_RMID: /*IPC_RMID 允许超级用户和消息队列的创建者或拥有者删除队列 */ err = -EPERM; if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) /*无删除的权限 */ goto out; freeque (id); //删除此队列 err = 0; goto out; default: err = -EINVAL; goto out; } out: Page25
  • 26. Linux 源代码分析报告 unlock_kernel(); return err; }  返回错误类型一览表: 错误类型 代表的意思 EDIRM 将消息删去 EINVAL 错误的 cmd 或 msgqid EPERM Cmd 参数值为 IPC_SET 或 IPC_RMID,但是,调用的进程的 有 效的 UID 没有足够的权限执行此项命令. 返回 6. 其他相关的函数:  static int findkey (key_t key) 函数参数: key:代表访问通信资源的权限 函数返回类型:找到了者返回 id 号,否则返回 -1; 函数功能:找到所需的 key 值 { int id; struct msqid_ds*msq; for (id = 0; id <= max_msqid; id++) { while ((msq = msgque[id]) == IPC_NOID) interruptible_sleep_on(&msg_lock); if (msq == IPC_UNUSED) continue; if (key == msq->msg_perm.key) //找到相应的 key 值 return id; } return -1; }  static int newque (key_t key, int msgflg) 函数参数: Page26
  • 27. Linux 源代码分析报告 key:代表访问通信资源的权限 msgflg:消息的标志符 函数返回类型:整型 函数功能:寻找可获得的消息队列资源。 { int id; struct msqid_ds *msq; struct ipc_perm *ipcp; for (id = 0; id < MSGMNI; id++) if (msgque[id] == IPC_UNUSED) { msgque[id] = (struct msqid_ds ) IPC_NOID; /*给消息队列置位为 IPC_NOID*/ goto found; } return -ENOSPC; //没有空间可以分配 found: msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL); if (!msq) { //若分配空间失败 msgque[id] = (struct msqid_ds ) IPC_UNUSED; /*置位为 IPC_UNUSED*/ wake_up (&msg_lock); /*调用 wake_up 函数,唤醒被阻塞的队列上的进程 */ return -ENOMEM; } /*设置 ipc_perm 结构的内容 */ ipcp = &msq->msg_perm; ipcp->mode = (msgflg & S_IRWXUGO); ipcp->key = key; ipcp->cuid = ipcp->uid = current->euid; ipcp->gid = ipcp->cgid = current->egid; /* 设置 msq 的各项内容 */ msq->msg_perm.seq = msg_seq; msq->msg_first = msq->msg_last = NULL; Page27
  • 28. Linux 源代码分析报告 msq->rwait = msq->wwait = NULL; msq->msg_cbytes = msq->msg_qnum = 0; msq->msg_lspid = msq->msg_lrpid = 0; msq->msg_stime = msq->msg_rtime = 0; msq->msg_qbytes = MSGMNB; msq->msg_ctime = CURRENT_TIME; if (id > max_msqid) max_msqid = id; msgque[id] = msq; //将新成员加入消息队列 used_queues++; wake_up (&msg_lock); //唤醒被阻塞的进程 return (unsigned int) msq->msg_perm.seq * MSGMNI + id; }  static void freeque (int id) 函数参数: id 消息序列号 函数返回类型:空 函数功能:从 msgque 数组中删去下标为 id 的消息 { struct msqid_ds *msq = msgque[id]; struct msg*msgp, *msgh; msq->msg_perm.seq++; msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* 空闲的消息序列数增 1, 且避免溢出 */ msgbytes -= msq->msg_cbytes; if (id == max_msqid) while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED)); msgque[id] = (struct msqid_ds *) IPC_UNUSED; used_queues--; //使用的消息队列数减 1 while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) { wake_up (&msq->rwait); //唤醒等待发送消息的队列上的进程 wake_up (&msq->wwait);//唤醒等待获取消息的队列上的进程 Page28
  • 29. Linux 源代码分析报告 schedule(); //对等待队列上的进程进行进程调度 } for (msgp = msq->msg_first; msgp; msgp = msgh ) { /*释放掉 msq 所指的空间 */ msgh = msgp->msg_next; msghdrs--; kfree(msgp); } kfree(msq); }  讨论:  在消息传递机制中,当读取一个消息后,消息将从 队列移去,其他进程不能读到。若因为接收的缓冲区 太小造成消息被截断,截断的部分将永远丢失。  进程必须通过带有 IPC_RMID 的 sys _msgctl 调用,来 显示的删除消息队列。如果不是这样则消息队列可以 长久的存在。则样就回导致系统很难判断,消息是为 了将来进程访问而留下来还是被无意的抛弃了,或 是由于想要释放它的进程不正常终止了,这样导致 系统无限的保存这消息。如果这种情况经常发生,这 种消息资源就会用光。  总的说来,消息队列传递数据时是以一种不连续消 息的方式,这样就可以更加灵活的处理数据。 Page29