首页 > 代码库 > MINIX3 系统任务分析
MINIX3 系统任务分析
MINIX3 系统任务分析
7.1 MINIX3 系统任务概要
MINIX3 怎么来给用户提供丰富的服务呢?除了中断,异常处理,除了时钟服务。 程序员总是希望一个操作系统给他提供足够的服务,使得他能够做出更加高效安 全的程序来。在 MINIX3 中,它提供了一种系统任务机制。这个机制的作用就是 介绍任何想调用系统调用的函数消息,之后将其进行一个精准的处理,使得其能 够对程序员提供帮助。
MINIX3 整体架构设计成 C/ S 模型,以 PM 为例,PM 其实在 MINIX3 看来就是 就是一个服务进程,用户就是客户端,用户发送消息给 PM 进程,PM 进程接受 消息之后,又将以个客户端的形式存在,这种客户端又会向内核发送消息请求服 务,这种层次模式类似于网络中的 TCP/IP。
关于 MINIX3 的系统调用和传统的 UNIX/LIUNX 架构不一样。我们不做详细的 分析。我们将以图示的方式揭示出 2 者的差异性。
113
这种设计主要是为了除了将一些不得不在内核的部分以外,其他的都必须移到内 核外中进行的这一种设计理念。像在 LINUX/UNIX 这种传统的单体内核中,系 统调用就是一种对于内核提供服务的调用。在 MINX3 中,就大大的不一样,上 面的图也说明了这一点:
在 MINIX3 中,系统调用是一种符合 POSIX 标准的设计方法,但是这里有一点, 用户进程不在是向内核直接发送请求,在 MINIX3 中,这点非常的重要,是 MINX3 将系统调用变成消息发送服务器,比如向 PM,FS 服务器。之后服务器在发送消 息给系统任务或者内核其他部分。这样就形成了所谓的系统调用。这一点相对于 传统的 LINUX/UNIX 设计而言,要复杂。
114
而下面是 MINIX3 的一种执行系统调用的通用过程:
115
7.2 MINIX3 源码导读
这个程序源文件时 kernel/system.c 我在这里把它完整的附录上来进行讲解。
/* This task handles the interface between the kernel and user-level servers. System services can be accessed by doing a system call. System calls are transformed into request messages, which are handled by this task. By convention, a sys_call() is transformed in a SYS_CALL request message thatis handled in a function named do_call().
*这个任务处理器是内核和用户层服务器的接口,系统服务器能够被一个系统调
用所获取。系统调用被转成请求信息,这个请求信息被这个任务处理。惯例的, 一个sys_call()被转成一个SYS_CALL请求信息,这个信息被一个叫做do_call()
的函数来处理
* A private call vector is used to map all system calls to the functions
thathandle them. The actual handler functions are contained in separate
filesto keep this file clean. The call vector is used in the system task‘s
116
mainloop to handle all incoming requests.
*一个私自调用向量号被用来映射所有的系统调用函数来处理他们。这个真正的 处理函数被单独放在一个文件中,主要是为了保证文件的整洁。调用号被用来处 理所有进入系统任务的主循环的请求
* In addition to the main sys_task() entry point, which starts the main loop, there are several other minor entry points:
* get_priv: assign privilege structure to user or system
process
* send_sig: send a signal directly to a system process
* cause_sig: take action to cause a signal to occur via PM
* umap_local: map virtual address in LOCAL_SEG to physical * umap_remote: map virtual address in REMOTE_SEG to physical * umap_bios: map virtual address in BIOS_SEG to physical
* virtual_copy: copy bytes from one virtual address to another
* get_randomness: accumulate randomness in a buffer
*
* Changes:
* Aug 04, 2005 check if system call is allowed (Jorrit N. Herder)
* Jul 20, 2005 send signal to services with message (Jorrit N.
Herder)
* Jan 15, 2005 new, generalized virtual copy function (Jorrit N.
Herder)
* Oct 10, 2004 dispatch system calls from call vector (Jorrit N.
Herder)
* Sep 30, 2004 source code documentation updated (Jorrit N.
Herder)
*/
#include "kernel.h"
#include "system.h"
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/sigcontext.h>
#if (CHIP == INTEL)
#include <ibm/memory.h>
#include "protect.h"
#endif
/* Declaration of the call vector that defines the mapping of system calls
* to handler functions. The vector is initialized in sys_init() with
map(), which makes sure the system call numbers are ok. No space is
allocated, because the dummy is declared extern. If an illegal call is
given, the array size will be negative and this won‘t compile.
117
*/
//这个是已经声明好的调用向量号,这个数组就是各个系统调用的函数入口地址. 是一个公共变量 在kernel/talbe.c有定义
PUBLIC int (*call_vec[NR_SYS_CALLS])(message *m_ptr);
//这个宏定义在下面系统任务初始化中会用到,主要用于将具体系统任务安装到 //call_vec[]函数内部
#define map(call_nr, handler) \
{extern int dummy[NR_SYS_CALLS>(unsigned)(call_nr-KERNEL_CALL) ? 1:-1];} \
call_vec[(call_nr-KERNEL_CALL)] = (handler)
FORWARD _PROTOTYPE( void initialize, (void)); 这个文件的第一个函数:
先看这个函数的执行过程图:
/*=================================================================== ========*
* sys_task 系统任务进程,主要是将想调用内核任务的进程
118
*
*==================================================================== =======*/
PUBLIC void sys_task()
{
/* Main entry point of sys_task. Get the message and dispatch on type.
*/
static message m;
register int result;
register struct proc *caller_ptr; unsigned int call_nr;
int s;
/* Initialize the system task. */
//初始化工作,下面一个函数有详细说明
//主要是对调用任务函数的入口进行一个初始化工作
initialize();
//这里是一个主循环,也是sys_task整个运行流程,在整个OS运行过程中, //sys_task将一直在处在这个函数里
while (TRUE) {
/* Get work. Block and wait until a request message arrives. */
//向任何消息源进行接受一个消息,注意这里的接受消息来源是ANY,所
有可以接受任何消息来源
//如果没有渴望的消息接受来源,则保持阻塞状态
receive(ANY, &m);
// 确认是系统哪个调用号
call_nr = (unsigned) m.m_type - KERNEL_CALL; //确定调用进程地址
caller_ptr = proc_addr(m.m_source);
/* See if the caller made a valid request and try to handle it. */
if (! (priv(caller_ptr)->s_call_mask & (1<<call_nr))) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("SYSTEM: request %d from %d denied.\n", call_nr,m.m_source);
#endif
result = ECALLDENIED; /* illegal message type */
} else if (call_nr >= NR_SYS_CALLS) { /* check call number
*/
//如果调用号超出范围,就表示是一个违法的操作 #if DEBUG_ENABLE_IPC_WARNINGS
kprintf("SYSTEM: illegal request %d from %d.\n", call_nr,m.m_source);
#endif
119
result = EBADREQUEST; /* illegal message type */
}
//这个是最终正常调用的结果,这里调用一个call_vec[call_nr],来处理 相应的信息
else {
result = (*call_vec[call_nr])(&m); /* handle the system call
*/
}
/* Send a reply, unless inhibited by a handler function. Use the kernel function lock_send() to prevent a system call trap. The destination * is known to be blocked waiting for a message.
*/
//如果返回结果合法,则尝试发送一个消息,当然这个消息用lock的形式,防 止被其他程序或进程干扰
if (result != EDONTREPLY) {
m.m_type = result; /* report status of call */
if (OK != (s=lock_send(m.m_source, &m))) {
kprintf("SYSTEM, reply to %d failed: %d\n", m.m_source,
s);
}
}
}
}
/*=================================================================== ========*
* initialize *
*==================================================================== =======*/
PRIVATE void initialize(void)
{
register struct priv *sp;
int i;
/* Initialize IRQ handler hooks. Mark all hooks available. */
//初始化IRQ处理器的钩子,所有标记所有的钩子表示可以使用,pro_nr就是
用于
//标志是否被使用
for (i=0; i<NR_IRQ_HOOKS; i++) {
irq_hooks[i].proc_nr = NONE;
}
120
/* Initialize all alarm timers for all processes. */ //这里初始所有警报器
for (sp=BEG_PRIV_ADDR; sp < END_PRIV_ADDR; sp++) { tmr_inittimer(&(sp->s_alarm_timer));
}
/* Initialize the call vector to a safe default handler. Some system
calls may be disabled or nonexistant. Then explicitely map known calls
to their handler functions. This is done with a macro that gives a compile
error if an illegal call number is used. The ordering is not important
here.
*/
//这点个循环就是初始化所有系统的调用函数入口,他们首先被标记成为不能 使用使用的状态
for (i=0; i<NR_SYS_CALLS; i++) {
call_vec[i] = do_unused;
}
//下面这里把系统调用号和函数对应起来,包括常见的进程管理信号处理I/O设 备等等
//这点还是非常容易看出来的
/* Process management. */
map(SYS_FORK, do_fork);
map(SYS_EXEC, do_exec);
map(SYS_EXIT, do_exit);
map(SYS_NICE, do_nice);
map(SYS_PRIVCTL, do_privctl); map(SYS_TRACE, do_trace);
/* Signal handling. */
map(SYS_KILL, do_kill);
map(SYS_GETKSIG, do_getksig);
map(SYS_ENDKSIG, do_endksig);
map(SYS_SIGSEND, do_sigsend);
/* a process forked a new process */
/* update process after execute */ /* clean up after process exit */ /* set scheduling priority */
/* system privileges control */ /* request a trace operation */
/* cause a process to be signaled */
/* PM checks for pending signals */
/* PM finished processing signal */
/* start POSIX-style signal */
map(SYS_SIGRETURN, do_sigreturn); /* return from POSIX-style
signal */
/* Device I/O. */
map(SYS_IRQCTL, do_irqctl);
map(SYS_DEVIO, do_devio);
map(SYS_SDEVIO, do_sdevio);
*/
map(SYS_VDEVIO, do_vdevio); map(SYS_INT86, do_int86);
/* interrupt control operations */
/* inb, inw, inl, outb, outw, outl */
/* phys_insb, _insw, _outsb, _outsw
/* vector with devio requests */ /* real-mode BIOS calls */
121
/* Memory management. */
map(SYS_NEWMAP, do_newmap);
map(SYS_SEGCTL, do_segctl);
map(SYS_MEMSET, do_memset);
/* Copying. */
map(SYS_UMAP, do_umap);
map(SYS_VIRCOPY, do_vircopy);
/* set up a process memory map */
/* add segment and get selector */ /* write char to memory area */
/* map virtual to physical address */
/* use pure virtual addressing */
map(SYS_PHYSCOPY, do_physcopy); /* use physical addressing */
map(SYS_VIRVCOPY, do_virvcopy); /* vector with copy requests */
map(SYS_PHYSVCOPY, do_physvcopy); /* vector with copy requests */
/* Clock functionality. */
map(SYS_TIMES, do_times); /* get uptime and process times */
map(SYS_SETALARM, do_setalarm); /* schedule a synchronous alarm */
/* System control. */
map(SYS_ABORT, do_abort); /* abort MINIX */
map(SYS_GETINFO, do_getinfo); /* request system information */
map(SYS_IOPENABLE, do_iopenable); /* Enable I/O */
}
/*===================================================================
========*
* get_priv 主要用于特权的处理,每一个进程是有特定的特权,
这个函数就是干这么一件事情:设置子进程的特权 *
*==================================================================== =======*/
PUBLIC int get_priv(rc, proc_type)
register struct proc *rc;
//儿子进程指针
int proc_type;
//系统或者用户进程的标志
{
/* new (child) process pointer */
/* system or user process flag */
/* Get a privilege structure. All user processes share the same privilege * structure. System processes get their own privilege structure.
得到一个特权结构。所有的用户进程共享同样的特权结构。系统进程就会得到它 们自己的结构
*/
register struct priv *sp; /* privilege structure */
//如果是系统进程就找到一个空的位置
if (proc_type == SYS_PROC) { /* find a new slot */
for (sp = BEG_PRIV_ADDR; sp < END_PRIV_ADDR; ++sp)
122
if (sp->s_proc_nr == NONE && sp->s_id != USER_PRIV_ID) break;
if (sp->s_proc_nr != NONE) return(ENOSPC);
rc->p_priv = sp; /* assign new slot */
rc->p_priv->s_proc_nr = proc_nr(rc); /* set association */
rc->p_priv->s_flags = SYS_PROC; /* mark as privileged */
} else {
//如果是用户进程,就共享特权表
rc->p_priv = &priv[USER_PRIV_ID]; /* use shared slot */
rc->p_priv->s_proc_nr = INIT_PROC_NR; /* set association */
rc->p_priv->s_flags = 0; /* no initial flags */
}
return(OK);
}
/*===================================================================
========*
* get_randomness 取随机数函数,这个在中断号等地
方是会用到的,我们在这里简要的介绍下。 *
*==================================================================== =======*/
PUBLIC void get_randomness(source)
int source;
{
/* On machines with the RDTSC (cycle counter read instruction - pentium * and up), use that for high-resolution raw entropy gathering. Otherwise, * use the realtime clock (tick resolution).在拥有RDTSC芯片中,可以通 过硬件获取随机数。否则就实时时钟来获得
*
* Unfortunately this test is run-time - we don‘t want to bother with
* compiling different kernels for different machines.
*On machines without RDTSC, we use read_clock().
*/
int r_next;
unsigned long tsc_high, tsc_low;
source %= RANDOM_SOURCES;
r_next= krandom.bin[source].r_next;
//486平台已经其后面的平台会有相关生成随机数的硬件
if (machine.processor > 486) {
read_tsc(&tsc_high, &tsc_low);
krandom.bin[source].r_buf[r_next] = tsc_low; } else {
123
//没有这种硬件,就从实时时钟里读出时钟源
krandom.bin[source].r_buf[r_next] = read_clock();
}
if (krandom.bin[source].r_size < RANDOM_ELEMENTS) { krandom.bin[source].r_size ++;
}
//最终生成随机数。
krandom.bin[source].r_next = (r_next + 1 ) % RANDOM_ELEMENTS;
}
/*===================================================================
========*
* send_sig 这个函数和下一个函数都和PM信号量机制有着
紧密的联系 读这2个函数时,最好结合相关的PM机制来实现。 *
*====================================================================
=======*/
PUBLIC void send_sig(proc_nr, sig_nr)
int proc_nr; /* system process to be signalled */
int sig_nr; /* signal to be sent, 1 to _NSIG */
{
/* Notify a system process about a signal. This is straightforward. Simply * set the signal that is to be delivered in the pending signals map and * send a notification with source SYSTEM.
*/
//通知系统进程一个信号量。这个非常直接,简单的设定这个信号量,这个信号 量被发送挂起信号位图和发送一个源为SYSTEM通知
register struct proc *rp;
rp = proc_addr(proc_nr);
//给需要信号量机制的进程添加一个信号量
sigaddset(&priv(rp)->s_sig_pending, sig_nr); //之后向proc_nr发送一个通知,通知来源为SYSTEM
lock_notify(SYSTEM, proc_nr);
}
/*=================================================================== ========*
* cause_sig 主要触发信号量机制 *
*==================================================================== =======*/
PUBLIC void cause_sig(proc_nr, sig_nr)
int proc_nr; /* process to be signalled */
124
int sig_nr; /* signal to be sent, 1 to _NSIG */
{
/* A system process wants to send a signal to a process. Examples are:
* - HARDWARE wanting to cause a SIGSEGV after a CPU exception
* - TTY wanting to cause SIGINT upon getting a DEL
* - FS wanting to cause SIGPIPE for a broken pipe
一个系统进程想发送一个信号给一个进程。举例如下:
在一个CPU异常之后HARDWARE想引起一个SIGSEGV
TTY在得到一个DEL之后,想引起一个SIGINT
FS想在一个broken pipe之后得到一个SIGPIPE
* Signals are handled by sending a message to PM. This function handles
the
* signals and makes sure the PM gets them by sending a notification. The * process being signaled is blocked while PM has not finished all signals * for it.
信号量通过发送一个消息给PM被处理。cause_sig函数处理这个信号量并且确保 PM得到它们,而给它们
发送一个通知。当PM还没有完成所有信号量正在被发送信号量的进程被阻塞
* Race conditions between calls to this function and the system calls
that
* process pending kernel signals cannot exist. Signal related functions
are
* only called when a user process causes a CPU exception and from the kernel
* process level, which runs to completion.
*/
register struct proc *rp;
/* Check if the signal is already pending. Process it otherwise. */
rp = proc_addr(proc_nr);
sigaddset(&rp->p_pending, sig_nr);
if (! (rp->p_rts_flags & SIGNALED)) { /* other pending */
if (rp->p_rts_flags == 0) lock_dequeue(rp); /* make not
ready */
rp->p_rts_flags |= SIGNALED | SIG_PENDING; /* update flags */ send_sig(PM_PROC_NR, SIGKSIG);
}
}
}
125
7.3 系统调用函数举例
现在进入主题进行分析,MINIX3 的有很多系统任务,我在此会将绝大部分的系 统任务给予介绍,以此让读者对 MINIX3 的系统任务有个全名直白的理解。 现在就进入/kernel/system 文件中看:
/* The kernel call implemented in this file:
* m_type: SYS_VIRCOPY, SYS_PHYSCOPY
*这个消息来源于2个:SYS_VIRCOPY和SYS_PHYSOCPY
* The parameters for this kernel call are:
* m5_c1: CP_SRC_SPACE source virtual segment 源虚拟段
//CP_SRC_SPACE 表示m5_1的宏定义 m5_1是在消息机制定义的一种方式,
//CR_SRC是为了书写方便而设定的。后面所有都采用这种形式,后面就会忽略这 //是模式的介绍。
* m5_l1: CP_SRC_ADDR
//在段的源偏移地址
* m5_i1: CP_SRC_PROC_NR
//源进程号
* m5_c2: CP_DST_SPACE
//目的虚拟段号
* m5_l2: CP_DST_ADDR
//目的段的偏移地址
* m5_i2: CP_DST_PROC_NR
//目的进程号
* m5_l3: CP_NR_BYTES
//复制的目的字节数
*/
#include "../system.h"
#include <minix/type.h>
source offset within segment
source process number
destination virtual segment
destination offset within segment
destination process number
number of bytes to copy
#if (USE_VIRCOPY || USE_PHYSCOPY)
/*=================================================================== ========*
* do_copy *
*==================================================================== =======*/
PUBLIC int do_copy(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_vircopy() and sys_physcopy(). Copy data using virtual or
* physical addressing. Although a single handler function is used, there
126
* are two different kernel calls so that permissions can be checked.
处理sys_vircopy()和sys_physcopy().用虚拟地址或者物理地址复制数据。尽
管一个单独的函数被使用,但是这里有2种不同的内核调用所以权限应该需要做
出检查
*/
struct vir_addr vir_addr[2]; /* virtual source and destination
address */
phys_bytes bytes; /* number of bytes to copy */
int i;
/* Dismember the command message. */
//这些宏定义在上面都有,不做详细的分析
vir_addr[_SRC_].proc_nr = m_ptr->CP_SRC_PROC_NR;
vir_addr[_SRC_].segment = m_ptr->CP_SRC_SPACE;
vir_addr[_SRC_].offset = (vir_bytes) m_ptr->CP_SRC_ADDR; vir_addr[_DST_].proc_nr = m_ptr->CP_DST_PROC_NR;
vir_addr[_DST_].segment = m_ptr->CP_DST_SPACE;
vir_addr[_DST_].offset = (vir_bytes) m_ptr->CP_DST_ADDR; bytes = (phys_bytes) m_ptr->CP_NR_BYTES;
/* Now do some checks for both the source and destination virtual address.
* This is done once for _SRC_, then once for _DST_. 主要是做一些检查,看看有没有违反权限
*/
for (i=_SRC_; i<=_DST_; i++) {
/* Check if process number was given implictly with SELF and is valid.
*/
if (vir_addr[i].proc_nr == SELF) vir_addr[i].proc_nr = m_ptr->m_source;
if (! isokprocn(vir_addr[i].proc_nr) && vir_addr[i].segment != PHYS_SEG)
return(EINVAL);
/* Check if physical addressing is used without SYS_PHYSCOPY. */ if ((vir_addr[i].segment & PHYS_SEG) &&
m_ptr->m_type != SYS_PHYSCOPY) return(EPERM);
}
/* Check for overflow. This would happen for 64K segments and 16-bit
* vir_bytes. Especially copying by the PM on do_fork() is affected.
检查是否溢出
*/
127
if (bytes != (vir_bytes) bytes) return(E2BIG);
/* Now try to make the actual virtual copy. */
//以上都通过,证明了没有问题,就进行真正以上的复制,从源复制bytes个字 //节到目的进程的地址上
return( virtual_copy(&vir_addr[_SRC_], &vir_addr[_DST_], bytes) );
}
#endif /* (USE_VIRCOPY || USE_PHYSCOPY) */
/* The kernel call implemented in this file: * m_type: SYS_FORK
//是用SYS_FORK调用
*
* The parameters for this kernel call are:
* m1_i1: PR_PROC_NR (child‘s process table slot)
//孩子进程表
* m1_i2: PR_PPROC_NR
//父亲进程
*/
#include "../system.h" #include <signal.h>
#if (CHIP == INTEL)
#include "../protect.h" #endif
#if USE_FORK
(parent, process that forked)
/*=================================================================== ========*
* do_fork *
事实上这个还是非常的重要,涉及到PM的知识,在此做一个简要的介绍,主要是 做一些设置工作。主要就是设置新进程的proc结构体。至于申请内存得其他事情 是由PM来处理
*==================================================================== =======*/
PUBLIC int do_fork(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_fork(). PR_PPROC_NR has forked. The child is PR_PROC_NR.
*/
#if (CHIP == INTEL)
reg_t old_ldt_sel;
#endif
128
register struct proc *rpc; /* child process pointer */
struct proc *rpp; /* parent process pointer */
int i;
rpp = proc_addr(m_ptr->PR_PPROC_NR);//rpp就是指向父亲进程
rpc = proc_addr(m_ptr->PR_PROC_NR);//rpp就是指向孩子进程
if (isemptyp(rpp) || ! isemptyp(rpc)) return(EINVAL);
/* Copy parent ‘proc‘ struct to child. And reinitialize some fields. 将父亲进程的proc结构复制给孩子进程,并且重新初始化一些字段 */
#if (CHIP == INTEL)
old_ldt_sel = rpc->p_ldt_sel; /* backup local descriptors */
*rpc = *rpp; /* copy ‘proc‘ struct *///将rpc指向父亲进
程,目的就是做进一步的复制
rpc->p_ldt_sel = old_ldt_sel; /* restore descriptors */
//将父亲进程的段描述符进行重新的设定
#else
*rpc = *rpp; /* copy ‘proc‘ struct */
#endif
rpc->p_nr = m_ptr->PR_PROC_NR; /* this was obliterated by copy */
/* Only one in group should have SIGNALED, child doesn‘t inherit tracing.
*/
//继承正在运行的进程一些标志位
rpc->p_rts_flags |= NO_MAP; /* inhibit process from running */
rpc->p_rts_flags &= ~(SIGNALED | SIG_PENDING | P_STOP); sigemptyset(&rpc->p_pending);
//孩子进程将返回设置成为0,这就使得孩子进程知道它是孩子进程
rpc->p_reg.retreg = 0; /* child sees pid = 0 to know it is child */ //设定所有的计时都为0,因为子进程还没有投入使用
rpc->p_user_time = 0; /* set all the accounting times to 0 */
rpc->p_sys_time = 0;
/* Parent and child have to share the quantum that the forked process
had,
* so that queued processes do not have to wait longer because of the
fork.
* If the time left is odd, the child gets an extra tick.
*/
//父亲进程和子进程共享整个时钟剩余量,这样做是有好处还有坏处,就不得而 知了!
rpc->p_ticks_left = (rpc->p_ticks_left + 1) / 2; rpp->p_ticks_left = rpp->p_ticks_left / 2;
129
/* If the parent is a privileged process, take away the privileges from
the
* child process and inhibit it from running by setting the NO_PRIV flag.
* The caller should explicitely set the new privileges before
executing.
*/
//主要是去除孩子进程的特权标志位,当然这个也是在父亲进程是一个有特权的 //情况做的事情,不然没有必要做
if (priv(rpp)->s_flags & SYS_PROC) {
rpc->p_priv = priv_addr(USER_PRIV_ID); rpc->p_rts_flags |= NO_PRIV;
}
return(OK);
}
#endif /* USE_FORK */
/* The kernel call implemented in this file:
* m_type: SYS_IRQCTL
系统类型来源于SYS_IRQCTL,主要是处理中断信息
*
* The parameters for this kernel call are:
* m5_c1: IRQ_REQUEST (control operation to perform)
//IRQ_REQUEST 控制操作来执行
* m5_c2: IRQ_VECTOR (irq line that must be controlled)
//向量号必须被控制
* m5_i1: IRQ_POLICY (irq policy allows reenabling interrupts)
//中断向量策略允许嵌套中断
* m5_l3: IRQ_HOOK_ID
//提供返回中断索引
* ,, ,,
kernel)
*/
(provides index to be returned on interrupt)
(returns index of irq hook assigned at
//选取这个系统调用来看下,这个系统调用其实就是一个用于处理中断的方法过 程,消息类型是SYS_IRQCTL
#include "../system.h"
#if USE_IRQCTL
FORWARD _PROTOTYPE(int generic_handler, (irq_hook_t *hook));
/*=================================================================== ========*
* do_irqctl *
130
*==================================================================== =======*/
PUBLIC int do_irqctl(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Dismember the request message. */
int irq_hook_id;
int notify_id;
int r = OK;
int irq_vec;
irq_hook_t *hook_ptr;
/* Hook identifiers start at 1 and end at NR_IRQ_HOOKS. */ irq_hook_id = (unsigned) m_ptr->IRQ_HOOK_ID - 1;
irq_vec = (unsigned) m_ptr->IRQ_VECTOR;
/* See what is requested and take needed actions. */ //接受的消息是需要内核做什么工作
switch(m_ptr->IRQ_REQUEST) {
/* Enable or disable IRQs. This is straightforward. */ //实现关闭和打开中断功能,当然在此要排除一些非法状况 case IRQ_ENABLE:
case IRQ_DISABLE:
//先检查时候违法操作
if (irq_hook_id >= NR_IRQ_HOOKS ||
irq_hooks[irq_hook_id].proc_nr == NONE) return(EINVAL);
if (irq_hooks[irq_hook_id].proc_nr != m_ptr->m_source)
return(EPERM);
//排除违法操作时,如果请求是关闭中断,则就关闭那个中断向量号,如果不是, //就开启中断向量号
if (m_ptr->IRQ_REQUEST == IRQ_ENABLE)
enable_irq(&irq_hooks[irq_hook_id]);
else
disable_irq(&irq_hooks[irq_hook_id]); break;
/* Control IRQ policies. Set a policy and needed details in the IRQ table.
* This policy is used by a generic function to handle hardware interrupts.
*/
131
case IRQ_SETPOLICY:
/* Check if IRQ line is acceptable. */
if (irq_vec < 0 || irq_vec >= NR_IRQ_VECTORS) return(EINVAL);
/* Find a free IRQ hook for this mapping. */
//
hook_ptr = NULL;
//主要是来寻找一个空闲的IRQ向量号来注册,记住这里中确确事实的中断向量 //号。是irq_hooks[]数组的下表
for (irq_hook_id=0; irq_hook_id<NR_IRQ_HOOKS; irq_hook_id++) {
if (irq_hooks[irq_hook_id].proc_nr == NONE) {
hook_ptr = &irq_hooks[irq_hook_id]; /* free hook */
break;
}
}
//如果没有找到,就会出现错误。
if (hook_ptr == NULL) return(ENOSPC);
/* When setting a policy, the caller must provide an identifier that
* is returned on the notification message if a interrupt occurs.
*/
notify_id = (unsigned) m_ptr->IRQ_HOOK_ID;
if (notify_id > CHAR_BIT * sizeof(irq_id_t) - 1) return(EINVAL);
/* Install the handler. */
//给hook_ptr设定详细的信息。
hook_ptr->proc_nr = m_ptr->m_source; /* process to notify */
hook_ptr->notify_id = notify_id; /* identifier to pass */
hook_ptr->policy = m_ptr->IRQ_POLICY; /* policy for interrupts
*/
//注意这里,这里就是将中断同样处理程序给注册到向量号为irq_vec上。我们 等下会详细看到通用中断处理程序generic_handler是干了一件什么事情
put_irq_handler(hook_ptr, irq_vec, generic_handler);
/* Return index of the IRQ hook in use. */
m_ptr->IRQ_HOOK_ID = irq_hook_id + 1; break;
case IRQ_RMPOLICY:
if (irq_hook_id >= NR_IRQ_HOOKS ||
132
irq_hooks[irq_hook_id].proc_nr == NONE) { return(EINVAL);
} else if (m_ptr->m_source != irq_hooks[irq_hook_id].proc_nr) {
return(EPERM);
}
/* Remove the handler and return. */
//在前面进行一系列的检查之后,发现没有问题时,就会将irq_hook_id中断信 息给去除掉
rm_irq_handler(&irq_hooks[irq_hook_id]); break;
default:
r = EINVAL; /* invalid IRQ_REQUEST */
}
return(r);
}
/*===================================================================
========*
* generic_handler 这是一个通用中断处理程序,前面
一个函数已经调用了这个函数,现在我们深入里面分析 *
*==================================================================== =======*/
PRIVATE int generic_handler(hook)
irq_hook_t *hook;
{
/* This function handles hardware interrupt in a simple and generic way. All interrupts are transformed into messages to a driver. The IRQ line will bereenabled if the policy says so.
*/
//这个函数处理硬件中断,用的方法非常简单和通用。所有的中断将被转化成消
息传送给驱动器。这个IRQ线将会被嵌套如果策略说可以的话。
/* As a side-effect, the interrupt handler gathers random information
by timestamping the interrupt events. This is used for /dev/random.
*/
get_randomness(hook->irq);
//这个是用随机算法取一个irq号
/* Add a bit for this interrupt to the process‘ pending interrupts. When * sending the notification message, this bit map will be magically set * as an argument.
*/
priv(proc_addr(hook->proc_nr))->s_int_pending |= (1 <<
133
hook->notify_id);
/* Build notification message and return. */ //发送一个通知给HARDWRARE
lock_notify(HARDWARE, hook->proc_nr);
return(hook->policy & IRQ_REENABLE);
}
#endif /* USE_IRQCTL */
/* The kernel call implemented in this file: * m_type: SYS_SETALARM
* 消息类型:SYS_SETALARM
*
* The parameters for this kernel call are:
* m2_l1: ALRM_EXP_TIME
*警报器消耗时间
* m2_i2: ALRM_ABS_TIME
ALRM_ABS_TIME:是不是绝对时间
* m2_l1: ALRM_TIME_LEFT
*/
#include "../system.h"
#if USE_SETALARM
(alarm‘s expiration time)
(expiration time is absolute?)
(return seconds left of previous)
FORWARD _PROTOTYPE( void cause_alarm, (timer_t *tp) );
/*=================================================================== ========*
* do_setalarm 主要工作是给当前进程设置时
钟警报器,将时钟设置成相应的想要的时间进行警报*
*==================================================================== =======*/
PUBLIC int do_setalarm(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* A process requests a synchronous alarm, or wants to cancel its alarm.
*/
//对时钟操作的最终需要调用的任务
register struct proc *rp; /* pointer to requesting process */ //指向正在请求的进程
int proc_nr; /* which process wants the alarm */
134
//哪个进程想要警报
long exp_time; /* expiration time for this alarm */
//这个时钟警报器的时间
int use_abs_time; /* use absolute or relative time */
//用绝对绝对时间或者相对时间
timer_t *tp; /* the process‘ timer structure */
//指向这个时钟警报器结构
clock_t uptime; /* placeholder for current uptime */
/* Extract shared parameters from the request message. */ //将消息信息抽取出来
exp_time = m_ptr->ALRM_EXP_TIME; /* alarm‘s expiration time */
use_abs_time = m_ptr->ALRM_ABS_TIME; /* flag for absolute time */
proc_nr = m_ptr->m_source; /* process to interrupt later */
rp = proc_addr(proc_nr);
if (! (priv(rp)->s_flags & SYS_PROC)) return(EPERM);
/* Get the timer structure and set the parameters for this alarm. */
//在这里设置tp所指向的timer_t的各个属性值,其实这个结构体是在进程里
//定义的
tp = &(priv(rp)->s_alarm_timer);
//ta_int设置成哪个进程想要警报
tmr_arg(tp)->ta_int = proc_nr;
//tp的tmr_func代表的是当时间耗尽该指向哪个函数
tp->tmr_func = cause_alarm;
/* Return the ticks left on the previous alarm. */
//主要是返回当前时间,看看还有多少时钟剩下,之后下面就是将警报器的时 //钟节拍设置好
uptime = get_uptime();
if ((tp->tmr_exp_time != TMR_NEVER) && (uptime < tp->tmr_exp_time) )
{
m_ptr->ALRM_TIME_LEFT = (tp->tmr_exp_time - uptime);
} else {
m_ptr->ALRM_TIME_LEFT = 0;
}
/* Finally, (re)set the timer depending on the expiration time. 最终,我们依靠消耗时间来设定时钟警报器
*/
//如果消耗时间为0,则需要重新设置时钟警报器。如果不为0,就调用
set_timer(),这个函数就是在内核时钟队列里安装这个时钟警报器,看门狗函 数为tmr_func
if (exp_time == 0) {
135
reset_timer(tp);
} else {
tp->tmr_exp_time = (use_abs_time) ? exp_time : exp_time + get_uptime();
set_timer(tp, tp->tmr_exp_time, tp->tmr_func);
}
return(OK);
}
/*===================================================================
========*
* cause_alarm 这个函数主要是引起警报,主要是向目标进程发
送一个消息,消息源为CLOCK *
*==================================================================== =======*/
PRIVATE void cause_alarm(tp)
timer_t *tp;
{
/* Routine called if a timer goes off and the process requested a synchronous
* alarm. The process number is stored in timer argument ‘ta_int‘. Notify
that
* process with a notification message from CLOCK.
*/
//如果时间计时器已经耗尽,并且进程需要一个异步警告器。进程数被存储 在timer结构的ta_int里,
//通知进程号为proc_nr一个消息,消息来源为CLOCK
int proc_nr = tmr_arg(tp)->ta_int; /* get process number */
lock_notify(CLOCK, proc_nr); /* notify process */
}
#endif /* USE_SETALARM */
就此 系统任务基本上的框架就分析完了,系统任务其实相对来讲还是比较简单 的。但是涉及到具体调用时需要注意消息的格式的具体含义!
MINIX3 系统任务分析