• 进程间通信(IPC)+进程加锁解锁


    【0】README


    【1】看看,我们的进程代码

    这里写图片描述

    【2】看看IPC替换系统调用 get_ticks 后的调用过程

    (本图片是对 source insight 工具打开的系统调用所涉及函数的截图,图片过大,建议使用 在“在新标签页中打开图片”)
    这里写图片描述
    这里写图片描述

    对上面两张图的分析(Analysis):(上图中的缓冲区 or 消息发送队列,进程间通信可能用到,也可能用不到)

    • A1)要知道 进程A -> get_ticks -> send_recv -> msg_send or msg_receive 等;
    • A2)系统进程 task_sys 率先启动,调用send_recv(RECEIVE, ANY, &msg)即msg_receive 等待接收消息 ,由于没有任何进程发送消息给它,该进程阻塞;我们看看阻塞代码:
      这里写图片描述
    • A3) 进程A启动,发送消息给 task_sys,进入 系统调用msg_send,关键代码如下:
      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)
      显然,msg_send 里面的if 语句,系统进程的结构体成员是满足的,所以 进程A向系统进程 task_sys 发送消息成功,(也就是吧进程A的 消息结构体 内容 copy 到 系统进程task_sys 的消息结构体存储空间,由函数 phys_copy 完成)

    • A4)消息发送成功后,返回0, send_recv 函数立即调用 ret = sendrec(RECEIVE, src_dest, msg) 即msg_receive 系统调用 接收 系统进程task_sys 发来的消息;因为 send_recv 中的 src_dest 是指定好了的, src_dest = task_sys(只不过,它传入的参数是task_sys 进程的 进程表 索引)

    • A5)系统进程 task_sys 收到消息后,向先前的 消息发送者(进程A) 发送消息,该消息封装了 ticks 的value值;
    • A6)系统进程 task_sys 调用 msg_send 系统调用,关键代码如下:
      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)
      显然,msg_send 里面的if 语句,进程A 的结构体成员是满足的,所以 系统进程task_sys 向 进程 A 发送消息成功;

    • A7)进程A 收到消息,抽取出 RETVAL, 关键代码如下:

          send_recv(BOTH, TASK_SYS, &msg);TASK_SYS=1
          return msg.RETVAL;
      

      这里写图片描述


    【3】我们看看 assert (panic类似)怎么调用的?

    这里写图片描述
    这里写图片描述

    对sys_printx函数的分析(Analysis):

    • A1)当sys_printx 发现传入字符串的第一个字符是MAG_CH_ASSERT时,会同时判断调用 系统调用的进程是系统进程还是用户进程(通过 sys_printx函数中if 语句 的 p_proc_ready < &proc_table[NR_TASKS]) 判断);
    • A2)如果是系统进程,则停止整个系统的运转,并将要打印的字符串打印在显存的各处;
    • A3)如果是用户进程, 则打印后像一个普通进程一样返回,届时该用户进程会因为 assertion_failure() 中对 函数spin 的调用而进 死循环;
    • Attention):换句话说, 系统进程的 assert 失败会导致系统停止运转(hlt), 而用户进程的失败仅仅导致 自己停转(spin)

    【4】我们接着看 进程的加锁 + 解锁

    以下有5副图片,第一幅图片是重中之重,给出了 加锁和解锁函数的定义,以及进程切换函数 schedule, 建议在分析的时候,把后4副图片和 第1 副图片做对照,效果更好。

    这里写图片描述
    4.1)进程加锁 在何时发生?(block)(这里p->p_flags 置!0 是关键)

    • case1)在msg_send 函数中: 如果消息接收者 没有 指定 该消息发送者,或者接收者没有准备好接收消息,则消息发送者会添加到 接收者的消息发送队列中;【 在本例中,消息发送者是进程A, 它是用户进程,所以进程A被阻塞(spin 死循环函数)】
      这里写图片描述
    • 阻塞(加锁)过程解析:由于 sender->p_flags |= SENDING(参见上图代码),显然 sender->p_flags 不等于零,不会进入assert 函数,直接进入 schedule()进行进程切换,显然 schedule 将CPU控制权切换到那些 p_flags == 0 的进程手里面;也即是只要p->p_flags !=0 ,那么该进程p 就永远也无法进行进程切换,获得CPU控制权(此谓阻塞)

    • 我们这里再添加p_flags 的作用,p_flags 用于表明进程的状态,取值3种(下文不再叙述有关 p_flags 的作用):

      • 1)value = 0:表明进程正在运行或准备运行;
      • 2)value = SENDING(宏定义,具体你不管,反正 !0): 表明进程处于发送消息状态,由于消息没有被送达(该进程还处于接收者进程的发送队列中), 消息发送者进程被阻塞;
      • 3)value = RECEIVING(宏定义,具体你不管,反正 !0): 表明进程处于接收消息状态,由于没有接收到消息, 消息接收者进程被阻塞;
    • case2) 在msg_receive 函数中: 如果没有任何进程向 当前进程发送消息的话,当前进程会阻塞,直到有进程发送消息给当前进程;【举个荔枝:在本例中,当前进程是个系统进程 task_sys,所以这样的话,整个系统会停止运转;】
      这里写图片描述

    • 阻塞(加锁)过程解析:由于 p_who_wanna_recv->p_flags |= RECEIVING (参见上图代码),显然 p_who_wanna_recv->p_flags 不等于零,不会进入assert 函数,直接进入 schedule()进行进程切换,显然 schedule 将CPU控制权切换到那些 p_flags == 0 的进程手里面;也即是只要p->p_flags !=0 ,那么该进程p 就永远也无法进行进程切换,获得CPU控制权(此谓阻塞)

    4.2)进程解锁在何时发生?(unblock)(这里p->p_flags 置0 是关键)

    • case1)在msg_send 函数中:如果处于 RECEIVING 状态的进程 接收到了消息,则 就会对该进程进行解锁;判断p->p_flags==0,【举个荔枝:在本例中,消息接收者进程是个系统进程 task_sys,所以这样的话,整个系统会停止运转】;
      这里写图片描述
    • 解锁过程解析:由于 p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */(参见上图代码),显然 p_dest->p_flags ==0,(0==0 -> TRUE)也不会进入assert 函数;紫薇曾经问过:你还记得大明湖畔的夏雨荷吗?我也来问一句:你还记得 加锁过程中调用的 schedule 吗? schedule 函数 阻塞该进程的法宝就是 当其 p_flags非零时,就不会对该进程进行CPU控制权的转让,因为if 语句根本就不满足;现在好了, 在解锁前 p_dest->p_flags &= ~RECEIVING(因为接收到消息前,p_flags==RECEIVING,当然和~RECEIVE 进行逻辑与后 得到0) ;也即是 原先阻塞的进程的p_flags==0 了,它就可以获得CPU控制权了(转向到schedule),这也就解锁了,Bingo!(此谓解锁)

    • case2)在msg_receive 函数中:如果接收者进程 接收任一消息,就从其发送队列中取出一个发送进程,然后发送进程解锁;
      这里写图片描述

    • 解锁过程解析:由于 p_from->p_flags &= ~SENDING(参见上图代码),显然 p_dest->p_flags ==0,(0==0 -> TRUE)也不会进入assert 函数;
      紫薇曾经问过:你还记得大明湖畔的夏雨荷吗?我也来问一句:你还记得 加锁过程中调用的 schedule 吗? schedule 函数 阻塞该进程的法宝就是 当其 p_flags非零时,就不会对该进程进行CPU控制权的转让,因为if 语句根本就不满足;现在好了, 在解锁前 p_dest->p_flags &= ~SENDING(因为接收到消息前,p_flags==SENDING,当然和~SENDING进行逻辑与后 得到0) ;也即是 原先阻塞的进程的p_flags==0 了,它就可以获得CPU控制权(转向到schedule),这也就解锁了,Bingo!(此谓解锁)

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    java:字符串(下)
    java字符串(上)
    Java流程控制:循环语句
    JAVA学习笔记(二十三)
    JAVA学习笔记(二十二)
    JAVA学习笔记(二十一)
    JAVA学习笔记(二十)
    JAVA学习笔记(十九)
    JAVA学习笔记(十八)
    JAVA学习笔记(十七)
  • 原文地址:https://www.cnblogs.com/pacoson/p/4893139.html
Copyright © 2020-2023  润新知