📄 proc.c
字号:
/* 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; 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. */ 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. */ src_id = priv(caller_ptr)->s_id; set_sys_bit(priv(dst_ptr)->s_notify_pending, src_id); return(OK);}/*===========================================================================* * lock_notify * *===========================================================================*/PUBLIC int lock_notify(src, dst)int src; /* sender of the notification */int dst; /* who is to be notified */{/* Safe gateway to mini_notify() for tasks and interrupt handlers. The sender * is explicitely given to prevent confusion where the call comes from. MINIX * kernel is not reentrant, which means to interrupts are disabled after * the first kernel entry (hardware interrupt, trap, or exception). Locking * is done by temporarily disabling interrupts. */ int result; /* Exception or interrupt occurred, thus already locked. */ if (k_reenter >= 0) { result = mini_notify(proc_addr(src), dst); } /* Call from task level, locking is required. */ else { lock(0, "notify"); result = mini_notify(proc_addr(src), dst); unlock(0); } return(result);}/*===========================================================================* * enqueue * *===========================================================================*/PRIVATE void enqueue(rp)register struct proc *rp; /* this process is now runnable */{/* Add 'rp' to one of the queues of runnable processes. This function is * responsible for inserting a process into one of the scheduling queues. * The mechanism is implemented here. The actual scheduling policy is * defined in sched() and pick_proc(). */ int q; /* scheduling queue to use */ int front; /* add to front or back */ struct proc *ptr, *tptr; #if DEBUG_SCHED_CHECK check_runqueues("enqueue"); if (rp->p_ready) kprintf("enqueue() already ready process\n");#endif /* Determine where to insert to process. */ sched(rp, &q, &front); if(rp->deadline > 0) { rp->p_priority = rp->p_max_priority; q = rp->p_max_priority; if (rdy_head[q] == NIL_PROC) { /* add to empty queue */ rdy_head[q] = rdy_tail[q] = rp; /* create a new queue */ rp->p_nextready = NIL_PROC; /* mark new end */ } else{ tptr = NIL_PROC; ptr = rdy_head[q]; while( ptr != NIL_PROC && ptr->deadline > 0 && ptr->deadline < rp->deadline) { tptr = ptr; ptr = ptr->p_nextready; } if( tptr == NIL_PROC ) { rp->p_nextready = ptr; rdy_head[q] = rp; } else{ tptr->p_nextready = rp; rp->p_nextready = ptr; } } } else { /* Now add the process to the queue. */ if (rdy_head[q] == NIL_PROC) { /* add to empty queue */ rdy_head[q] = rdy_tail[q] = rp; /* create a new queue */ rp->p_nextready = NIL_PROC; /* mark new end */ } else if (front) { /* add to head of queue */ tptr = NIL_PROC; ptr = rdy_head[q]; while( ptr != NIL_PROC && ptr->deadline > 0) { tptr = ptr; ptr = ptr->p_nextready; } if( tptr == NIL_PROC ) { rp->p_nextready = ptr; rdy_head[q] = rp; } else{ tptr->p_nextready = rp; rp->p_nextready = ptr; } } else { /* add to tail of queue */ rdy_tail[q]->p_nextready = rp; /* chain tail of queue */ rdy_tail[q] = rp; /* set new queue tail */ rp->p_nextready = NIL_PROC; /* mark new end */ } } /* Now select the next process to run. */ pick_proc(); #if DEBUG_SCHED_CHECK rp->p_ready = 1; check_runqueues("enqueue");#endif}/*===========================================================================* * dequeue * *===========================================================================*/PRIVATE void dequeue(rp)register struct proc *rp; /* this process is no longer runnable */{/* A process must be removed from the scheduling queues, for example, because * it has blocked. If the currently active process is removed, a new process * is picked to run by calling pick_proc(). */ register int q = rp->p_priority; /* queue to use */ register struct proc **xpp; /* iterate over queue */ register struct proc *prev_xp; /* Side-effect for kernel: check if the task's stack still is ok? */ if (iskernelp(rp)) { if (*priv(rp)->s_stack_guard != STACK_GUARD) panic("stack overrun by task", proc_nr(rp)); }#if DEBUG_SCHED_CHECK check_runqueues("dequeue"); if (! rp->p_ready) kprintf("dequeue() already unready process\n");#endif /* Now make sure that the process is not in its ready queue. Remove the * process if it is found. A process can be made unready even if it is not * running by being sent a signal that kills it. */ prev_xp = NIL_PROC; for (xpp = &rdy_head[q]; *xpp != NIL_PROC; xpp = &(*xpp)->p_nextready) { if (*xpp == rp) { /* found process to remove */ *xpp = (*xpp)->p_nextready; /* replace with next chain */ if (rp == rdy_tail[q]) /* queue tail removed */ rdy_tail[q] = prev_xp; /* set new tail */ if (rp == proc_ptr || rp == next_ptr) /* active process removed */ pick_proc(); /* pick new process to run */ break; } prev_xp = *xpp; /* save previous in chain */ } #if DEBUG_SCHED_CHECK rp->p_ready = 0; check_runqueues("dequeue");#endif}/*===========================================================================* * sched * *===========================================================================*/PRIVATE void sched(rp, queue, front)register struct proc *rp; /* process to be scheduled */int *queue; /* return: queue to use */int *front; /* return: front or back */{/* This function determines the scheduling policy. It is called whenever a * process must be added to one of the scheduling queues to decide where to * insert it. As a side-effect the process' priority may be updated. */ static struct proc *prev_ptr = NIL_PROC; /* previous without time */ int time_left = (rp->p_ticks_left > 0); /* quantum fully consumed */ int penalty = 0; /* change in priority */ /* Check whether the process has time left. Otherwise give a new quantum * and possibly raise the priority. Processes using multiple quantums * in a row get a lower priority to catch infinite loops in high priority * processes (system servers and drivers). */ if ( ! time_left) { /* quantum consumed ? */ rp->p_ticks_left = rp->p_quantum_size; /* give new quantum */ if (prev_ptr == rp) penalty ++; /* catch infinite loops */ else penalty --; /* give slow way back */ prev_ptr = rp; /* store ptr for next */ } /* Determine the new priority of this process. The bounds are determined * by IDLE's queue and the maximum priority of this process. Kernel task * and the idle process are never changed in priority. */ if (penalty != 0 && ! iskernelp(rp)) { rp->p_priority += penalty; /* update with penalty */ if (rp->p_priority < rp->p_max_priority) /* check upper bound */ rp->p_priority=rp->p_max_priority; else if (rp->p_priority > IDLE_Q-1) /* check lower bound */ rp->p_priority = IDLE_Q-1; } /* If there is time left, the process is added to the front of its queue, * so that it can immediately run. The queue to use simply is always the * process' current priority. */ *queue = rp->p_priority; *front = time_left;}/*===========================================================================* * pick_proc * *===========================================================================*/PRIVATE void pick_proc(){/* Decide who to run now. A new process is selected by setting 'next_ptr'. * When a billable process is selected, record it in 'bill_ptr', so that the * clock task can tell who to bill for system time. */ register struct proc *rp; /* process to run */ int q; /* iterate over queues */ /* Check each of the scheduling queues for ready processes. The number of * queues is defined in proc.h, and priorities are set in the task table. * The lowest queue contains IDLE, which is always ready. */ for (q=0; q < NR_SCHED_QUEUES; q++) { if ( (rp = rdy_head[q]) != NIL_PROC) { next_ptr = rp; /* run process 'rp' next */ if (priv(rp)->s_flags & BILLABLE) bill_ptr = rp; /* bill for system time */ return; } }}/*===========================================================================* * lock_send * *===========================================================================*/PUBLIC int lock_send(dst, m_ptr)int dst; /* to whom is message being sent? */message *m_ptr; /* pointer to message buffer */{/* Safe gateway to mini_send() for tasks. */ int result; lock(2, "send"); result = mini_send(proc_ptr, dst, m_ptr, NON_BLOCKING); unlock(2); return(result);}/*===========================================================================* * lock_enqueue * *===========================================================================*/PUBLIC void lock_enqueue(rp)struct proc *rp; /* this process is now runnable */{/* Safe gateway to enqueue() for tasks. */ lock(3, "enqueue"); enqueue(rp); unlock(3);}/*===========================================================================* * lock_dequeue * *===========================================================================*/PUBLIC void lock_dequeue(rp)struct proc *rp; /* this process is no longer runnable */{/* Safe gateway to dequeue() for tasks. */ lock(4, "dequeue"); dequeue(rp); unlock(4);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -