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

为君一笑 提交于 2019-12-10 09:57:47

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

IPC 消息机制的建立

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

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

 

一,建立消息通信机制

·sys_send 函数:
PRIVATE int msg_send(struct proc *current, int dest, MESSAGE *m)// current 是谁要发							{								// dest 是发给谁								 							// m 是要发送的消息
	struct proc *sender = current;
	struct proc *p_dest = proc_table + dest;

	// 确保 sender 不是给自己发
	assert(dest != proc2pid(sender));

	/* 确保没有死锁 */
	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); // p_dest->p_msg 的内存必须要开辟好
		assert(m);

		phys_copy(va2la(dest, p_dest->p_msg), 		// p_dest 的 msg 的线性地址同样是等于:[p_dest 的 DS : offset(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;  // 清空 p_dest 上次是因为等待谁而阻塞!
		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; // m 其实就是 sender 要发送给 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;
}

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

·sys_receive 函数:
PRIVATE int msg_receive(struct proc *current, int src, MESSAGE *m) // m:将消息往回收到 m 中
{																   // sec:从哪里收
																   // current:谁想回收
	struct proc *p_who_wanna_recv = current;					  
	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_frome 就是 p_who_wanna_recv 等待队列上的第一个
			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
				    */

			// 这里在 p_who_wanna_recv 的 q_sending 队列上找到那个具体的进程
			// 估计要做更新等待列表的工作
			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.
		 */
		// update 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; // recv 方阻塞自己的时候指明它原本是想从哪里接收数据!
		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;
}

——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 之间互相传递消息

 

·main.c 节选
PUBLIC int get_ticks()
{
	MESSAGE msg;
	reset_msg(&msg);
	msg.type = GET_TICKS;
	send_recv(BOTH, TASK_SERVER, &msg);
	return msg.RETVAL;
}

void TestA()
{
	while (TRUE)
	{
		printf("%d  ", get_ticks());
		milli_delay(300);
	}
}

void TestB()
{
	MESSAGE _m;
	_m.RETVAL = 1;
	while (TRUE)
	{
		send_recv(SEND, 4, &_m);
		send_recv(RECEIVE, 4, &_m);
		printf("TestB number is [%d]\n", ++_m.RETVAL);
		milli_delay(2000);
	}
}

void TestC()
{
	MESSAGE _m;
	while (TRUE)
	{
		send_recv(RECEIVE, 3, &_m);
		printf("TestC number is [%d]\n", ++_m.RETVAL);
		send_recv(SEND, 3, &_m);
	}
}
·sys_task.c
// ----------------------------
// <systask.c>
//                      Jack Zheng
//              Comment:December 7, 2019
// ----------------------------
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

void task_server()
{

    MESSAGE _m;
    while (TRUE)
    {
        send_recv(RECEIVE, ANY, &_m);
        int src = _m.source;
        switch (_m.type)
        {
        case GET_TICKS: /* 某个进程请求的这个服务 */
            _m.RETVAL = ticks;
            send_recv(SEND, src, &_m);
            break;

        default:
            panic("unknown Message type");
            break;
        }
    }
}

运行:

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

 

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!