亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

信號量機制

2024-06-28 13:24:37
字體:
來源:轉載
供稿:網友
信號量機制

信號量機制

11.1 2 個程序的例子

先看 2 個程序;

#include<unistd.h> int main(void)

{

allarm(10); for(;;;);

}

這段程序的含義比較明顯:這個程序在一個無限循環中,直到過了 10 秒,之后

程序被終止。

在來看另外一個程序:

Static void setvalue(void)

{

Flag=1;

}

int main(void) {

int sum=0; int flag=0;

struct sigaction act; act.sa_handler=setvalue,act. sa_sa_flags=0;

sigempteyset(&act,sa_mask);

sigaction(SIGINT,&act,NULL); while(flag==0)

{

sum+=1;

}

PRintf(“ the sum is %d”,&sum);

}

分析這個程序:這個程序就是注冊一個捕獲信號量 SIGINT(在兼容 POSIX 內會

169

默認是 Ctrl+C),捕獲到這個信號量之后執行 setvalue 函數,就停止加,最后將結 果打印出來之后程序(進程)結束。

舉這 2 個程序的目的是:用戶程序編寫者需要異步執行,需要異步觸發時間,操 作系統必須提供這樣的功能---異步執行。在 MINX3 中,用信號量來實現這種異 步功能。

11.2 MINIX 信號量機制概述

MINIX3 采用微內核結構,PM(進程管理器)是放到內核外執行,但是你或許 已經感受到,信號量的機制實現一定要靠內核的幫助來完成。所以信號量的實現 還是比較復雜。

首先我們想象一個事情:一個進程怎么能夠讓 PM 和內核知道它設定了一個信號 量呢?事實上內核部分是不知道的,內核的功能就是接受消息之后將它發送出 去。事實上,真正實現信號量的主體部分是 PM 部分,這點到后會非常的清楚。 先默認是 PM 來控制信號量主體實現,我們來看看 PM:

在 PM 中,我們同樣定義了一個進程表數組,為每一個進程設定一個數據結構, 這個結構是

EXTERN struct mproc {

……….

/* Signal handling information. */

sigset_t mp_ignore;

sigset_t mp_catch; sigset_t mp_sig2mess; sigset_t mp_sigmask; sigset_t mp_sigmask2;

/* 1 means ignore the signal, 0 means don't */

/* 1 means catch the signal, 0 means don't */ /* 1 means transform into notify message */ /* signals to be blocked */

/* saved copy of mp_sigmask */

sigset_t mp_sigpending; /* pending signals to be handled */

struct sigaction mp_sigact[_NSIG + 1]; /* as in sigaction(2) */

vir_bytes mp_sigreturn; /* address of C library __sigreturn function */

struct timer mp_timer; /* watchdog timer for alarm(2) */ …………

}mproc[NR_PROCS];

可以看到這里定義了一個靜態變量數組。上面暫且展現了與信號量有關的域

先看下 strcut sigaction mp_sigact[NSIG+1];這個是一個結構體數組,里面每一項 都是 sigaction。這里每一個項就是存儲一個信號量信息。如果進程有需要標記信

號量,則就是在這個結構體數組標志,這里到后面還要詳細介紹。sigset_t *

都是一些位圖,用于標志一些 flag.之后 vir_bytes mp_sigreturn,這是 C 語言庫里面 _sigreturn 函數的地址,我們可以設想下,當捕獲函數完成后,我們應該怎么辦, 必須要讓用戶進程毫無察覺到的放回,所以必須有一個函數來完成這個任務,而 且最終是靠內核設置相關棧來完成,這個數據項就是這個函數地址。最后一個數 據結構就是一個看門狗時鐘,用于處理與時鐘相關的例程。這個在后面會詳細的 介紹。

170

wpsB8AE.tmp

11.3 MINIX3 信號量機制源碼導讀

函數主要放在 pm/signal.c 中,我在這里會把與內核相關的系統調用函數也拿出 來,主要是為了講解的方便。首先是看 do_sigaction()函數: 這個函數調用的宏觀過程如下圖:

/*=================================================================== ========*

* do_sigaction 執行SIGACTION系統調用,這個系統調用沒

有參數,參數都是在m_in信息里 這個函數主體就是注冊一個信號量 *

*==================================================================== =======*/

PUBLIC int do_sigaction() {

int r;

struct sigaction svec; struct sigaction *svp;

//SIGKILL代表不能捕獲信號量,就返回

if (m_in.sig_nr == SIGKILL) return(OK); //不在范圍內,返回錯誤信息

if (m_in.sig_nr < 1 || m_in.sig_nr > _NSIG) return (EINVAL);

//svp是指向sigaction的地址,在這里,要理解mp_sigact[]數組的含義,里面 每一個元值都表示一個信號處理結構.用m_in.sig_nr做索引

svp = &mp->mp_sigact[m_in.sig_nr];

//準備注冊一個信號量 將系統任務的當前屬性全部復制到m_in.sig_osa所指向 //的變量中

171

if ((struct sigaction *) m_in.sig_osa != (struct sigaction *) NULL) { r = sys_datacopy(PM_PROC_NR,(vir_bytes) svp,

who, (vir_bytes) m_in.sig_osa, (phys_bytes) sizeof(svec)); if (r != OK) return(r);

}

if ((struct sigaction *) m_in.sig_nsa == (struct sigaction *) NULL) return(OK);

/* Read in the sigaction structure. */

r = sys_datacopy(who, (vir_bytes) m_in.sig_nsa,

PM_PROC_NR, (vir_bytes) &svec, (phys_bytes) sizeof(svec)); if (r != OK) return(r);

// SIG_IGN代表忽略這個信號量

if (svec.sa_handler == SIG_IGN) {

sigaddset(&mp->mp_ignore, m_in.sig_nr);

sigdelset(&mp->mp_sigpending, m_in.sig_nr); sigdelset(&mp->mp_catch, m_in.sig_nr); sigdelset(&mp->mp_sig2mess, m_in.sig_nr);

//默認信號處理函數

} else if (svec.sa_handler == SIG_DFL) { sigdelset(&mp->mp_ignore, m_in.sig_nr); sigdelset(&mp->mp_catch, m_in.sig_nr);

sigdelset(&mp->mp_sig2mess, m_in.sig_nr);

//做為一個信息傳遞到到MINIX中

} else if (svec.sa_handler == SIG_MESS) {

if (! (mp->mp_flags & PRIV_PROC)) return(EPERM); sigdelset(&mp->mp_ignore, m_in.sig_nr); sigaddset(&mp->mp_sig2mess, m_in.sig_nr); sigdelset(&mp->mp_catch, m_in.sig_nr);

//捕獲自己定義的函數

} else {

sigdelset(&mp->mp_ignore, m_in.sig_nr); sigaddset(&mp->mp_catch, m_in.sig_nr); sigdelset(&mp->mp_sig2mess, m_in.sig_nr); }

//將svec結構體各個域傳遞給mp結構體,也將其他的一些信息傳遞給 mp->mp_sigaction[],最終確定注冊成功。

mp->mp_sigact[m_in.sig_nr].sa_handler = svec.sa_handler; sigdelset(&svec.sa_mask, SIGKILL);

mp->mp_sigact[m_in.sig_nr].sa_mask = svec.sa_mask;

mp->mp_sigact[m_in.sig_nr].sa_flags = svec.sa_flags; mp->mp_sigreturn = (vir_bytes) m_in.sig_ret; return(OK);

}

172

/*=================================================================== ========*

* check_sig 這個函數就是發送信號的一個接口 *

*====================================================================

=======*/

PUBLIC int check_sig(proc_id, signo)

pid_t proc_id; /* pid of proc to sig, or 0 or -1, or -pgrp */

int signo; /* signal to send to process (0 to _NSIG) */

{

/* Check to see if it is possible to send a signal. The signal may have to be sent to a group of processes. This routine is invoked by the KILL system call, and also when the kernel catches a DEL or other signal.

*/

//檢查看是否需要發送一個信號量,信號量也許不得不發送給一組進程。

//當然當內核捕獲到一個DEL或者其他信號時,這個例程涉及到KILL系統調用 register struct mproc *rmp;

int count; /* count # of signals sent */

int error_code;

//檢查要發送的信號量是否合法

if (signo < 0 || signo > _NSIG) return(EINVAL);

//如果嘗試給INIT_PID進程發送SIGKILL信號,將會產生錯誤 //這個在開機時永遠不會被殺死

/* Return EINVAL for attempts to send SIGKILL to INIT alone. */ if (proc_id == INIT_PID && signo == SIGKILL) return(EINVAL); //對進程表搜索進程信號量

/* Search the proc table for processes to signal.

* pid magic.)

*/

count = 0;

error_code = ESRCH;

(See forkexit.c about

for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { if (!(rmp->mp_flags & IN_USE)) continue;

if ((rmp->mp_flags & ZOMBIE) && signo != 0) continue;

/* Check for selection. */

if (proc_id > 0 && proc_id != rmp->mp_pid) continue;

if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) continue; if (proc_id == -1 && rmp->mp_pid <= INIT_PID) continue;

173

if (proc_id < -1 && rmp->mp_procgrp != -proc_id) continue;

/* Check for permission. */

if (mp->mp_effuid != SUPER_USER

&& mp->mp_realuid != rmp->mp_realuid && mp->mp_effuid != rmp->mp_realuid && mp->mp_realuid != rmp->mp_effuid

&& mp->mp_effuid != rmp->mp_effuid) { error_code = EPERM;

continue;

}

count++;

if (signo == 0) continue;

/* 'sig_proc' will handle the disposition of the signal. The * signal may be caught, blocked, ignored, or cause process * termination, possibly with core dump.

*/

//找到滿足條件后,將調用sig_proc()函數,來處理這個信號量

sig_proc(rmp, signo);

if (proc_id > 0) break; /* only one process being signaled */

}

/* If the calling process has killed itself, don't reply. */

if ((mp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) return(SUSPEND); return(count > 0 ? OK : error_code);

}

在往下面分析之前,先看一個結構體。

/* PM passes the address of a structure of this type to KERNEL when

* sys_sendsig() is invoked as part of the signal catching mechanism. * The structure contain all the information that KERNEL needs to build * the signal stack.當使用sys_sendsing()這個函數來進行捕獲 PM傳遞將這 個結構體傳遞給內核。這個結構體包含了所用內核想要建造的信號棧信息

*/

struct sigmsg {

int sm_signo; /* signal number being caught 捕獲信號量號 */

unsigned long sm_mask; /* mask to restore when handler returns 當處

理返回時 重新標記一些標記位*/

vir_bytes sm_sighandler; /* address of handler 信號量處理地址 */

vir_bytes sm_sigreturn; /* address of _sigreturn in C library 事實

上是一個信號量返回地址,在這里就是_sigreturn地址*/

vir_bytes sm_stkptr; /* user stack pointer 用戶棧的指針 */

174

};

這個結構體得功能其實就是PM所處理的一個專門為信號量所設置的結構體,主要 功能是為內核提供所有想要的棧信息。

/*=================================================================== ========*

* sig_proc 這個函數是最重要的是處理信號量的核心函數

*

*==================================================================== =======*/

PUBLIC void sig_proc(rmp, signo)

register struct mproc *rmp; /* pointer to the process to be signaled */

int signo; /* signal to send to process (1 to _NSIG) */

{

/* Send a signal to a process. Check to see if the signal is to be caught, * ignored, tranformed into a message (for system processes) or blocked. * 給一個進程發送一個信號量。檢查看看這個信號是否被捕獲,忽略,轉變成 一個消息或者阻塞

* - If the signal is to be transformed into a message, request the KERNEL to send the target process a system notification with the pending signal as an argument.

如果這個信號量被轉成一個消息,請求內核發送一個系統通知給這個目標進程 * - If the signal is to be caught, request the KERNEL to push a sigcontext * structure and a sigframe structure onto the catcher's stack. Also, KERNEL will reset the program counter and stack pointer, so that when the process next runs, it will be executing the signal handler. When the signal handler returns, sigreturn(2) will be called. Then KERNEL will

restore the signal context from the sigcontext structure.

如果這個信號量被捕獲,請求內核將sigcontext壓入棧中并且將sigframe結構 壓入捕獲者的棧中。當然,內核將會重新設置這個程序計數器和堆棧指針,所以 當進程在下次來運行時,他將會執行信號處理函數。當信號處理函數返回時, sigreturn(2)例程將會被調用。內核將會重新從sigcontext結構重新設置信號量 上下文

* If there is insufficient stack space, kill the process. 如果沒有充足堆??臻g,將會殺死這個進程

*/

vir_bytes new_sp; int s;

int slot;

int sigflags;

struct sigmsg sm;

slot = (int) (rmp - mproc);

175

//查看進程表中目的進程的標志位,看看其是不是IN_USE,如果該進程已經結 束或者僵死

//將不能執行這個操作。

if ((rmp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) {

printf("PM: signal %d sent to %s process %d/n",

signo, (rmp->mp_flags & ZOMBIE) ? "zombie" : "dead", slot); panic(__FILE__,"", NO_NUM);

}

//查看進程的標志位,如果該進程正在被跟蹤,那么就收到這個信號量后,將 停止操作

if ((rmp->mp_flags & TRACED) && signo != SIGKILL) { /* A traced process has special handling. */

unpause(slot);

stop_proc(rmp, signo); /* a signal causes it to stop */

return;

}

//如果信號量被忽略,則函數結束

/* Some signals are ignored by default. */ if (sigismember(&rmp->mp_ignore, signo)) { return;

}

//如果信號量被阻塞,將mp_sigpending位圖設置相應的位 if (sigismember(&rmp->mp_sigmask, signo)) { /* Signal should be blocked. */

sigaddset(&rmp->mp_sigpending, signo); return;

}

#if ENABLE_SWAP

if (rmp->mp_flags & ONSWAP) {

/* Process is swapped out, leave signal pending. */

sigaddset(&rmp->mp_sigpending, signo);

swap_inqueue(rmp);

return;

}

#endif

//函數的主體部分,非常重要

sigflags = rmp->mp_sigact[signo].sa_flags;

//首先判斷時候捕獲這個信號量

if (sigismember(&rmp->mp_catch, signo)) { if (rmp->mp_flags & SIGSUSPENDED) sm.sm_mask = rmp->mp_sigmask2;

else

sm.sm_mask = rmp->mp_sigmask;

sm.sm_signo = signo;//被捕獲的信號量號

176

//被捕獲信號量處理函數的首地址

sm.sm_sighandler = (vir_bytes) rmp->mp_sigact[signo].sa_handler;

//被捕獲的信號量處理函數的返回地址,這個地址是在庫函數中

sm.sm_sigreturn = rmp->mp_sigreturn;

//分配新的堆棧主要用于儲存信息

if ((s=get_stack_ptr(slot, &new_sp)) != OK)

panic(__FILE__,"couldn't get new stack pointer",s); //將捕獲的信號量處理函數的堆棧地址儲存起來

sm.sm_stkptr = new_sp;

//給sigcontext和sigframe結構騰出地址來,new_sp的地址也就隨之改變 /* Make room for the sigcontext and sigframe struct. */ new_sp -= sizeof(struct sigcontext)

+ 3 * sizeof(char *) + 2 * sizeof(int);

if (adjust(rmp, rmp->mp_seg[D].mem_len, new_sp) != OK) goto doterminate;

//給進程的信號量進行標識,也就是設定標識位

rmp->mp_sigmask |= rmp->mp_sigact[signo].sa_mask; if (sigflags & SA_NODEFER)

sigdelset(&rmp->mp_sigmask, signo);

else

sigaddset(&rmp->mp_sigmask, signo);

if (sigflags & SA_RESETHAND) {

sigdelset(&rmp->mp_catch, signo);

rmp->mp_sigact[signo].sa_handler = SIG_DFL; }

//給內核發送消息,消息存儲在sm內。當然這不是進入內核的最終消息格式 //關于sys_sigsend在下面有詳細分析

if (OK == (s=sys_sigsend(slot, &sm))) {

sigdelset(&rmp->mp_sigpending, signo);

/* If process is hanging on PAUSE, WAIT, SIGSUSPEND, tty, * pipe, etc., release it.

*/

unpause(slot); return;

}

panic(__FILE__, "warning, sys_sigsend failed", s);

}

//

else if (sigismember(&rmp->mp_sig2mess, signo)) { if (OK != (s=sys_kill(slot,signo)))

panic(__FILE__, "warning, sys_kill failed", s);

177

return;

}

doterminate:

/* Signal should not or cannot be caught. Take default action. */ if (sigismember(&ign_sset, signo)) return;

rmp->mp_sigstatus = (char) signo;

if (sigismember(&core_sset, signo)) { #if ENABLE_SWAP

if (rmp->mp_flags & ONSWAP) {

/* Process is swapped out, leave signal pending. */

sigaddset(&rmp->mp_sigpending, signo);

swap_inqueue(rmp);

return;

}

#endif

/* Switch to the user's FS environment and dump core. */

tell_fs(CHDIR, slot, FALSE, 0);

dump_core(rmp);

}

pm_exit(rmp, 0); /* terminate process */

}

往下講解之前先看下幾個結構體: struct sigregs {

#if _Word_SIZE == 4 short sr_gs;

short sr_fs;

#endif /* _WORD_SIZE == 4 */ short sr_es;

short sr_ds; int sr_di; int sr_si; int sr_bp;

int sr_st; /* stack top -- used in kernel */

int sr_bx; int sr_dx; int sr_cx;

int sr_retreg;

int sr_retadr; /* return address to caller of save -- used

* in kernel */

int sr_pc;

int sr_cs;

178

int sr_psw; int sr_sp; int sr_ss; };

struct sigframe { /* stack frame created for signalled process */

_PROTOTYPE( void (*sf_retadr), (void) );

int sf_signo;

int sf_code;

struct sigcontext *sf_scp; int sf_fp;

_PROTOTYPE( void (*sf_retadr2), (void) ); struct sigcontext *sf_scpcopy;

};

struct sigcontext {

int sc_flags; /* sigstack state to restore */

long sc_mask; /* signal mask to restore */

struct sigregs sc_regs; /* register set to restore */

};

對上面的結構體一一做出一個解釋:

第1:struct sigregs 這個結構體存儲的是CPU各個寄存器的值和一些其他的值: 一些通用寄存器的值就不介紹了,現在來介紹幾個與寄存器值無關的值。

int sr_st; /* stack top -- used in kernel */

這個值的意思就是棧頂指針,先記住,在后面的分析中會有介紹

int sr_retadr; 這個值的意思是調用者保留的返回地址,在進行信號量的處理 過程中,會涉及到處理調用者的最后用戶狀態的下地址,所以這里必須要設一個 值進行保存

第2:sigframe 這個結構體主作用的就是3個: struct sigcontext *sf_scopy 和 int sf_signo以及 void(*sf_retadr)(void)

struct sigcontext *sf_scopy 這個是指向sigcontext結構體,下面會介紹。 int sf_signo 是被信號量號

void (*sf_retadr)(void)是返回函數的地址。

第3 sigcontext結構體,主要是儲存做上下文切換動作一些必需量。 先看下執行流程圖(對于上下函數的一個銜接)

179

wpsB8CE.tmp

#include "syslib.h"

/*=================================================================== ========*

* sys_sigsend (本來本函數在Lib文件中

但是方便講解,在這里拿出來) 接著上面的動作進行分析 *

*====================================================================

=======*/

PUBLIC int sys_sigsend(proc_ep, sig_ctxt)

endpoint_t proc_ep; /* for which process */

struct sigmsg *sig_ctxt; /* POSIX style handling */

{ //這個函數主要是構造一個消息,之后發往內核

message m; int result;

m.SIG_ENDPT = proc_ep;

m.SIG_CTXT_PTR = (char *) sig_ctxt;

// SIG_CTXT_PTR代表的就是信號量上下文地址

result = _kernel_call(SYS_SIGSEND, &m);

//給內核發送一條消息,將執行系統調用

//系統調用號時SYS_SIGSEND,下面也將這個do_sigsend函數附上來

180

wpsB8EE.tmp

return(result);

}

這個函數的主體是進行一系列復制動作,最關鍵的信息就是將用戶棧的

rp_p_regs結構體復制sigmsg這個結構。這個結構體是非常重要的。你想象一下, 如果我現在準備捕獲一個信號量,我們動用了這個信號量處理函數之后,棧和各 個寄存器值都可能發生變化,所以必須要做出一個保留工作,這個函數主體就是 做這個工作。之后將各個結構體的各個域初始化。

看看這個函數主體是做一個什么工作,先看下這個函數執行流程圖:

/*=================================================================== ========*

* do_sigsend 這里的m_ptr其實就是上面的消息m

函數主要設置相關信息到用戶棧 *

*==================================================================== =======*/

PUBLIC int do_sigsend(m_ptr)

message *m_ptr; /* pointer to request message */

{

/* Handle sys_sigsend, POSIX-style signal handling. */ //處理sys_sigsend 這么做是為了符合POSIX標準 struct sigmsg smsg;

register struct proc *rp;

phys_bytes src_phys, dst_phys; struct sigcontext sc, *scp; struct sigframe fr, *frp;

181

if (! isokprocn(m_ptr->SIG_PROC)) return(EINVAL);

if (iskerneln(m_ptr->SIG_PROC)) return(EPERM);

rp = proc_addr(m_ptr->SIG_PROC);//待通知進程的地址

//sigmsg結構從用戶狀態復制到內核地址空間 下面這調用這2個函數的目的 比較明確,就是把用戶態已經寫好的sigmsg結構體復制到這里所定義的smsg結構 體中。這個起始地址是src_phys

/* Get the sigmsg structure into our address space. */

src_phys = umap_local(proc_addr(PM_PROC_NR), D, (vir_bytes) m_ptr->SIG_CTXT_PTR, (vir_bytes) sizeof(struct sigmsg)); if (src_phys == 0) return(EFAULT);

phys_copy(src_phys,vir2phys(&smsg),(phys_bytes) sizeof(struct sigmsg));

/* Compute the user stack pointer where sigcontext will be stored. */ //smsg.sm_stkptr-1是用戶棧指針所指向的地方

scp = (struct sigcontext *) smsg.sm_stkptr - 1;

/* Copy the registers to the sigcontext structure. */ //將CPU中的寄存器都復制到sigcontext結構

memcpy(&sc.sc_regs, (char *) &rp->p_reg, sizeof(struct sigregs));

/* Finish the sigcontext initialization. */

//將sc其他2個量進行設置

sc.sc_flags = SC_SIGCONTEXT; sc.sc_mask = smsg.sm_mask;

/* Copy the sigcontext structure to the user's stack. */

//將剛剛設置好的sigcontext結構體復制到要發送信號量的用戶棧進程中

dst_phys = umap_local(rp, D, (vir_bytes) scp,

(vir_bytes) sizeof(struct sigcontext)); if (dst_phys == 0) return(EFAULT);

phys_copy(vir2phys(&sc), dst_phys, (phys_bytes) sizeof(struct sigcontext));

/* Initialize the sigframe structure. */

//現在做最后一步工作,初始化sigframe結構體工作。這個結構體對從信號量的 //返回有非常中要的作用

frp = (struct sigframe *) scp - 1; fr.sf_scpcopy = scp;

fr.sf_retadr2= (void (*)()) rp->p_reg.pc; fr.sf_fp = rp->p_reg.fp;

rp->p_reg.fp = (reg_t) &frp->sf_fp; fr.sf_scp = scp;

fr.sf_code = 0; /* XXX - should be used for type of FP exception */

182

fr.sf_signo = smsg.sm_signo;

fr.sf_retadr = (void (*)()) smsg.sm_sigreturn;

/* Copy the sigframe structure to the user's stack. */

dst_phys = umap_local(rp, D, (vir_bytes) frp, (vir_bytes) sizeof(struct sigframe)); if (dst_phys == 0) return(EFAULT);

phys_copy(vir2phys(&fr), dst_phys, (phys_bytes) sizeof(struct sigframe));

/* Reset user registers to execute the signal handler. */ //設置用戶寄存器來執行信號處理函數

rp->p_reg.sp = (reg_t) frp;

rp->p_reg.pc = (reg_t) smsg.sm_sighandler;

return(OK);

}

從時間上看,執行完上一個函數之后,還是在內核中,內核調度器調度上面被設 置信號量的進程之后,進程就會調用信號量處理函數,這個宏觀上只是一個順序 執行,但是在微觀上還是做了非常多動作。

現在我們分析這個問題,信號量函數處理完之后,我們是不是應該設置一個函數 來執行信號量返回調用的操作?答案是毫無疑問的,因為這個動作能需要重新設 置相關的寄存器的量。我們就從這個動作開始 也就是 do_sigreturn,這個函數是 PM 一個調用函數。我們假設已經進入了這個函數。我先看下 do_sigreturn 主要 是干了什么工作?

do_sigreturn 函數主要是調用這個 sys_sigreturn 函數,這個函數是與內核函數的 一個接口,調用系統任務的系統函數。

/*=================================================================== ========*

* do_sigreturn 注意這個函數是PM機制下的信號返回函數,下面

那個函數是內核狀態下的信號返回函數 *

*==================================================================== =======*/

PUBLIC int do_sigreturn() {

/* A user signal handler is done. Restore context and check for * pending unblocked signals.

*/

int r;

mp->mp_sigmask = (sigset_t) m_in.sig_set; sigdelset(&mp->mp_sigmask, SIGKILL);

183

r = sys_sigreturn(who, (struct sigmsg *) m_in.sig_context); check_pending(mp);

return(r);

}

/*=================================================================== ========*

* sys_sigreturn 與內核的一個接口函

數,函數主體構造一個消息,調用do_sigreturn函數,我們就順著往后面那個

函數 *

*====================================================================

=======*/

PUBLIC int sys_sigreturn(proc_ep, sig_ctxt)

endpoint_t proc_ep;

struct sigmsg *sig_ctxt; {

message m;

int result;

m.SIG_ENDPT = proc_ep;

/* for which process */ /* POSIX style handling */

m.SIG_CTXT_PTR = (char *) sig_ctxt;

result = _kernel_call(SYS_SIGRETURN, &m); return(result);

}

我們接著上面的那個函數,現在來看看這個函數,這個函數的主體就是將之前保 留的p_regs都還原到現在這個進程的p_regs

184

wpsB90F.tmp

/*=================================================================== ========*

* do_sigreturn 內核處理信號返回函數,這個函數上面那個函

數調用SYSTEM進程,之后SYSTEM進程就調用這個函數 *

*==================================================================== =======*/

PUBLIC int do_sigreturn(m_ptr)

message *m_ptr; /* pointer to request message */

{

/* POSIX style signals require sys_sigreturn to put things in order before * the signalled process can resume execution

*/

struct sigcontext sc;

register struct proc *rp; phys_bytes src_phys;

if (! isokprocn(m_ptr->SIG_PROC)) return(EINVAL); if (iskerneln(m_ptr->SIG_PROC)) return(EPERM); rp = proc_addr(m_ptr->SIG_PROC);

185

/* Copy in the sigcontext structure. */

//復制sigcontext結構,分析下從哪里復制到哪里!

//這種復制就是從外核復制到內核中,將m_ptr結構所指向的sigcontext結構體 復制到前面所定義的結構體sc中。

//注意這里是返回操作!返回就是將之前所定義好的內核寄存器復原。這點比較 的總要,在后的代碼會有所涉及!

src_phys = umap_local(rp, D, (vir_bytes) m_ptr->SIG_CTXT_PTR, (vir_bytes) sizeof(struct sigcontext));

if (src_phys == 0) return(EFAULT);

phys_copy(src_phys, vir2phys(&sc), (phys_bytes) sizeof(struct sigcontext));

/* Make sure that this is not just a jump buffer. */

if ((sc.sc_flags & SC_SIGCONTEXT) == 0) return(EINVAL);

/* Fix up only certain key registers if the compiler doesn't use * register variables within functions containing setjmp. */

//將rp.p_reg復原捕獲信號量前得各個值,這點很重要,因為在捕獲信號量時, 指向信號處理函數,可能各個寄存器的值都發生了變化,為了保持他們原來的狀 態,這里必須進行在一次的復制

if (sc.sc_flags & SC_NOREGLOCALS) { rp->p_reg.retreg = sc.sc_retreg; rp->p_reg.fp = sc.sc_fp; rp->p_reg.pc = sc.sc_pc; rp->p_reg.sp = sc.sc_sp; return(OK);

}

sc.sc_psw = rp->p_reg.psw;

#if (CHIP == INTEL)

/* Don't panic kernel if user gave bad selectors. */

//段選擇器的恢復工作,將rp指向的進程各個段選擇器都復制到sc結構體相應的 域中。為后面的還原過程做出基礎

sc.sc_cs = rp->p_reg.cs; sc.sc_ds = rp->p_reg.ds; sc.sc_es = rp->p_reg.es; #if _WORD_SIZE == 4 sc.sc_fs = rp->p_reg.fs; sc.sc_gs = rp->p_reg.gs;

#endif

#endif

186

wpsB92F.tmp

/* Restore the registers. */

//這里就是全部重新賦值,sc.sc_regs的各個值都恢復到被信號量的棧值。 memcpy(&rp->p_reg, &sc.sc_regs, sizeof(struct sigregs)); return(OK);

}

從時間軸上來看。執行完這個函數,只要調度器調度這個進程,這個進程就可以 繼續被信號量中斷前得指令繼續執行。從宏觀上,用戶只是會覺得整個調用過程 就是一個普通的調用函數一樣。!

MINIX3 的信號量機制基本上就是這樣,事實上,讀者可以結合前面的時鐘機制, 繼續分析一次 alarm 怎么來實現這種信號量機制!最后用一副圖來表示我們整個 執行過程:

至此,信號量處理機制基本講解完成,信號量機制比較復雜,需要仔細的分析。


上一篇:有名管道_2

下一篇:倒騰路由器

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产福利成人在线| 91国内免费在线视频| 日韩美女免费线视频| 日韩视频免费大全中文字幕| 日韩不卡中文字幕| 欧美亚洲在线观看| 久久99亚洲热视| 91精品久久久久久久久不口人| 在线成人激情黄色| 亚洲精品之草原avav久久| 欧美激情精品久久久久久大尺度| 久久99青青精品免费观看| www国产91| 亚洲福利视频久久| 日韩的一区二区| 欧美中文字幕在线观看| 亚洲精品天天看| 国产成人精品最新| 国产精品扒开腿做爽爽爽视频| 成人欧美在线视频| 精品国产欧美一区二区三区成人| 精品久久久久久中文字幕一区奶水| x99av成人免费| 精品视频在线播放色网色视频| 国产精品成人aaaaa网站| 日韩在线视频网| 久久久久亚洲精品国产| 欧美刺激性大交免费视频| 狠狠躁夜夜躁久久躁别揉| 亚洲一区二区三区在线视频| 不卡av在线网站| 色妞欧美日韩在线| 92看片淫黄大片欧美看国产片| 69**夜色精品国产69乱| 国产在线高清精品| 日韩精品电影网| 精品国产91久久久久久老师| 成人综合网网址| 日韩精品极品毛片系列视频| 55夜色66夜色国产精品视频| 美女扒开尿口让男人操亚洲视频网站| 日韩av网址在线观看| 国a精品视频大全| 国产精品7m视频| 成人在线观看视频网站| 欧美激情国产精品| 亚洲图片欧洲图片av| 国产日韩专区在线| 中文字幕在线日韩| 亚洲aⅴ男人的天堂在线观看| 午夜精品久久久久久久99热浪潮| 久久频这里精品99香蕉| 日韩免费观看在线观看| 精品久久久香蕉免费精品视频| 久久久久www| 九九久久精品一区| 亚洲精品一区av在线播放| 美女国内精品自产拍在线播放| 精品久久久久久久久久久久| 国产精品精品久久久| 久久精品国产免费观看| 日韩在线欧美在线| 国产精品国产三级国产专播精品人| 91精品久久久久久久久久另类| 亚洲乱码一区av黑人高潮| 精品国产依人香蕉在线精品| 久久中文久久字幕| 国产亚洲人成网站在线观看| 亚洲欧洲中文天堂| 国产欧美精品日韩| 91精品成人久久| 国产一区二区三区欧美| 欧美成人午夜激情| 色无极亚洲影院| 欧美野外猛男的大粗鳮| 久久久久久久久久久91| 国产精品久久久久秋霞鲁丝| 96精品久久久久中文字幕| 精品中文字幕视频| 久久精品影视伊人网| 日韩电影在线观看中文字幕| 日韩欧美高清在线视频| 91免费综合在线| 国产热re99久久6国产精品| 国产精品第二页| 亚洲欧美日韩国产成人| 大伊人狠狠躁夜夜躁av一区| 在线观看国产成人av片| 一区二区三区四区在线观看视频| 国产欧美精品一区二区三区-老狼| 亚洲激情 国产| 久久亚洲精品中文字幕冲田杏梨| 亚洲欧美一区二区三区在线| 91热精品视频| 欧美视频国产精品| 最新的欧美黄色| 欧美猛少妇色xxxxx| 日韩av综合网| 91国内免费在线视频| 国产精品久久久久久久7电影| 欧美激情手机在线视频| 久久久久国产视频| 精品少妇v888av| 国产成人亚洲精品| 狠狠躁天天躁日日躁欧美| 久久久久久久激情视频| 成人av色在线观看| 欧美二区乱c黑人| 国产精品日韩欧美大师| 一道本无吗dⅴd在线播放一区| 国产精品igao视频| 日韩av三级在线观看| 永久免费精品影视网站| 97在线视频免费观看| 欧美—级高清免费播放| 国产精品福利片| 秋霞成人午夜鲁丝一区二区三区| 97久久精品人人澡人人爽缅北| 欧美一区二粉嫩精品国产一线天| 久热精品视频在线免费观看| 久久久精品免费视频| 亚洲天堂av在线免费观看| 国模精品系列视频| 久久国产精品免费视频| 欧美极品美女视频网站在线观看免费| 大伊人狠狠躁夜夜躁av一区| 97视频免费观看| 国产精品视频一区二区高潮| 国产精品美女免费| 亚洲免费视频在线观看| 欧美日韩黄色大片| 在线日韩欧美视频| 毛片精品免费在线观看| 国产成人精品免高潮在线观看| 久久在线免费视频| 精品自在线视频| 日韩中文字幕在线播放| 欧美性色视频在线| 成人免费网站在线看| 国产精品久久久久7777婷婷| 亚洲成人网在线| 亚洲精品白浆高清久久久久久| 国产精品高潮在线| 中文字幕亚洲字幕| 国产精品久久久久影院日本| 中文字幕精品视频| 色偷偷噜噜噜亚洲男人的天堂| 黑人巨大精品欧美一区二区免费| 久久成人亚洲精品| 久久手机精品视频| 国产mv免费观看入口亚洲| 91精品中国老女人| 欧美极品第一页| 97视频网站入口| 亚洲福利视频免费观看| 亚洲第一色在线| 欧美一级电影免费在线观看| 国产成人亚洲综合青青| 日韩免费观看视频| 国产精品香蕉在线观看| 久久久精品在线| 日韩毛片中文字幕| 久久久精品中文字幕| 欧美贵妇videos办公室|