《一个操作系统的实现》总结3——系统任务和用户进程

本文详细介绍了操作系统中进程通信的实现,重点分析了send_recv函数。接着,探讨了系统任务,包括task_tty(处理tty交互)、task_sys(处理get_ticks和get_pid请求)、task_hd(硬盘读写)、task_fs(文件系统操作)、task_mm(fork和exec实现)。文章还阐述了用户进程Init如何启动并模拟shell,以及避免shell指令覆盖kernel的机制。
摘要由CSDN通过智能技术生成

  三、运行中的系统任务和用户进程

 首先,进程之间最重要的是进程通信,先来分析一下通信是如何实现的。

 进程要与其它进程通信均是调用send_recv函数,那我们就来看一下这个函数,节自kernel/proc.c:

/***send_recv调用了sendrec(syscall.asm),sendrec用int	INT_VECTOR_SYS_CALL进行系统调用陷入内核,这时到了sys_call(kernel.asm)中执行,在sys_call中又通过sys_call_table(global.c)初始化的表项进入sys_sendrec(位于本文件)执行,它分别调用msg_send(位于本文件)和msg_receive(位于本文件)来进行进程间消息的传递***/
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:
		ret = sendrec(SEND, src_dest, msg);
		if (ret == 0)
			ret = sendrec(RECEIVE, src_dest, msg);
		break;
	case SEND:
	case RECEIVE:
		ret = sendrec(function, src_dest, msg);
		break;
	default:
		assert((function == BOTH) ||
		       (function == SEND) || (function == RECEIVE));
		break;
	}

	return ret;
}

  中间涉及到的处理流程在注释中都已说明,就不再详细地贴出每个函数分析了,有兴趣自己看书吧。

 

 

 接下来,依次分析各个系统任务和用户进程,用户进程很简单,而且不属于操作系统实现,因此重点放在系统任务上。

 先来看看系统任务和用户进程都有哪些,节自kernel/global.c:

PUBLIC	struct task	task_table[NR_TASKS] = {
	/* entry        stack size        task name */
	/* -----        ----------        --------- */
	{task_tty,      STACK_SIZE_TTY,   "TTY"       },
	{task_sys,      STACK_SIZE_SYS,   "SYS"       },
	{task_hd,       STACK_SIZE_HD,    "HD"        },
	{task_fs,       STACK_SIZE_FS,    "FS"        },
	{task_mm,       STACK_SIZE_MM,    "MM"        }};

PUBLIC	struct task	user_proc_table[NR_NATIVE_PROCS] = {
	/* entry    stack size     proc name */
	/* -----    ----------     --------- */
	{Init,   STACK_SIZE_INIT,  "INIT" },
	{TestA,  STACK_SIZE_TESTA, "TestA"},
	{TestB,  STACK_SIZE_TESTB, "TestB"},
	{TestC,  STACK_SIZE_TESTC, "TestC"}};



1、task_tty

 操作系统将tty作为特殊的文件对待,从tty读入和输出到tty由该系统任务进行处理。它负责从键盘缓冲区中拿数据送到请求进程(通过文件系统task_fs中介),或从请求进程拿数据送到显存显示(通过文件系统task_fs)。

节自kernel/tty.c:

/***处理对tty的输入输出***/
PUBLIC void task_tty()
{
	TTY *	tty;
	MESSAGE msg;

	init_keyboard();

	for (tty = TTY_FIRST; tty < TTY_END; tty++)
		init_tty(tty);

	select_console(0);

	while (1) {
		for (tty = TTY_FIRST; tty < TTY_END; tty++) {
			do {
				tty_dev_read(tty);
				tty_dev_write(tty);
			} while (tty->ibuf_cnt);
		}

		send_recv(RECEIVE, ANY, &msg);

		int src = msg.source;
		assert(src != TASK_TTY);

		TTY* ptty = &tty_table[msg.DEVICE];

		switch (msg.type) {
		case DEV_OPEN:
			reset_msg(&msg);
			msg.type = SYSCALL_RET;
			send_recv(SEND, src, &msg);
			break;
/***直接给tty发送read和write消息的只有文件系统,因此msg->source只能是文件系统,其它的进程想要读写tty是通过文件系统这个中介来发送消息的,实际需要读和写的进程是msg->PROC_NR指定的进程。注意这点,跟一般的进程间通信是有区别的。***/
		case DEV_READ:
/***tty_do_read会根据msg设置tty的参数,tty_req_buf、tty_trans_cnt等,然后进入下次循环的开始将会在for循环中处理该msg对应的进程。在该函数完成时会将msg中PROC_NR对应的进程阻塞,进行下一次循环中执行tty_dev_write中,会将缓冲区中的指令逐字节复制到PROC_NR对应的进程的缓冲区中,完成一条指令的输出,即遇到\n时,会给PROC_NR对应的被阻塞的进程发送继续执行的msg。***/
			tty_do_read(ptty, &msg);
			break;
		case DEV_WRITE:
/***将进程缓冲区中的数据写出到显存,然后返回***/
			tty_do_write(ptty, &msg);
			break;
		case HARD_INT:
/***该信号是为了周期性地唤醒tty任务,避免它阻塞在前面的send_recv中。如果一个进程要求从tty输入,而人的输入间隔很大,这就造成一条命令没有一次性输入完
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值