主要内容

实现进程间的通信

Linux的系统调用与Minix的系统调用不同,以点见面可以了解一些宏内核与微内核的区别。
Linux的fork函数通过调用中断0x80,通过IDT转化为_system_call,最终通过一个函数指针数组转化为调用_sys_fork实现
Minix的调用中只有接收消息和发送消息的系统调用,总共有三种SEND,RECEIVE,BOTH。
Minix的fork函数实现需要一个内存管理器(MM),负责fork所要做的工作
对fork的调用先转化为内核态函数sys_call,sys_call将消息传递给MM,MM获得
消息后得知消息的内容是进行fork操作,就进一步调用do_fork完成。
其他系统调用也无外乎通过syscall转化为发送消息,相应的进程接收消息并处理。

进程间发送消息又被称为进程间通信(IPC),IPC又可分为同步和异步。
模仿Minix的IPC机制,新建系统调用sendrec来模拟sys_call

sendrec:mov  eax, _NR_sendrecmov ebx, [esp + 4] ; functionmov   ecx, [esp + 8] ; src_destmov   edx, [esp + 12]    ; p_msgint  INT_VECTOR_SYS_CALLret

对应的sys_sendrec
将SEND消息交给msg_send处理
将RECEIVE消息交给msg_receive处理

PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p)
{assert(k_reenter == 0);  /* make sure we are not in ring0 */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 SEND followed 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;
}

msg_send和msg_receive函数

/******************************************************************************                ldt_seg_linear*****************************************************************************/
/*** <Ring 0~1> Calculate the linear address of a certain segment of a given* proc.* * @param p   Whose (the proc ptr).* @param idx Which (one proc has more than one segments).* * @return  The required linear address.*****************************************************************************/
PUBLIC int ldt_seg_linear(struct proc* p, int idx)
{struct descriptor * d = &p->ldts[idx];return d->base_high << 24 | d->base_mid << 16 | d->base_low;
}/******************************************************************************                  va2la*****************************************************************************/
/*** <Ring 0~1> Virtual addr --> Linear addr.* * @param pid  PID of the proc whose address is to be calculated.* @param va   Virtual address.* * @return The linear address for the given virtual address.*****************************************************************************/
PUBLIC void* va2la(int pid, void* va)
{struct proc* p = &proc_table[pid];u32 seg_base = ldt_seg_linear(p, INDEX_LDT_RW);u32 la = seg_base + (u32)va;if (pid < NR_TASKS + NR_PROCS) {assert(la == (u32)va);}return (void*)la;
}/******************************************************************************                                reset_msg*****************************************************************************/
/*** <Ring 0~3> Clear up a MESSAGE by setting each byte to 0.* * @param p  The message to be cleared.*****************************************************************************/
PUBLIC void reset_msg(MESSAGE* p)
{memset(p, 0, sizeof(MESSAGE));
}/******************************************************************************                                block*****************************************************************************/
/*** <Ring 0> This routine is called after `p_flags' has been set (!= 0), it* calls `schedule()' to choose another proc as the `proc_ready'.** @attention This routine does not change `p_flags'. Make sure the `p_flags'* of the proc to be blocked has been set properly.* * @param p The proc to be blocked.*****************************************************************************/
PRIVATE void block(struct proc* p)
{assert(p->p_flags);schedule();
}/******************************************************************************                                unblock*****************************************************************************/
/*** <Ring 0> This is a dummy routine. It does nothing actually. When it is* called, the `p_flags' should have been cleared (== 0).* * @param p The unblocked proc.*****************************************************************************/
PRIVATE void unblock(struct proc* p)
{assert(p->p_flags == 0);
}/******************************************************************************                                deadlock*****************************************************************************/
/*** <Ring 0> Check whether it is safe to send a message from src to dest.* The routine will detect if the messaging graph contains a cycle. For* instance, if we have procs trying to send messages like this:* A -> B -> C -> A, then a deadlock occurs, because all of them will* wait forever. If no cycles detected, it is considered as safe.* * @param src   Who wants to send message.* @param dest  To whom the message is sent.* * @return Zero if success.*****************************************************************************/
PRIVATE int deadlock(int src, int dest)
{struct proc* p = proc_table + dest;while (1) {if (p->p_flags & SENDING) {if (p->p_sendto == src) {/* print the chain */p = proc_table + dest;printl("=_=%s", p->name);do {assert(p->p_msg);p = proc_table + p->p_sendto;printl("->%s", p->name);} while (p != proc_table + src);printl("=_=");return 1;}p = proc_table + p->p_sendto;}else {break;}}return 0;
}/******************************************************************************                                msg_send*****************************************************************************/
/*** <Ring 0> Send a message to the dest proc. If dest is blocked waiting for* the message, copy the message to it and unblock dest. Otherwise the caller* will be blocked and appended to the dest's sending queue.* * @param current  The caller, the sender.* @param dest     To whom the message is sent.* @param m        The message.* * @return Zero if success.*****************************************************************************/
PRIVATE int msg_send(struct proc* current, int dest, MESSAGE* m)
{struct proc* sender = current;struct proc* p_dest = proc_table + dest; /* proc dest */assert(proc2pid(sender) != dest);/* check for 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 */struct proc * 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*****************************************************************************/
/*** <Ring 0> Try to get a message from the src proc. If src is blocked sending* the message, copy the message from it and unblock src. Otherwise the caller* will be blocked.* * @param current The caller, the proc who wanna receive.* @param src     From whom the message will be received.* @param m       The message ptr to accept the message.* * @return  Zero if success.*****************************************************************************/
PRIVATE int msg_receive(struct proc* current, int src, MESSAGE* m)
{struct proc* p_who_wanna_recv = current; /*** This name is a little bit* wierd, but it makes me* think clearly, so I keep* it.*/struct proc* p_from = 0; /* from which the message will be fetched */struct proc* 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.*/MESSAGE msg;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);return 0;}/* Arrives here 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 and pick 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 if (src >= 0 && src < NR_TASKS + NR_PROCS) {/* 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;struct proc* 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 */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 have been* waiting for this moment in the queue, so we should* 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;p_who_wanna_recv->p_recvfrom = src;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;
}

围绕两个主要函数,作者还实现了几个必要的函数
ldt_seg_linear 根据进程的LDT中描述符来找到描述符所指向段的基地址
va2la 将虚拟地址转为线性地址
reset_msg 将一个消息清零
block 阻塞一个进程
unblock 解除一个进程的阻塞
deadlock 判断是否构成死锁

假如进程A要向B发消息M:
A准备好M,A通过系统调用sendrec,最终调用msg_send,判断是否死锁,判断B是否在等待A的消息
如果是,消息被赋值给B,B被解除阻塞,继续运行,如果不是,A被阻塞,加入到B的发送队列
假如B想接受消息:
B准备一个消息结构体M
B通过系统调用sendrec,最终调用msg_receive,判断B是否需要硬件消息,如果B能接受任意进程消息,
从消息进程队列中选取第一个,将其消息复制给M,如果B接受特定消息,判断A是否在等待向B发消息,如果是
则将消息复制给M,如果不是,B被阻塞

对于进程增加了两种可能的状态,SENDING和RECEIVING,用p_flags来表示
只有当p_flags为0是运行态时才能运行

PUBLIC void schedule()
{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;}
}

一个操作系统的实现(8)进程间通信相关推荐

  1. 《一个操作系统的实现》读书笔记连载ing……

    从昨天起深陷<一个操作系统的实现>这个书不能自拔,这本书可以说再次燃起了我小时候想写操作系统的美好愿望,所以说愿望总是美好的,还进入了大学开始真正去学计算机才发现似乎写操作系统是和我没什么 ...

  2. 一个操作系统的实现(1)

    一个操作系统的实现 说明:本文是一个简单的学习记录,不是全面给大家提供学习的文章,文章内容均代表作者的个人观点,难免会有错误.转载请保留作者信息. 2010/11/20                ...

  3. 《Orange’s 一个操作系统的实现》3.保护模式3----DOS加载.EXE过程

    在<<Orange's 一个操作系统的实现>>一书中有时使用了org 0100h,为何是0100h?因为书中的例子是为了突破引导扇区512字节的限制, 而将asm文件编译为.c ...

  4. 《Orange’s 一个操作系统的实现》3.保护模式7-特权级转移(通过调用门转移目标段-无特权级转换)...

    在上次的代码基础上,添加一个代码段作为通过调用门转移的目标段.了解一下调用的工作方法,代码分析如下: <<红色标识部分为新增代码>> ; =================== ...

  5. 一个操作系统的实现01

    老师说,写好操作系统,期末就没有大作业了 没有大作业=期末空闲时间更多=我可以看每到期末就看的停不下来的电视剧 欢迎大家来和我一起来学操作系统制作 参考书籍: 一个操作系统的实现 于渊 参考博客:ht ...

  6. orange's系统可以装mysql 吗?_bochs 2.4.2 ubuntu 安装运行问题《orange#39;s 一个操作系统的...

    用源码安装完后会遇到的问题: <1> 运行可能碰到如下问题: Event type: PANIC Device: [ ] Message: dlopen failed for module ...

  7. Orange's:一个操作系统的实现 Descriptor 3宏详解

    补充:关于GDT/LDT.段选择子和段描述符的解释       GDT/LDT:GDT/LDT是段描述符表,里面定义了每个段的段描述符的界限和属性,而段描述符的基址是在代码段中初始化的.        ...

  8. Orange‘s’ 一个操作系统的实现

    突然想要随时记录一下实验过程中的各种问题,因此有了此文档: 由于是中途开始的,所以就偷懒直接跳过了vm.ubuntu32位16.04.bochs2.6.8.nasm的安装,下面开始正文内容: 2022 ...

  9. <一个操作系统的实现>:sudo mount -o loop pm.img /mnt/floppy出错

    在<一个操作系统的实现>中使用命令sudo mount -o loop pm.img /mnt/floppy出现如下错误: mount: /mnt/floppy: wrong fs typ ...

最新文章

  1. R语言计算F1评估指标实战:F1 score、使用R中caret包中的confusionMatrix()函数为给定的logistic回归模型计算F1得分(和其他指标)
  2. Vue 路由 过渡动效 数据获取
  3. 排序后顺序错乱java_排列顺序不对怎么调
  4. 浅谈如何提升数据中心制冷能效
  5. HTML5的入门教程
  6. python深拷贝实现原理,js递归实现深拷贝
  7. restlet java_restlet(javase版本) 的最基本使用
  8. cad多段线画圆弧方向_【学员分享】CAD多段线用法
  9. RHEL7学习笔记6:系统启动过程
  10. octobercms mysql_在Ubuntu 18.04/Debian 9上安装October CMS
  11. 在BAT工作是什么样的?来听听在职员工们的说法
  12. 【VUE+Elemet 】最全正则验证 + 表单验证 + 注意事项
  13. NOIP2018(普及组 ) 赛后感想 题解
  14. AtCoder题解——AtCoder Grand Contest 048——A - atcoder < S
  15. 神经网络编程(python实现)
  16. 条码查询接口,商品条码和药品条码查询
  17. Prolific USB-to-Serial Comm Port最新程序不支持win11怎么处理
  18. 7.0 + 拍照异常了解一下
  19. AD单独设置单个或多个焊盘敷铜规则
  20. 关于ie浏览器的插件IEDevToolBar 和DebugBar安装问题

热门文章

  1. python遇到Segmentation fault (core dumped)调试方法
  2. 仿班级聊天室(DOM原型法)并且用localStorage存储消息记录
  3. golang下文件锁的使用
  4. LaTeX 文字对齐
  5. 【C语言】实现简单的五子棋
  6. hazelcast 搭建_SpringBoot整合Hazelcast实现分布式缓存
  7. Macchanger-更改mac地址
  8. 【行业云说直播间】-智慧能源 低碳未来正式上线
  9. VM虚拟机Ubuntu配置静态ip,亲测有效
  10. android中point pt1,Android dip,px,pt,sp 的区别详解