第八章 进程通信

    xiaoxiao2021-03-25  107

    一、微内核和宏内核

    宏内核包括 UNIX,MS-DOS,linux

    微内核有 Mach,Windows/NT,Minix

    二、进程间通信IPC

    分析程序a的执行过程,程序a执行效果如下图

    发送消息方

    1.调用get_ticks()获得

    kernel/main.c

    void TestA()

    {while (1) {

                       printf("<Ticks:%d>", get_ticks());

                       milli_delay(200);

             }

    }

    2. get_ticks()会调用send_recv

    kernel/main.c

    PUBLIC int get_ticks()

    {

             MESSAGEmsg;

             reset_msg(&msg);                //清空消息

             msg.type =GET_TICKS;       // GET_TICKS=2

             send_recv(BOTH,TASK_SYS, &msg);      // BOTH =3 //

             returnmsg.RETVAL;   RETVAL=u.m3.m3il

    }

    3. send_recv调用系统级的函数sendrec

    kernel/proc.c

    /*****************************************************************************

     *                                send_recv

    *****************************************************************************/

    PUBLIC int send_recv(int function, int src_dest, MESSAGE*msg)//用户级的

    {

             int ret =0;

             if(function == RECEIVE)

                       memset(msg,0, sizeof(MESSAGE));

             switch(function) {

             case BOTH:                                     // BOTH=3

                       ret= sendrec(SEND,src_dest, msg);

                       if(ret == 0)

                                ret= sendrec(RECEIVE,src_dest, msg);

                       break;

             case SEND:                            // SEND=

             case RECEIVE:              //RECEIVE=2

                       ret= sendrec(function,src_dest, msg);

                       break;

             default:

                       assert((function== BOTH) ||

                              (function == SEND) || (function ==RECEIVE));

                       break;

             }

             return ret;

    }

    4. sendrec就是sys_sendrec函数,此函数调用msg_receivemsg_send

    [1]a/kernel/syscall.asm

    sendrec:

             mov eax,_NR_sendrec   //_NR_sendrec=0x1

             mov ebx,[esp + 4]   ; function

             mov ecx,[esp + 8]   ; src_dest

             mov edx,[esp + 12] ;  p_msg

             int    INT_VECTOR_SYS_CALL

             ret

     

    [2]kernel/protect.c

     

    PUBLIC void init_prot()

    {

    …………..

    init_idt_desc(INT_VECTOR_SYS_CALL,   DA_386IGate,sys_call,                            PRIVILEGE_USER);

    …………..

    }

    [3]kernel/kernel.asm

    sys_call:

             call    save

             sti

             push  esi

     

             push  dword [p_proc_ready]

             push  edx

             push  ecx

             push  ebx

             call    [sys_call_table + eax * 4]

             add   esp, 4 * 4

     

             pop   esi

             mov     [esi + EAXREG - P_STACKBASE], eax

             cli

             ret

     

    [4]kerne/global.c

    PUBLIC       system_call         sys_call_table[NR_SYS_CALL] = {sys_printx,sys_sendrec};

    发现sendrec会调用sys_sendrec

    kernel/proc.c

    PUBLIC intsys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p)

    {

             assert(k_reenter== 0);         /* make sure we are not inring0 */

             assert((src_dest>= 0 && src_dest < NR_TASKS + NR_PROCS) ||

                   src_dest == ANY ||

                   src_dest == INTERRUPT);

             int ret = 0;

             int caller = proc2pid(p);

             MESSAGE* mla = (MESSAGE*)va2la(caller,m);

             mla->source = caller;

             assert(mla->source!= src_dest);

             /**

              * Actually we have the third message type:BOTH. However, it is not

              * allowed to be passed to the kernel directly.Kernel doesn't know

              * it at all. It is transformed into a SENDfollowed by a RECEIVE

              * by `send_recv()'.

              */

             if (function == SEND) {

                       ret = msg_send(p, src_dest,m);

                       if (ret != 0)

                                return ret;

             }

             else if (function == RECEIVE) {

                       ret = msg_receive(p,src_dest, m);

                       if (ret != 0)

                                return ret;

             }

             else {

                       panic("{sys_sendrec}invalid function: "

                             "%d (SEND:%d, RECEIVE:%d).",function, SEND, RECEIVE);

             }

             return 0;

    }

    5.msg_sendmsg_receive

    其中使用的函数

    ldt_seg_linear()每个进程都有自己的LDT,位于进程表的中间,这个函数就是根据LDT中描述符的索引来求得描述符所指向的段的基地址。

    va2la()用来由虚拟地址求线性地址,它用到了ldt_seg_linear()

    reset_msg()用于把一个消息的每个字节清零。

    block()阻塞一个进程

    unbock()解除一个进程的阻塞

    deadlock()简单地判断是否发生死锁。方法是判断消息的发送是否构成一个环,如果构成环则意味着发生死锁,比如A试图发消息给B,同时B试图给C,C试图给A发消息,那么死锁就发生了,因为A,B,C三个进程都将无限等待下去

     

    /*****************************************************************************

     *                                msg_send

     *****************************************************************************/

    *****************************************************************************/

    PRIVATEint msg_send(struct proc* current, int dest, MESSAGE* m)

    {

             structproc* sender = current;

             structproc* p_dest = proc_table + dest; /* proc dest */

     

             assert(proc2pid(sender)!= dest);

     

             /* checkfor deadlock here */

             if(deadlock(proc2pid(sender), dest)) {

                       panic(">>DEADLOCK<<%s->%s", sender->name, p_dest->name);

             }

     

             if((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg*/

                 (p_dest->p_recvfrom == proc2pid(sender)||

                  p_dest->p_recvfrom == ANY)) {

                       assert(p_dest->p_msg);

                       assert(m);

     

                       phys_copy(va2la(dest,p_dest->p_msg),

                                  va2la(proc2pid(sender), m),

                                  sizeof(MESSAGE));

                       p_dest->p_msg= 0;

                       p_dest->p_flags&= ~RECEIVING; /* dest has received the msg */

                       p_dest->p_recvfrom= NO_TASK;

                       unblock(p_dest);

     

                       assert(p_dest->p_flags== 0);

                       assert(p_dest->p_msg== 0);

                       assert(p_dest->p_recvfrom== NO_TASK);

                       assert(p_dest->p_sendto== NO_TASK);

                       assert(sender->p_flags== 0);

                       assert(sender->p_msg== 0);

                       assert(sender->p_recvfrom== NO_TASK);

                       assert(sender->p_sendto== NO_TASK);

             }

             else { /*dest is not waiting for the msg */

                       sender->p_flags|= SENDING;

                       assert(sender->p_flags== SENDING);

                       sender->p_sendto= dest;

                       sender->p_msg= m;

     

                       /*append to the sending queue */

                       structproc * p;

                       if(p_dest->q_sending) {

                                p= p_dest->q_sending;

                                while(p->next_sending)

                                         p= p->next_sending;

                                p->next_sending= sender;

                       }

                       else{

                                p_dest->q_sending= sender;

                       }

                       sender->next_sending= 0;

     

                       block(sender);

     

                       assert(sender->p_flags== SENDING);

                       assert(sender->p_msg!= 0);

                       assert(sender->p_recvfrom== NO_TASK);

                       assert(sender->p_sendto== dest);

             }

     

             return 0;

    }

     

     

    /*****************************************************************************

     *                                msg_receive

     *****************************************************************************/

    *****************************************************************************/

    PRIVATEint msg_receive(struct proc* current, int src, MESSAGE* m)

    {

             structproc* p_who_wanna_recv = current; /**

                                                              * This name is a little bit

                                                              * wierd, but it makes me

                                                              * think clearly, so I keep

                                                              * it.

                                                              */

             structproc* p_from = 0; /* from which the message will be fetched */

             structproc* prev = 0;

             int copyok= 0;

     

             assert(proc2pid(p_who_wanna_recv)!= src);

     

             if((p_who_wanna_recv->has_int_msg) &&

                 ((src== ANY) || (src == INTERRUPT))) {

                       /*There is an interrupt needs p_who_wanna_recv's handling and

                        * p_who_wanna_recv is ready to handle it.

                        */

     

                       MESSAGEmsg;

                       reset_msg(&msg);

                       msg.source= INTERRUPT;

                       msg.type= HARD_INT;

                       assert(m);

                       phys_copy(va2la(proc2pid(p_who_wanna_recv),m), &msg,

                                  sizeof(MESSAGE));

     

                       p_who_wanna_recv->has_int_msg= 0;

     

                       assert(p_who_wanna_recv->p_flags== 0);

                       assert(p_who_wanna_recv->p_msg== 0);

                       assert(p_who_wanna_recv->p_sendto== NO_TASK);

                       assert(p_who_wanna_recv->has_int_msg== 0);

     

                       return0;

             }

     

     

             /* Arriveshere if no interrupt for p_who_wanna_recv. */

             if (src ==ANY) {

                       /*p_who_wanna_recv is ready to receive messages from

                        * ANY proc, we'll check the sending queue andpick the

                        * first proc in it.

                        */

                       if(p_who_wanna_recv->q_sending) {

                                p_from= p_who_wanna_recv->q_sending;

                                copyok= 1;

     

                                assert(p_who_wanna_recv->p_flags== 0);

                                assert(p_who_wanna_recv->p_msg== 0);

                                assert(p_who_wanna_recv->p_recvfrom== NO_TASK);

                                assert(p_who_wanna_recv->p_sendto== NO_TASK);

                                assert(p_who_wanna_recv->q_sending!= 0);

                                assert(p_from->p_flags== SENDING);

                                assert(p_from->p_msg!= 0);

                                assert(p_from->p_recvfrom== NO_TASK);

                                assert(p_from->p_sendto== proc2pid(p_who_wanna_recv));

                       }

             }

             else {

                       /*p_who_wanna_recv wants to receive a message from

                        * a certain proc: src.

                        */

                       p_from= &proc_table[src];

     

                       if((p_from->p_flags & SENDING) &&

                           (p_from->p_sendto ==proc2pid(p_who_wanna_recv))) {

                                /*Perfect, src is sending a message to

                                 * p_who_wanna_recv.

                                 */

                                copyok= 1;

     

                                structproc* p = p_who_wanna_recv->q_sending;

                                assert(p);/* p_from must have been appended to the

                                             * queue, so the queue must not be NULL

                                             */

                                while(p) {

                                         assert(p_from->p_flags& SENDING);

                                         if(proc2pid(p) == src) { /* if p is the one */

                                                   p_from= p;

                                                   break;

                                         }

                                         prev= p;

                                         p= p->next_sending;

                                }

     

                                assert(p_who_wanna_recv->p_flags== 0);

                                assert(p_who_wanna_recv->p_msg== 0);

                                assert(p_who_wanna_recv->p_recvfrom== NO_TASK);

                                assert(p_who_wanna_recv->p_sendto== NO_TASK);

                                assert(p_who_wanna_recv->q_sending!= 0);

                                assert(p_from->p_flags== SENDING);

                                assert(p_from->p_msg!= 0);

                                assert(p_from->p_recvfrom== NO_TASK);

                                assert(p_from->p_sendto== proc2pid(p_who_wanna_recv));

                       }

             }

     

             if (copyok){

                       /*It's determined from which proc the message will

                        * be copied. Note that this proc must havebeen

                        * waiting for this moment in the queue, so weshould

                        * remove it from the queue.

                        */

                       if(p_from == p_who_wanna_recv->q_sending) { /* the 1st one */

                                assert(prev== 0);

                                p_who_wanna_recv->q_sending= p_from->next_sending;

                                p_from->next_sending= 0;

                       }

                       else{

                                assert(prev);

                                prev->next_sending= p_from->next_sending;

                                p_from->next_sending= 0;

                       }

     

                       assert(m);

                       assert(p_from->p_msg);

                       /*copy the message */

                       phys_copy(va2la(proc2pid(p_who_wanna_recv),m),

                                  va2la(proc2pid(p_from), p_from->p_msg),

                                  sizeof(MESSAGE));

     

                       p_from->p_msg= 0;

                       p_from->p_sendto= NO_TASK;

                       p_from->p_flags&= ~SENDING;

                       unblock(p_from);

             }

             else {  /* nobody's sending any msg */

                       /*Set p_flags so that p_who_wanna_recv will not

                        * be scheduled until it is unblocked.

                        */

                       p_who_wanna_recv->p_flags|= RECEIVING;

     

                       p_who_wanna_recv->p_msg= m;

     

                       if(src == ANY)

                                p_who_wanna_recv->p_recvfrom= ANY;

                       else

                                p_who_wanna_recv->p_recvfrom= proc2pid(p_from);

     

                       block(p_who_wanna_recv);

     

                       assert(p_who_wanna_recv->p_flags== RECEIVING);

                       assert(p_who_wanna_recv->p_msg!= 0);

                       assert(p_who_wanna_recv->p_recvfrom!= NO_TASK);

                       assert(p_who_wanna_recv->p_sendto== NO_TASK);

                       assert(p_who_wanna_recv->has_int_msg== 0);

             }

     

             return 0;

    }

    接收消息方

    新建一个系统进程来接收用户进程的消息,并且返回ticks值。新建一个系统进程,叫SYSTASK

    1.kernel/main.c

    PUBLIC int kernel_main()

    {

             …..

             p_task    = task_table + i;           //内核任务

             …..

    }

     

    2.kernel/global.c

    PUBLIC       structtask  task_table[NR_TASKS] = {

             {task_tty,STACK_SIZE_TTY, "TTY"},

             {task_sys,STACK_SIZE_SYS, "SYS"}};      //任务名是SYS,任务将执行的函数是task_sys,

     

    3. kernel/systask.c

    /*****************************************************************************

    * <Ring 1> The main loop of TASK SYS.

    *****************************************************************************/

    PUBLIC void task_sys()

    {

             MESSAGEmsg;

             while (1) {

                       send_recv(RECEIVE,ANY, &msg);

                       intsrc = msg.source;

                       switch(msg.type) {

                      case GET_TICKS:

                                msg.RETVAL= ticks;

                                send_recv(SEND,src, &msg);  //发送消息

                                break;

                       default:

                                panic("unknownmsg type");

                                break;

                       }

             }

    }

    新的结构体

    1.进程表的新成员proc

    a/include/proc.h

    structproc {

             struct stackframe regs;    /* process registers saved in stack frame*/

             u16 ldt_sel;               /* gdt selector giving ldt baseand limit */

             struct descriptor ldts[LDT_SIZE]; /*local descs for code and data */

            int ticks;                 /* remained ticks */

            int priority;

             u32 pid;                   /* process id passed in fromMM */

             char name[16];              //进程名

             int  p_flags;          //用于标明进程的状态,有三种,第一种:0进程正在运行,第二种:SENDING进程处于发送消息的状态。由于消息还未送达,进程被阻塞。第三种:RECEIVING进程处于接收消息的状态。由于消息还未收到,进程被阻塞。

             MESSAGE * p_msg;     //指向消息体的指针

             int p_recvfrom;            //假设进程P想要接收消息,但目前没有进程发送消息给它,本成员记录P想要从谁那里接收消息

             int p_sendto;                //假设进程P想要发送消息,但目前没有进程接收它,本成员记录P想要发送消息给谁

             inthas_int_msg;     //如果有一个终端需要某进程来处理,或者换句话说,某进程正在等待一个中断发生——比如硬盘驱动可能会等待

    硬盘中断的发生,系统在得知中断发生后会将此位置为1。

             struct proc *q_sending;  //如果有若干进程—比如啊a,b和c—都向同一个进程P发送消息,而P此时并未准备接受消息,那么A,B,C将会排成一个队列,进程P的q_sending指向第一个试图发送消息的进程

             struct proc *next_sending;  //试图发送消息A,B,C三进程排成的队列的实现方式是这样的:目的进程P的进程表的q_sending指向A,进程A的进程表next_sending指向B,进程B的进程表的next_sending指向C,进程C的进程表的next_sending指向空

     

             int nr_tty;

    };

    2.发送消息的流程

    假设有进程A想要向B发送消息M,那么过程将会是这样的:

    A首先准备好M

    A通过系统调用sendrec,最终调用msg_send

    简单判断是否发生死锁

    判断目标进程B是否正在等待来自A的消息:

             如果是:消息被复制给B,B被解除阻塞,继续运行

             如果否:A被阻塞,并被加入到B的发送队列中

     

    假设有进程B想要接收消息(来自特定进程,中断或者任意进程),那么过程将会是这样的

    1. B准备一个空的消息结构体M, 用于接收消息。

    2. B通过系统调用sendrec, 最终调用msg_receive。 3. 判断B是否有个来自硬件的消息( 通过has_int_msg) , 如果是, 并且B准备接收来自中断的消息或准备接收任意消息, 则马上准备一个消息给B, 并返回。 4. 如果B想接收来自任意进程的消息, 则从自己的发送队列中选取第一个( 如果队列非空的话) , 将其消息复制给M。 5. 如果B是想接收来自特定进程A的消息, 则先判断A是否正在等待向B发送消息, 若是的话, 将其消息复制给M。 6. 如果此时没有任何进程发消息给B, B会被阻塞。 值得说明的是, 不管是接收方还是发送方, 都各自维护一个消息结构体, 只不过发送方的结构体是携带了消息内容的而接收方

     

    3.新加的函数assert()panic()

    #define ASSERT

    #ifdef ASSERT

    voidassertion_failure(char *exp, char *file, char *base_file, int line);

    #defineassert(exp)  if (exp) ; \

            else assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__)

    #else

    #define assert(exp)

    #endif

     

    __FILE__将被展开成当前输入的文件。在这里,它告诉我们哪个文件中产生了异常。

    __BASE_FILE__可被认为是传递给编译器的那个文件名。比如你在m.c中包含了n.hn.h中的某一个assert函数失败

    了,__FILE__n.h __BASE_FILE__m.c

    __LINE__将被展开成当前的行号。

    4.新的调度算法,加入阻塞

    当p_flags为0时才让其运行

    kernel/proc.c

    PUBLIC voidschedule()

    {

             struct proc*         p;

             int              greatest_ticks= 0;

             while (!greatest_ticks) {

                       for (p = &FIRST_PROC; p<= &LAST_PROC; p++) {

                                if (p->p_flags ==0) {

                                          if (p->ticks > greatest_ticks) {

                                                   greatest_ticks= p->ticks;

                                                   p_proc_ready= p;

                                         }

                                }

                       }

                       if (!greatest_ticks)

                                for (p =&FIRST_PROC; p <= &LAST_PROC; p++)

                                         if(p->p_flags == 0)

                                                   p->ticks= p->priority;

             }

    }

     

    转载请注明原文地址: https://ju.6miu.com/read-19169.html

    最新回复(0)