MINXI笔记之消息传递


/*===========================================================================*
 *				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!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值