当前位置:   article > 正文

冬天OS(二十三):宏内核改微内核_os微内核改造

os微内核改造

--------------------------------------------------------

IPC 消息机制的建立

--------------------------------------------------------

宏内核 VS 微内核
将内核完成的任务交给专门的任务进程来完成:例如 get_ticks 系统调用,在宏内核中 get_ticks 是作为一个系统调用函数存在的,通过系统调用函数表,内核在 ring0 执行函数 get_ticks,get_ticks 返回,这次系统调用才算结束,而在微内核中,get_ticks 不是系统调用,而是作为某个任务进程将要执行的功能函数而存在的,内核在 ring0 通过发送消息唤醒任务进程之后就立即返回!
 

 

一,建立消息通信机制

  1. ·sys_send 函数:
  2. PRIVATE int msg_send(struct proc *current, int dest, MESSAGE *m)// current 是谁要发 { // dest 是发给谁 // m 是要发送的消息
  3. struct proc *sender = current;
  4. struct proc *p_dest = proc_table + dest;
  5. // 确保 sender 不是给自己发
  6. assert(dest != proc2pid(sender));
  7. /* 确保没有死锁 */
  8. if (deadlock(proc2pid(sender), dest))
  9. panic(">>DEADLOCK<< %s->%s", sender->name, p_dest->name);
  10. if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */
  11. (p_dest->p_recvfrom == proc2pid(sender) ||
  12. p_dest->p_recvfrom == ANY))
  13. {
  14. assert(p_dest->p_msg); // p_dest->p_msg 的内存必须要开辟好
  15. assert(m);
  16. phys_copy(va2la(dest, p_dest->p_msg), // p_dest 的 msg 的线性地址同样是等于:[p_dest 的 DS : offset(p_msg)]
  17. va2la(proc2pid(sender), m), // 内核态下所有对用户态下的数据的访问,都要基于这种形式
  18. sizeof(MESSAGE));
  19. p_dest->p_msg = 0;
  20. p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */
  21. p_dest->p_recvfrom = NO_TASK; // 清空 p_dest 上次是因为等待谁而阻塞!
  22. unblock(p_dest);
  23. assert(p_dest->p_flags == 0);
  24. assert(p_dest->p_msg == 0);
  25. assert(p_dest->p_recvfrom == NO_TASK);
  26. assert(p_dest->p_sendto == NO_TASK);
  27. assert(sender->p_flags == 0);
  28. assert(sender->p_msg == 0);
  29. assert(sender->p_recvfrom == NO_TASK);
  30. assert(sender->p_sendto == NO_TASK);
  31. }
  32. else
  33. { /* dest is not waiting for the msg */
  34. sender->p_flags |= SENDING;
  35. assert(sender->p_flags == SENDING);
  36. sender->p_sendto = dest;
  37. sender->p_msg = m; // m 其实就是 sender 要发送给 dest 的包,
  38. // 只是这里在构建 sender 的阻塞状态的时候需要让 p_msg 指向 m
  39. /* append to the sending queue */
  40. struct proc *p;
  41. if (p_dest->q_sending)
  42. {
  43. p = p_dest->q_sending;
  44. while (p->next_sending)
  45. p = p->next_sending;
  46. p->next_sending = sender;
  47. }
  48. else
  49. p_dest->q_sending = sender;
  50. sender->next_sending = 0;
  51. block(sender); // 把控制权交给别人,等同于自己同时阻塞
  52. assert(sender->p_flags == SENDING);
  53. assert(sender->p_msg != 0);
  54. assert(sender->p_recvfrom == NO_TASK);
  55. assert(sender->p_sendto == dest);
  56. }
  57. return 0;
  58. }

 ——sys_send 首先简单地检查是否产生死锁,然后检查如果 recv 方正在 recving 并且 want recv 就是自己(或者 any),就将消息赋值给 recv 方,然后唤醒对方!否则 recv 方没打算接收或者想接收的不是自己,就进行阻塞前的准备设置然后阻塞!
 

  1. ·sys_receive 函数:
  2. PRIVATE int msg_receive(struct proc *current, int src, MESSAGE *m) // m:将消息往回收到 m 中
  3. { // sec:从哪里收
  4. // current:谁想回收
  5. struct proc *p_who_wanna_recv = current;
  6. struct proc *p_from = 0; /* from which the message will be fetched */
  7. struct proc *prev = 0;
  8. int copyok = 0;
  9. assert(proc2pid(p_who_wanna_recv) != src); // 不能从自己接收
  10. if ((p_who_wanna_recv->has_int_msg) &&
  11. ((src == ANY) || (src == INTERRUPT)))
  12. {
  13. /* There is an interrupt needs p_who_wanna_recv's handling and
  14. * p_who_wanna_recv is ready to handle it.
  15. */
  16. MESSAGE msg;
  17. reset_msg(&msg);
  18. msg.source = INTERRUPT;
  19. msg.type = HARD_INT;
  20. assert(m);
  21. // 代表着接收到了中断消息,然后满载而归!
  22. // 注意这里并没有随之唤醒“中断进程”,因为中断不会阻塞着等待接收进程接收!
  23. phys_copy(va2la(proc2pid(p_who_wanna_recv), m), &msg,
  24. sizeof(MESSAGE));
  25. p_who_wanna_recv->has_int_msg = 0;
  26. assert(p_who_wanna_recv->p_flags == 0);
  27. assert(p_who_wanna_recv->p_msg == 0);
  28. assert(p_who_wanna_recv->p_sendto == NO_TASK);
  29. assert(p_who_wanna_recv->has_int_msg == 0);
  30. return 0;
  31. }
  32. /* Arrives here if no interrupt for p_who_wanna_recv. */
  33. if (src == ANY)
  34. {
  35. /* p_who_wanna_recv is ready to receive messages from
  36. * ANY proc, we'll check the sending queue and pick the
  37. * first proc in it.
  38. */
  39. if (p_who_wanna_recv->q_sending)
  40. {
  41. // p_frome 就是 p_who_wanna_recv 等待队列上的第一个
  42. p_from = p_who_wanna_recv->q_sending;
  43. copyok = 1;
  44. assert(p_who_wanna_recv->p_flags == 0);
  45. assert(p_who_wanna_recv->p_msg == 0);
  46. assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
  47. assert(p_who_wanna_recv->p_sendto == NO_TASK);
  48. assert(p_who_wanna_recv->q_sending != 0);
  49. assert(p_from->p_flags == SENDING);
  50. assert(p_from->p_msg != 0);
  51. assert(p_from->p_recvfrom == NO_TASK);
  52. assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
  53. }
  54. }
  55. else if (src >= 0 && src < NR_TASKS + NR_PROCS)
  56. {
  57. /* p_who_wanna_recv wants to receive a message from
  58. * a certain proc: src.
  59. */
  60. // 接收者就想接收指定进程的消息,那么开始判断指定进程是否在发送消息并且在给自己发送消息
  61. // 之后把他的数据接受了并把它唤醒,然后从自己的发送阻塞队列上剔除!
  62. p_from = &proc_table[src];
  63. if ((p_from->p_flags & SENDING) &&
  64. (p_from->p_sendto == proc2pid(p_who_wanna_recv)))
  65. {
  66. /* Perfect, src is sending a message to
  67. * p_who_wanna_recv.
  68. */
  69. copyok = 1;
  70. struct proc *p = p_who_wanna_recv->q_sending;
  71. assert(p); /* p_from must have been appended to the
  72. * queue, so the queue must not be NULL
  73. */
  74. // 这里在 p_who_wanna_recv 的 q_sending 队列上找到那个具体的进程
  75. // 估计要做更新等待列表的工作
  76. while (p)
  77. {
  78. assert(p_from->p_flags & SENDING);
  79. if (proc2pid(p) == src) /* if p is the one */
  80. break;
  81. prev = p;
  82. p = p->next_sending;
  83. }
  84. assert(p_who_wanna_recv->p_flags == 0);
  85. assert(p_who_wanna_recv->p_msg == 0);
  86. assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
  87. assert(p_who_wanna_recv->p_sendto == NO_TASK);
  88. assert(p_who_wanna_recv->q_sending != 0);
  89. assert(p_from->p_flags == SENDING);
  90. assert(p_from->p_msg != 0);
  91. assert(p_from->p_recvfrom == NO_TASK);
  92. assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
  93. }
  94. }
  95. if (copyok)
  96. {
  97. /* It's determined from which proc the message will
  98. * be copied. Note that this proc must have been
  99. * waiting for this moment in the queue, so we should
  100. * remove it from the queue.
  101. */
  102. // update the queue!
  103. if (p_from == p_who_wanna_recv->q_sending)
  104. { /* the 1st one */
  105. assert(prev == 0);
  106. p_who_wanna_recv->q_sending = p_from->next_sending;
  107. p_from->next_sending = 0;
  108. }
  109. else
  110. {
  111. assert(prev);
  112. prev->next_sending = p_from->next_sending;
  113. p_from->next_sending = 0;
  114. }
  115. assert(m);
  116. assert(p_from->p_msg);
  117. /* copy the message */
  118. phys_copy(va2la(proc2pid(p_who_wanna_recv), m),
  119. va2la(proc2pid(p_from), p_from->p_msg),
  120. sizeof(MESSAGE));
  121. p_from->p_msg = 0;
  122. p_from->p_sendto = NO_TASK;
  123. p_from->p_flags &= ~SENDING;
  124. // 将发送方解锁掉,下一次就会加入调度
  125. unblock(p_from);
  126. }
  127. else
  128. { /* nobody's sending any msg */
  129. /* Set p_flags so that p_who_wanna_recv will not
  130. * be scheduled until it is unblocked.
  131. */
  132. p_who_wanna_recv->p_flags |= RECEIVING;
  133. p_who_wanna_recv->p_msg = m;
  134. p_who_wanna_recv->p_recvfrom = src; // recv 方阻塞自己的时候指明它原本是想从哪里接收数据!
  135. block(p_who_wanna_recv);
  136. assert(p_who_wanna_recv->p_flags == RECEIVING);
  137. assert(p_who_wanna_recv->p_msg != 0);
  138. assert(p_who_wanna_recv->p_recvfrom != NO_TASK);
  139. assert(p_who_wanna_recv->p_sendto == NO_TASK);
  140. assert(p_who_wanna_recv->has_int_msg == 0);
  141. }
  142. return 0;
  143. }

——msg_recv 首先检查是否有中断通知,如果有,就“接受”中断通知然后返回,如果没有中断通知,就判断 recv 是想从 any 接收还是从具体的进程接收,从 any 接收好办,选择 recv 的发送队列的第一个 send 然后接收他的消息就行了,如果是从具体进程 recv ,那么就要判断那个具体的进程是否正在给 recv 发送,如果正在,那么一拍即合(否则就 recv 就会阻塞的),拿走那个 send 的数据然后激活 send ,自己返回!

 

测试消息机制:
开始 TestA 阻塞在从 TestB 接收数据,并且 TestC 阻塞在想从 ANY 接收数据,然后 TestB 发消息给 TestA,TestA 接收到 TestB 的消息了之后又给 TestC 发送消息!

 

二,宏内核改微内核
消息机制是微内核的核心,我们既然已经有了消息机制,就可以搭建微内核了,上面消息机制的测试中,TestX 都是发送或者接收了一次消息之后就不再发送或者接收消息了,如果我们弄一个进程,在那里不间断地 recv ,再给 MESSAGE 注明类型,我们就可以说:哪个进程给哪个系统任务发送了消息,请求的是这个系统任务的哪种类型的服务!(服务的类型取决于系统任务支持什么类型的服务,不能你虽然把我唤醒了,但请求的不是我力所能及的服务,可以猜猜 sys_server 会做什么反应 :))

 

1,添加一个系统任务:sys_server
2,增加 sys_server 提供的一种服务类型(GET_TICKS)
3,TestB 和 TestC 之间互相传递消息

 

  1. ·main.c 节选
  2. PUBLIC int get_ticks()
  3. {
  4. MESSAGE msg;
  5. reset_msg(&msg);
  6. msg.type = GET_TICKS;
  7. send_recv(BOTH, TASK_SERVER, &msg);
  8. return msg.RETVAL;
  9. }
  10. void TestA()
  11. {
  12. while (TRUE)
  13. {
  14. printf("%d ", get_ticks());
  15. milli_delay(300);
  16. }
  17. }
  18. void TestB()
  19. {
  20. MESSAGE _m;
  21. _m.RETVAL = 1;
  22. while (TRUE)
  23. {
  24. send_recv(SEND, 4, &_m);
  25. send_recv(RECEIVE, 4, &_m);
  26. printf("TestB number is [%d]\n", ++_m.RETVAL);
  27. milli_delay(2000);
  28. }
  29. }
  30. void TestC()
  31. {
  32. MESSAGE _m;
  33. while (TRUE)
  34. {
  35. send_recv(RECEIVE, 3, &_m);
  36. printf("TestC number is [%d]\n", ++_m.RETVAL);
  37. send_recv(SEND, 3, &_m);
  38. }
  39. }
  1. ·sys_task.c
  2. // ----------------------------
  3. // <systask.c>
  4. // Jack Zheng
  5. // Comment:December 7, 2019
  6. // ----------------------------
  7. #include "type.h"
  8. #include "const.h"
  9. #include "protect.h"
  10. #include "string.h"
  11. #include "proc.h"
  12. #include "tty.h"
  13. #include "console.h"
  14. #include "global.h"
  15. #include "keyboard.h"
  16. #include "proto.h"
  17. void task_server()
  18. {
  19. MESSAGE _m;
  20. while (TRUE)
  21. {
  22. send_recv(RECEIVE, ANY, &_m);
  23. int src = _m.source;
  24. switch (_m.type)
  25. {
  26. case GET_TICKS: /* 某个进程请求的这个服务 */
  27. _m.RETVAL = ticks;
  28. send_recv(SEND, src, &_m);
  29. break;
  30. default:
  31. panic("unknown Message type");
  32. break;
  33. }
  34. }
  35. }

运行:

sys_server 的 GET_TICKS 是在接收到消息之后才解析消息类型然后进行相应的处理!所以进程请求系统任务服务的大致手段是:唤醒系统任务,系统任务解析服务类型,服务!

 

OK,可以看到,我们的内核从宏内核改为微内核之后优雅了许多,所谓一切都建立在消息上!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/755835
推荐阅读
相关标签
  

闽ICP备14008679号