/*===========================================================================*
* sys_call *
*===========================================================================*/
PUBLIC int sys_call(call_nr, src_dst, m_ptr)
int call_nr; /* system call number and flags */ //从系统调用的第一个参数中抽取出函数编码(SEND,RECEIVE等)和标志
int src_dst; /* src to receive from or dst to send to */
message *m_ptr; /* pointer to message in the caller's space */
{
/* System calls are done by trapping to the kernel with an INT instruction.
* The trap is caught and sys_call() is called to send or receive a message
* (or both). The caller is always given by 'proc_ptr'.
*/
register struct proc *caller_ptr = proc_ptr; /* get pointer to caller */
int function = call_nr & SYSCALL_FUNC; /* get system call function */
unsigned flags = call_nr & SYSCALL_FLAGS; /* get flags */
int mask_entry; /* bit to check in send mask */
int group_size; /* used for deadlock check */
int result; /* the system call's result */
vir_clicks vlo, vhi; /* virtual clicks containing message to send */
/* Check if the process has privileges for the requested call. Calls to the
* kernel may only be SENDREC, because tasks always reply and may not block
* if the caller doesn't do receive().
*/
//首先检查调用进程时是否允许发出的这个调用
if (! (priv(caller_ptr)->s_trap_mask & (1 << function)) ||
(iskerneln(src_dst) && function != SENDREC
&& function != RECEIVE)) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("sys_call: trap %d not allowed, caller %d, src_dst %d\n",
function, proc_nr(caller_ptr), src_dst);
#endif
return(ETRAPDENIED); /* trap denied by mask or kernel */
}
/* Require a valid source and/ or destination process, unless echoing. */
//然后检查得是指定的源进程或目标进程是否是一个有效的进程
if (src_dst != ANY && function != ECHO) {
if (! isokprocn(src_dst)) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("sys_call: invalid src_dst, src_dst %d, caller %d\n",
src_dst, proc_nr(caller_ptr));
#endif
return(EBADSRCDST); /* invalid process number */
}
if (isemptyn(src_dst)) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("sys_call: dead src_dst; trap %d, from %d, to %d\n",
function, proc_nr(caller_ptr), src_dst);
#endif
return(EDEADSRCDST);
}
}
/* If the call involves a message buffer, i.e., for SEND, RECEIVE, SENDREC,
* or ECHO, check the message pointer. This check allows a message to be
* anywhere in data or stack or gap. It will have to be made more elaborate
* for machines which don't have the gap mapped.
*/
//再检查消息的指针是否指向内存中的一个有效区域
if (function & CHECK_PTR) {
vlo = (vir_bytes) m_ptr >> CLICK_SHIFT;
vhi = ((vir_bytes) m_ptr + MESS_SIZE - 1) >> CLICK_SHIFT;
if (vlo < caller_ptr->p_memmap[D].mem_vir || vlo > vhi ||
vhi >= caller_ptr->p_memmap[S].mem_vir +
caller_ptr->p_memmap[S].mem_len) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("sys_call: invalid message pointer, trap %d, caller %d\n",
function, proc_nr(caller_ptr));
#endif
return(EFAULT); /* invalid message pointer */
}
}
/* If the call is to send to a process, i.e., for SEND, SENDREC or NOTIFY,
* verify that the caller is allowed to send to the given destination.
*/
//MUNX3特权定义了一个给定的进程允许向哪些进程发送消息,是否允许发送消息
if (function & CHECK_DST) {
if (! get_sys_bit(priv(caller_ptr)->s_ipc_to, nr_to_id(src_dst))) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("sys_call: ipc mask denied trap %d from %d to %d\n",
function, proc_nr(caller_ptr), src_dst);
#endif
return(ECALLDENIED); /* call denied by ipc mask */
}
}
/* Check for a possible deadlock for blocking SEND(REC) and RECEIVE. */
if (function & CHECK_DEADLOCK) {
if (group_size = deadlock(function, caller_ptr, src_dst)) {
#if DEBUG_ENABLE_IPC_WARNINGS
kprintf("sys_call: trap %d from %d to %d deadlocked, group size %d\n",
function, proc_nr(caller_ptr), src_dst, group_size);
#endif
return(ELOCKED);
}
}
/* Now check if the call is known and try to perform the request. The only
* system calls that exist in MINIX are sending and receiving messages.
* - SENDREC: combines SEND and RECEIVE in a single system call
* - SEND: sender blocks until its message has been delivered
* - RECEIVE: receiver blocks until an acceptable message has arrived
* - NOTIFY: nonblocking call; deliver notification or mark pending
* - ECHO: nonblocking call; directly echo back the message
*/
//最后的检查是验证目标进程正在运行并且么有启动终止过程
switch(function) { //若为SENDREC则顺序执行,下面的send,receive
case SENDREC:
/* A flag is set so that notifications cannot interrupt SENDREC. */
priv(caller_ptr)->s_flags |= SENDREC_BUSY;
/* fall through */
case SEND:
result = mini_send(caller_ptr, src_dst, m_ptr, flags);
if (function == SEND || result != OK) {
break; /* done, or SEND failed */
} /* fall through for SENDREC */
case RECEIVE:
if (function == RECEIVE)
priv(caller_ptr)->s_flags &= ~SENDREC_BUSY;
result = mini_receive(caller_ptr, src_dst, m_ptr, flags);
break;
case NOTIFY:
result = mini_notify(caller_ptr, src_dst);
break;
case ECHO:
CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, caller_ptr, m_ptr);
result = OK;
break;
default:
result = EBADCALL; /* illegal system call */
}
/* Now, return the result of the system call to the caller. */
return(result);
}
/*===========================================================================*
* deadlock *
*===========================================================================*/
//检查死锁,如果发送进程和目标进程正在同时向对方发送消息,且group_size为2,且对方不为发送状态是,不为死锁
//或者没有进程处于发送和接受状态,不为死锁
PRIVATE int deadlock(function, cp, src_dst) //这里的function为0x03
int function; /* trap number */
register struct proc *cp; /* pointer to caller */
register int src_dst; /* src or dst process */
{
/* Check for deadlock. This can happen if 'caller_ptr' and 'src_dst' have
* a cyclic dependency of blocking send and receive calls. The only cyclic
* depency that is not fatal is if the caller and target directly SEND(REC)
* and RECEIVE to each other. If a deadlock is found, the group size is
* returned. Otherwise zero is returned.
*/
register struct proc *xp; /* process pointer */
int group_size = 1; /* start with only caller */
int trap_flags;
while (src_dst != ANY) { /* check while process nr */
xp = proc_addr(src_dst); /* follow chain of processes */
group_size ++; /* extra process in group */
/* Check whether the last process in the chain has a depency. If it
* has not, the cycle cannot be closed and we are done.
*/
if (xp->p_rts_flags & RECEIVING) { /* xp has dependency */
src_dst = xp->p_getfrom; /* get xp's source */
} else if (xp->p_rts_flags & SENDING) { /* xp has dependency */
src_dst = xp->p_sendto; /* get xp's destination */
} else {
return(0); /* not a deadlock */
}
/* Now check if there is a cyclic dependency. For group sizes of two,
* a combination of SEND(REC) and RECEIVE is not fatal. Larger groups
* or other combinations indicate a deadlock.
*/
if (src_dst == proc_nr(cp)) { /* possible deadlock */
if (group_size == 2) { /* caller and src_dst */
/* The function number is magically converted to flags. */
if ((xp->p_rts_flags ^ (function << 2)) & SENDING) { //xp->p_rts_flags 不为SENDING
return(0); /* not a deadlock */
}
}
return(group_size); /* deadlock found */
}
}
return(0); /* not a deadlock */
}
/*===========================================================================*
* mini_send *
*===========================================================================*/
PRIVATE int mini_send(caller_ptr, dst, m_ptr, flags) //3个参数,调用进程、目标进程以及指向消息所在缓冲区的指针
register struct proc *caller_ptr; /* who is trying to send a message? */
int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
unsigned flags; /* system call flags */
{
/* Send a message from 'caller_ptr' to 'dst'. If 'dst' is blocked waiting
* for this message, copy the message to it and unblock 'dst'. If 'dst' is
* not waiting at all, or is waiting for another source, queue 'caller_ptr'.
*/
register struct proc *dst_ptr = proc_addr(dst);
register struct proc **xpp;
/* Check if 'dst' is blocked waiting for this message. The destination's
* SENDING flag may be set when its SENDREC call blocked while sending.
*/
//如果它正在等待发送进程或是ANY,则实用CopyMess宏复制消消息,
//接受进程通过复位RECEIVING位取消阻塞,然后调用enqueue给接收
//进程一个运行机会
if ( (dst_ptr->p_rts_flags & (RECEIVING | SENDING)) == RECEIVING &&
(dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) {
/* Destination is indeed waiting for this message. */
CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr,
dst_ptr->p_messbuf);
if ((dst_ptr->p_rts_flags &= ~RECEIVING) == 0) enqueue(dst_ptr); //复位
} else if ( ! (flags & NON_BLOCKING)) {
/* Destination is not waiting. Block and dequeue caller. */
//接收进程没有阻塞,或阻塞了但在等待别的进程的消息,则阻塞发送进程
//并将发送进程移出运行队列,然后接到等待向进程发送消息得链表尾部
caller_ptr->p_messbuf = m_ptr;
if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr); //当进程在准备队列,则其p_rts_flags == 0
caller_ptr->p_rts_flags |= SENDING;
caller_ptr->p_sendto = dst;
/* Process is now blocked. Put in on the destination's queue. */
xpp = &dst_ptr->p_caller_q; /* find end of list */
while (*xpp != NIL_PROC) xpp = &(*xpp)->p_q_link;
*xpp = caller_ptr; /* add caller to end */
caller_ptr->p_q_link = NIL_PROC; /* mark new end of list */
} else {
return(ENOTREADY);
}
return(OK);
}
/*===========================================================================*
* mini_receive *
*===========================================================================*/
PRIVATE int mini_receive(caller_ptr, src, m_ptr, flags)
register struct proc *caller_ptr; /* process trying to get message */
int src; /* which message source is wanted */
message *m_ptr; /* pointer to message buffer */
unsigned flags; /* system call flags */
{
/* A process or task wants to get a message. If a message is already queued,
* acquire it and deblock the sender. If no message from the desired source
* is available block the caller, unless the flags don't allow blocking.
*/
register struct proc **xpp;
register struct notification **ntf_q_pp;
message m;
int bit_nr;
sys_map_t *map;
bitchunk_t *chunk;
int i, src_id, src_proc_nr;
/* Check to see if a message from desired source is already available.
* The caller's SENDING flag may be set if SENDREC couldn't send. If it is
* set, the process should be blocked.
*/
if (!(caller_ptr->p_rts_flags & SENDING)) {
/* Check if there are pending notifications, except for SENDREC. */
if (! (priv(caller_ptr)->s_flags & SENDREC_BUSY)) {
// 有挂起的通知,将标记不再挂起并将通知交付处理(通知比一般的消息更具有更高的优先级)
map = &priv(caller_ptr)->s_notify_pending;
for (chunk=&map->chunk[0]; chunk<&map->chunk[NR_SYS_CHUNKS]; chunk++) {
/* Find a pending notification from the requested source. */
if (! *chunk) continue; /* no bits in chunk */
for (i=0; ! (*chunk & (1<<i)); ++i) {} /* look up the bit */ //挂起位为1
src_id = (chunk - &map->chunk[0]) * BITCHUNK_BITS + i; //算出源进程号,哪一个进程的哪一个通知消息?
if (src_id >= NR_SYS_PROCS) break; /* out of range */
src_proc_nr = id_to_nr(src_id); /* get source proc */
#if DEBUG_ENABLE_IPC_WARNINGS
if(src_proc_nr == NONE) {
kprintf("mini_receive: sending notify from NONE\n");
}
#endif
if (src!=ANY && src!=src_proc_nr) continue;/* source not ok */
*chunk &= ~(1 << i); /* no longer pending */ //挂起位置1
/* Found a suitable source, deliver the notification message. */
BuildMess(&m, src_proc_nr, caller_ptr);/* assemble message */
CopyMess(src_proc_nr, proc_addr(HARDWARE), &m, caller_ptr, m_ptr);
return(OK); /* report success */
}
}
/* Check caller queue. Use pointer pointers to keep code simple. */
xpp = &caller_ptr->p_caller_q; /* head of list of procs wishing to send */
//即从要发消息的进程中找,有没有要发给自己的
while (*xpp != NIL_PROC) {
if (src == ANY || src == proc_nr(*xpp)) {
/* Found acceptable message. Copy it and update status. */
CopyMess((*xpp)->p_nr, *xpp, (*xpp)->p_messbuf, caller_ptr, m_ptr);
if (((*xpp)->p_rts_flags &= ~SENDING) == 0) enqueue(*xpp);
*xpp = (*xpp)->p_q_link; /* remove from queue */
return(OK); /* report success */
}
xpp = &(*xpp)->p_q_link;/* proceed to next */
}
}
/* No suitable message is available or the caller couldn't send in SENDREC.
* Block the process trying to receive, unless the flags tell otherwise.
*/
//阻塞进程
if ( ! (flags & NON_BLOCKING)) {
caller_ptr->p_getfrom = src;
caller_ptr->p_messbuf = m_ptr;
if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr);
caller_ptr->p_rts_flags |= RECEIVING;
return(OK);
} else {
return(ENOTREADY);
}
}
/*===========================================================================*
* mini_notify *
*===========================================================================*/
PRIVATE int mini_notify(caller_ptr, dst)
register struct proc *caller_ptr; /* sender of the notification */
int dst; /* which process to notify */
{
register struct proc *dst_ptr = proc_addr(dst);
int src_id; /* source id for late delivery */
message m; /* the notification message */
/* Check to see if target is blocked waiting for this message. A process
* can be both sending and receiving during a SENDREC system call.
*/
if ((dst_ptr->p_rts_flags & (RECEIVING|SENDING)) == RECEIVING &&
! (priv(dst_ptr)->s_flags & SENDREC_BUSY) &&
(dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) {
/* Destination is indeed waiting for a message. Assemble a notification
* message and deliver it. Copy from pseudo-source HARDWARE, since the
* message is in the kernel's address space.
*/
//如果消息的接收者阻塞,并等待接受消息,那么将通过BulidMess
//构造消息然后交付处理。接受者的RECEIVING标志被关闭,然后
//调用enqueue
BuildMess(&m, proc_nr(caller_ptr), dst_ptr);
CopyMess(proc_nr(caller_ptr), proc_addr(HARDWARE), &m,
dst_ptr, dst_ptr->p_messbuf);
dst_ptr->p_rts_flags &= ~RECEIVING; /* deblock destination */
if (dst_ptr->p_rts_flags == 0) enqueue(dst_ptr);
return(OK);
}
/* Destination is not ready to receive the notification. Add it to the
* bit map with pending notifications. Note the indirectness: the system id
* instead of the process number is used in the pending bit map.
*/
//如果接收者没有等待消息,那么它的s_notify_pending位图中的一个位被置位
//它指示了有一个通知挂起并表明了发送者的身份
//如果在上一个通知处理之前需要向同一个接收者发送另外一个通知,那么接收
//者位图中的一位将会被有效地重写,从同一个发送者的多条通知合并为一个通知消息
src_id = priv(caller_ptr)->s_id;
set_sys_bit(priv(dst_ptr)->s_notify_pending, src_id);
return(OK);
}
!*===========================================================================*
!* cp_mess *
!*===========================================================================*
! PUBLIC void cp_mess(int src, phys_clicks src_clicks, vir_bytes src_offset,
! phys_clicks dst_clicks, vir_bytes dst_offset); 反序入栈,栈顶为src
! This routine makes a fast copy of a message from anywhere in the address
! space to anywhere else. It also copies the source address provided as a
! parameter to the call into the first word of the destination message.
!
! Note that the message size, "Msize" is in DWORDS (not bytes) and must be set
! correctly. Changing the definition of message in the type file and not
! changing it here will lead to total disaster.
CM_ARGS = 4 + 4 + 4 + 4 + 4 ! 4 + 4 + 4 + 4 + 4
! es ds edi esi eip proc scl sof dcl dof
.align 16
_cp_mess:
cld
push esi
push edi
push ds
push es
mov eax, FLAT_DS_SELECTOR
mov ds, ax
mov es, ax
mov esi, CM_ARGS+4(esp) ! src clicks 进入相应地址空间
shl esi, CLICK_SHIFT
add esi, CM_ARGS+4+4(esp) ! src offset 计算发送进程地址空间
mov edi, CM_ARGS+4+4+4(esp) ! dst clicks
shl edi, CLICK_SHIFT
add edi, CM_ARGS+4+4+4+4(esp) ! dst offset 计算接收进程地址空间
mov eax, CM_ARGS(esp) ! process number of sender
stos ! copy number of sender to dest message 将发送进程号存入接受地址空间第一个
add esi, 4 ! do not copy first word
mov ecx, Msize - 1 ! remember, first word does not count 将要发送多少个数据
rep
movs ! copy the message 拷贝Msize - 1个数据到接收进程地址空间
pop es
pop ds
pop edi
pop esi
ret ! that is all folks!