毕业论文-操作系统课程设计论文之消息函数的分析论文.doc
摘 要 Linux是一种自由和开放源码的类Unix操作系统。目前存在着许多不同的Linux,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,从手机、平板电脑、路由器和视频游戏控制台,到台式计算机、大型机和超级计算机。 Linux采用消息队列的方式来实现消息传递。System V的消息队列(message queues)是进程之间互相发送消息的一种异步(asynchronously)方式,在这种情形之下,发送方不必等待接收方检查它的消息即在发送完消息后,发送方就可以从事其它工作了而接收方也不必一直等待消息。 新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。消息队列允许一个或多个进程写消息,一个或多个进程读取消息。Linux维护了一系列消息队列的msgque向量表。其中的每一个单元都指向一个msqid_ds的数据结构,完整描述这个消息队列。当创建消息队列的时候,从系统内存中分配一个新的msqid_ds的数据结构并插入到向量表中。关键字: Linux , 消息函数 ,分析 目 录摘 要11 课设简介31.1课程设计题目31.2课程设计小组成员32 LINUX的消息函数主模块功能描述33 LINUX的消息函数各个子模块功能描述43.1 Msgget:43.2 Msgsnd:53.3 Msgrcv:63.4 Msgctl:74 LINUX的消息函数各个子模块相关函数代码分析结果94.1有关常量、函数及相关错误信息的含义:94.1.1 常量含义:94.1.2 错误信息含义94.1.3 相关函数及文件首部含义94.2函数sys_msgget的分析164.2.1 代码及注释164.3发送消息函数real_msgsnd的分析234.3.1 代码分析234.4接收消息函数real_msgrcv的分析284.4.1 代码及注释284.5函数sys_msgctl的分析334.5.1 代码及注释33心得体会42参考文献431 课设简介1.1课程设计题目LINUX的消息函数的分析1.2课程设计小组成员 Msgget:孙帅,分析消息队列的创建函数(sys_msgget)以及与它相关的函数newque、findkey、msg_init,写出代码分析结果,明确组内成员的明细分工,总体把握组内成员的进度。后期组织组内成员成果汇总进行本组总体报告撰写。王亚璇,画出流程图来表示相关函数之间的相互调用关系。魏蕾,负责查阅资料。 Msgsnd:张婷,分析消息的发送函数(real_msgsnd)以及与它相关的函数sys_msgsnd,写出代码分析结果。王鑫坤,画出流程图来表示相关函数之间的相互调用关系。闫瞳,张飞龙,负责查阅资料。 Msgrcv:樊德山,分析消息的接收函数(real_msgrcv)以及与它相关的函数sys_msgrcv,写出代码分析结果。赵松,画出流程图来表示相关函数之间的相互调用关系。鹿新宇,孙适,负责查阅资料。 Msgctl:刘晶,分析消息队列的控制函数(sys_msgctl)以及与它相关的函数freeque,写出代码分析结果。鞠冰霜,画出流程图来表示相关函数之间的相互调用关系。刘航,负责查阅资料。2 LINUX的消息函数主模块功能描述Linux采用消息队列的方式来实现消息传递。System V的消息队列(message queues)是进程之间互相发送消息的一种异步(asynchronously)方式,在这种情形之下,发送方不必等待接收方检查它的消息即在发送完消息后,发送方就可以从事其它工作了而接收方也不必一直等待消息。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。消息队列允许一个或多个进程写消息,一个或多个进程读取消息。Linux维护了一系列消息队列的msgque向量表。其中的每一个单元都指向一个msqid_ds的数据结构,完整描述这个消息队列。当创建消息队列的时候,从系统内存中分配一个新的msqid_ds的数据结构并插入到向量表中。每一个msqid_ds数据结构都包括一个ipc_perm的数据结构和进入这个队列的消息的指针。另外,Linux保留队列的改动时间,例如上次队列写的时间等。Msqid_ds队列也包括两个等待队列:一个用于向消息队列写,另一个用于读。 每一次一个进程试图向写队列写消息,它的有效用户和组的标识符就要和队列的 ipc_perm数据结构的模式比较。如果进程可以向这个队列写,则消息会从进程的地址空间写到msg数据结构,放到消息队列的最后。每一个消息都带有进程间约定的,应用程序指定类型的标记。但是,因为Linux限制了可以写的消息的数量和长度,可能会没有空间容纳消息。这时,进程会被放到消息队列的写等待队列,然后调用调度程序选择一个新的进程运行。当一个或多个消息从这个消息队列中读出去的时候会被唤醒。从队列中读是一个相似的过程。进程的访问权限一样被检查。一个读进程可以选择是不管消息的类型从队列中读取第一条消息还是选择特殊类型的消息。如果没有符合条件的消息,读进程会被加到消息队列的读等待进程,然后运行调度程序。当一个新的消息写到队列的时候,这个进程会被唤醒,继续运行。3 LINUX的消息函数各个子模块功能描述3.1 Msgget:功能:取得一个消息队列。调用者提供消息队列的键标(用于表示一个消息列的唯一的名字),当这个队列存在的时候,这个系统调用负责返回这个队列的标识号;如果这个队列不存在,就创立一个消息队列,然后返回这个消息队列的标识号。主要由sys_msgget执行。说明:系统调用返回与参数key相关的消息队列的标识符. 若以下事实成立,则与消息队列相关的标识符和数据结构将被创建出来: 若参数key等于IPC_PRIVATE. 若参数key没有存在的消息队列标识符与之相关,同时(msgflg&IPC_CREAT)为真. 创建消息队列的同时,与新的消息队列标识符相关的数据结构将被初始化为如下: msg_perm.cuid和msg_perm.uid设置为调用进程的有效UID. msg_perm.cgid和msg_perm.gid设置为调用进程的有效GID. msg_perm.mode访问权限比特位设置为msgflg访问权限比特位. msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime设置为0. msg_ctime设置为当前系统时间. msg_qbytes设置为系统允许的最大值. 返回值:调用成功则返回一个非0值,称为消息队列标识符;否则返回值为-1. 简单框图: 3.2 Msgsnd:功能:发送消息到指定的消息队列中。主要由real_msgsnd执行。说明:发送一个消息到由msqid指定消息队列标识号的消息队列. 参数msgp指向一个用户定义的缓冲区,并且缓冲区的第一个域应为长整型,指定消息类型,其他数据放在缓冲区的消息中其他正文区内. 下面是消息元素定义: long mtype; char mtext; mtype是一个整数,用于接收进程选择消息类型. mtext是一个长度为msgsz字节的任何正文,参数msgsz可从0到系统允许的最大值之间变化. msgflg指定操作行为: 若(msgflg&IPC_NOWAIT)是真的,消息并不是被立即发送而调用进程会立即返回. 若(msgflg&IPC_NOWAIT)不是真的,则调用进程会被挂起直到下面情况之一发生: 消息被发送出去. 消息队列标志被系统删除.系统调用返回-1. 调用进程接收到未被忽略的中断信号,调用进程继续执行或被终止. 调用成功后,对应指定的消息队列的相关结构做如下动作: 消息数(msg_qnum)加1. 消息队列最近发送进程号(msg_lspid)改为调用进程号. 消息队列发送时间(msg_stime)改为当前系统时间. 以上信息可用命令ipcs -a看到.返回值:发送成功则返回0,否则返回-1.简单框图:3.3 Msgrcv: 功能:用msgrcv函数系统调用从msqid消息队列中读取一条信息并将其放入消息段指针msgp指向的结构。msgsz给出mtext的字节数, 如果所接收的消息比msgsz大且msgflg&MSG_NOERROR为真,则按msgsz的大小截断而不通知调用进程。从消息队列中取得指定类型的消息.。 说明:系统调用从由msqid指定的消息队列中读取一个由msgtyp指定类型的消息到由msgp指向的缓冲区中,同样的,该缓冲区的结构如前所述,包括消息类型和消息正文.msgsz为可接收的消息正文的字节数.若接收到的消息正文的长度大于msgsz,则会被截短到msgsz字节为止(当消息标志msgflg&MSG_NOERROR为真时),截掉的部份将被丢失,而且不通知消息发送进程. msgtyp指定消息类型: 为0则接收消息队列中第一个消息. 大于0则接收消息队列中第一个类型为msgtyp的消息. 小于0则接收消息队列中第一个类型值不小于msgtyp绝对值且类型值又最小的消息. msgflg指定操作行为: 若(msgflg&IPC_NOWAIT)是真的,调用进程会立即返回,若没有接收到消息则返回值为-1,error设置为ENOMSG. 若(msgflg&IPC_NOWAIT)不是真的,则调用进程会被挂起直到下面情况之一发生: 队列中的消息的类型是有效的. 消息队列标志被系统删除.系统调用返回-1. 调用进程接收到未被忽略的中断信号,调用进程继续执行或被终止. 调用成功后,对应指定的消息队列的相关结构做如下动作: 消息数(msg_qnum)减1. 消息队列最近接收进程号(msg_lrpid)改为调用进程号. 消息队列接收时间(msg_rtime)改为当前系统时间. 以上信息可用命令ipcs -a看到. 返回值:调用成功则返回值等于接收到实际消息正文的字节数.不成功则返回-1.简单框图:3.4 Msgctl: 功能:在消息队列上执行指定的操作。根据参数的不同和权限的不同,可以执行检索、删除等等操作。主要由sys_msgctl执行。 说明:系统调用提供一系列消息控制操作,操作动作由cmd定义,以下cmd定义值表明了各操作动作的定义. IPC_STAT:将msqid相关的数据结构中各个元素的当前值放入由buf指向的结构中. IPC_SET:将msqid相关的数据结构中的下列各元素设置为由buf指向的结构中的对应值. msg_perm.uid msg_perm.gid msg_perm.mode msg_qbytes该命令只能由有效UID等于msg_perm.cuid或msg_perm.uid的进程或有效UID有合适权限的进程操作.只有具有合适权限的用户才能增加msg_qbytes的值. IPC_RMID:删除由msqid指示的消息队列.将它从系统中删除并破坏相关的数据结构. 该命令只能由有效UID等于msg_perm.cuid或msg_perm.uid的进程或有效UID有合适权限的进程操作.返回值:调用成功则返回值为0,否则为-1.简单框图:4 LINUX的消息函数各个子模块相关函数代码分析结果4.1 有关常量及相关错误信息的含义:4.1.1 常量含义:static struct msqid_ds *msgqueMSGMNI; /消息队列static int msgbytes = 0; /消息队列中所有消息的总字节数static int msghdrs = 0; /消息队列的队头static unsigned short msg_seq = 0;static int used_queues = 0; /已用的消息队列数static int max_msqid = 0; /消息队列最大的ID值 static struct wait_queue *msg_lock = NULL; /消息队列锁定,不让等待进程进入4.1.2 相关错误信息的含义:EINVAL22/* Invalid argument */EFAULT14/* Bad address */EIDRM43/* Identifier removed */EACCES13/* Permission denied */EAGAIN11/* Try again */EINTR 4/* Interrupted system call */ENOMEM12/* Out of memory */E2BIG 7/* Arg list too long */ENOMSG42/* No message of desired type */ENOSPC28/* No space left on device */ENOMEM12/* Out of memory */EPERM 1/* Operation not permitted */ENOENT 2/* No such file or directory */EEXIST17/* File exists */4.1.3 相关函数及文件首部含义: struct msg_receiver struct list_head r_list; /* 等待接收者进程列表*/struct task_struct* r_tsk; /*任务列表*/int r_mode; /* 接收模式*/long r_msgtype; /* 等待的消息模型*/long r_maxsize; /* 最大大小*/struct msg_msg* volatile r_msg; /* 消息结构*/; struct msg_sender struct list_head list; /* 等待发送者进程列表*/ struct task_struct* tsk; /* 任务列表*/; struct msg_msgseg /*每一个消息都要有msg_msg数据结构*/struct msg_msgseg* next;/*消息的下一部分 */;struct msg_msg /: msg消息队列数据结构:/struct list_head m_list; /*用于存放消息的链表*/long m_type; /*消息类型*/ int m_ts; /*消息文本大小*/struct msg_msgseg* next;/*下一个消息*/;struct msg_queue struct kern_ipc_perm q_perm;/*IPC许可证*/time_t q_stime;/* 最后发送时间*/time_t q_rtime;/* 最后接收时间*/time_t q_ctime;/* 最后修改时间 */unsigned long q_cbytes;/*队列当前的字节数 */unsigned long q_qnum;/*队列中消息的数量 */unsigned long q_qbytes;/*队列的最大字节数*/pid_t q_lspid;/* 最后一个发送者进程的PID*/pid_t q_lrpid;/* 最后一个接受者进程的PID*/struct list_head q_messages; /* 存放消息的队列*/struct list_head q_receivers; /* 消息接收者的队列*/struct list_head q_senders; /* 消息发送者的队列*/;#define SEARCH_ANY1/*搜索全部*/#define SEARCH_EQUAL2/*搜索相等的*/#define SEARCH_NOTEQUAL3/*搜索不相等的*/#define SEARCH_LESSEQUAL4/*搜索小于等于的*/static atomic_t msg_bytes = ATOMIC_INIT(0);/*原子变量消息字节数初始值为0*/static atomic_t msg_hdrs = ATOMIC_INIT(0);/*/static struct ipc_ids msg_ids;/*消息ID*/#define msg_lock(id)(struct msg_queue*)ipc_lock(&msg_ids,id) /* 锁定IPC*/#define msg_unlock(id)ipc_unlock(&msg_ids,id) /* 解锁IPC*/#define msg_rmid(id)(struct msg_queue*)ipc_rmid(&msg_ids,id)#define msg_checkid(msq, msgid)/* 检查IPC*/ipc_checkid(&msg_ids,&msq->q_perm,msgid)#define msg_buildid(id, seq) /* 创建一个消息队列类型的IPC资源标示符*/ipc_buildid(&msg_ids, id, seq)static void freeque (int id);/*释放一个进程队列*/static int newque (key_t key, int msgflg);/*新建一个队列*/#ifdef CONFIG_PROC_FSstatic int sysvipc_msg_read_proc(char *buffer, char *start, off_t offset, int length, int *eof, void *data);/*系统进程消息读取工具*/#endifvoid _init msg_init (void)/*初始化*/ipc_init_ids(&msg_ids,msg_ctlmni);#ifdef CONFIG_PROC_FScreate_proc_read_entry("sysvipc/msg", 0, 0, sysvipc_msg_read_proc, NULL);#endifstatic int newque (key_t key, int msgflg)/*新建消息队列*/int id;/*号*/struct msg_queue *msq;/*定义消息队列*/msq = (struct msg_queue *) kmalloc (sizeof (*msq), GFP_KERNEL);/*初始化*/if (!msq) return -ENOMEM;id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni);if(id = -1) kfree(msq);return -ENOSPC;msq->q_perm.mode = (msgflg & S_IRWXUGO);msq->q_perm.key = key;msq->q_stime = msq->q_rtime = 0;msq->q_ctime = CURRENT_TIME;msq->q_cbytes = msq->q_qnum = 0;msq->q_qbytes = msg_ctlmnb;msq->q_lspid = msq->q_lrpid = 0;INIT_LIST_HEAD(&msq->q_messages);INIT_LIST_HEAD(&msq->q_receivers);INIT_LIST_HEAD(&msq->q_senders);msg_unlock(id);return msg_buildid(id,msq->q_perm.seq);static void free_msg(struct msg_msg* msg)/*释放一个消息*/struct msg_msgseg* seg;seg = msg->next;kfree(msg);while(seg != NULL) struct msg_msgseg* tmp = seg->next;kfree(seg);seg = tmp;static struct msg_msg* load_msg(void* src, int len)/*加载消息*/struct msg_msg* msg;struct msg_msgseg* pseg;int err;int alen;alen = len;if(alen > DATALEN_MSG)alen = DATALEN_MSG;msg = (struct msg_msg *) kmalloc (sizeof(*msg) + alen, GFP_KERNEL);if(msg=NULL)return ERR_PTR(-ENOMEM);msg->next = NULL;if (copy_from_user(msg+1, src, alen) err = -EFAULT;goto out_err;len -= alen;src = (char*)src)+alen;pseg = &msg->next;while(len > 0) struct msg_msgseg* seg;alen = len;if(alen > DATALEN_SEG)alen = DATALEN_SEG;seg = (struct msg_msgseg *) kmalloc (sizeof(*seg) + alen, GFP_KERNEL);if(seg=NULL) err=-ENOMEM;goto out_err;*pseg = seg;seg->next = NULL;if(copy_from_user (seg+1, src, alen) err = -EFAULT;goto out_err;pseg = &seg->next;len -= alen;src = (char*)src)+alen;return msg;out_err:free_msg(msg);return ERR_PTR(err);static int store_msg(void* dest, struct msg_msg* msg, int len)/*存储消息*/int alen;struct msg_msgseg *seg;alen = len;if(alen > DATALEN_MSG)alen = DATALEN_MSG;if(copy_to_user (dest, msg+1, alen)return -1;len -= alen;dest = (char*)dest)+alen;seg = msg->next;while(len > 0) alen = len;if(alen > DATALEN_SEG)alen = DATALEN_SEG;if(copy_to_user (dest, seg+1, alen)return -1;len -= alen;dest = (char*)dest)+alen;seg=seg->next;return 0;static inline void ss_add(struct msg_queue* msq, struct msg_sender* mss)/*将一个消息添加到消息队列尾部*/mss->tsk=current;current->state=TASK_INTERRUPTIBLE;list_add_tail(&mss->list,&msq->q_senders);static inline void ss_del(struct msg_sender* mss)/*删除一个发送者的消息*/if(mss->list.next != NULL)list_del(&mss->list);static void ss_wakeup(struct list_head* h, int kill)/*唤醒一个消息*/struct list_head *tmp;tmp = h->next;while (tmp != h) struct msg_sender* mss;mss = list_entry(tmp,struct msg_sender,list);tmp = tmp->next;if(kill)mss->list.next=NULL;wake_up_process(mss->tsk);static void expunge_all(struct msg_queue* msq, int res) /*去除所有消息*/struct list_head *tmp;tmp = msq->q_receivers.next;while (tmp != &msq->q_receivers) struct msg_receiver* msr;msr = list_entry(tmp,struct msg_receiver,r_list);tmp = tmp->next;msr->r_msg = ERR_PTR(res);wake_up_process(msr->r_tsk);4.2 函数sys_msgget的分析4.2.1 代码及注释asmlinkage int sys_msgget (key_t key, int msgflg) int id, ret = -EPERM;struct msqid_ds *msq;lock_kernel(); /锁内核if (key = IPC_PRIVATE) /判断调用的参数key中是否指定了IPC_PRIVATE 即判断调用者需要一个新队列;ret = newque(key, msgflg); /如果调用的参数key中指定了IPC_PRIVATE则调用newque函数新建一个队列,并返回新建队列的序列号IDelse if (id = findkey (key) = -1) /如果调用的参数key中没有指定IPC_PRIVATE,则调用findkey函数来搜索一下是否有这个键值指定的队列,并用把函数的返回值赋给参数IDif (!(msgflg & IPC_CREAT) /如果key键值未被使用,则判断调用的参数msgflg中是否设置了IPC_CREAT(00001000)位ret = -ENOENT; /如果调用的参数msgflg中设置了IPC_CREAT(00001000)位,则返回错误,错误类型ENOENT(No such file or directory)elseret = newque(key, msgflg); /如果调用的参数msgflg中没有设置IPC_CREAT(00001000)位,则调用newque函数新建一个队列,并返回新建队列的序列号ID else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) /key这个键值已被使用。判断调用的参数msgflg是否标志了IPC_CREAT(00001000)位和IPC_EXCL(00002000)位ret = -EEXIST; /如果调用的参数msgflg中既设置了IPC_CREAT(00001000)位,又设置了IPC_EXCL(00002000)位,则返回错误,错误类型EEXIST(File exists) else /如果有key这个键值指定的队列,则使用这个键值所指明的队列作为当前消息队列msq = msgqueid; /把用findkey返回的这个键值所指明的队列指针赋给当前消息队列if (msq = IPC_UNUSED | msq = IPC_NOID) /判断当前消息队列(msgque)项目是否被设置IPC_UNUSED或IPC_NOIDret = -EIDRM; /如果当前消息队列(msgque)项目指向被设置IPC_UNUSED或被设置IPC_NOID,则该地方没有消息队列(msgque)或者调用者缺少访问它的权限返回错误,错误类型EACCES(Identifier removed)else if (ipcperms(&msq->msg_perm, msgflg) /如果当前消息队列指针指既没有向IPC_UNUSED又没有指向IPC_NOID,判断是否允许访问进程间通讯资源IPCret = -EACCES; /如果返回值非0,则不允许访问进程间通讯资源IPC,返回错误,错误类型EACCES(Permission denied)elseret = (unsigned int) msq->msg_perm.seq * MSGMNI + id; /如果返回值ID为0,则允许访问进程间通讯资源IPC,序列编号和msgque下标被编码在返回值里。这个编号将成为调用者要传递给sys_msgsnd、sys_msgrcv,以及sys_msgctl的msgid参数。unlock_kernel();/释放内核return ret; /返回ret值 流程图:定位消息队列函数findkey的分析:static int findkey (key_t key) /定位具有给定键值的消息队列int id;struct msqid_ds *msq;for (id = 0; id <= max_msqid; id+) /开始对消息队列(msgque)里所有可能被占据的消息单元进行循环搜索,max_msqid表示消息队列里被占据的最大数组元素while (msq = msgqueid) = IPC_NOID) /如果当前数组元素值是IPC_NOID,那么就会在那里创建一个消息队列interruptible_sleep_on (&msg_lock); /这个新创建的消息队列可能具有正在被搜索的键值,调用interruptible_sleep_on函数使findkey函数等待该队列的创建工作完成if (msq = IPC_UNUSED) /判断该消息队列(msgque)的项目是否未被使用continue; /如果该消息队列(msgque)的项目未被使用,那么该消息队列(msgque)明显不具有匹配的键值if (key = msq->msg_perm.key) /判断键值是否匹配return id; /如果匹配的键值被找到,则返回相应的数组下标return -1; /循环结束,但是仍然未找到相互匹配的键值,则返回值1以表示失败流程图:创建消息队列函数newque的分析:static int newque (key_t key, int msgflg) /定位一个没有使用的msgque项,并尝试在那里创建一个新的消息队列int id; /* 定义一个局部变量用于循环搜索消息队列 */struct msqid_ds *msq;struct ipc_perm *ipcp;for (id = 0; id < MSGMNI; id+) /循环对消息队列进行搜索if (msgqueid = IPC_UNUSED) /判断消息队列中是否有未使用的指针msgqueid = (struct msqid_ds *) IPC_NOID; /如果找到了未使用的msgque项,就用IPC_NOID来标记它goto found; /跳转到fo