集群系统MOSIX分析(7)_VMware, Unix及操作系统讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  VMware, Unix及操作系统讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3994 | 回复: 0   主题: 集群系统MOSIX分析(7)        下一篇 
cc
注册用户
等级:中校
经验:1900
发帖:195
精华:0
注册:2011-7-25
状态:离线
发送短消息息给cc 加好友    发送短消息息给cc 发消息
发表于: IP:您无权察看 2011-9-5 11:32:32 | [全部帖] [楼主帖] 楼主

LINUX信号机制

信号是异步的进程间通讯机制,是在软件层次上对中断机制的一种模拟。LINUX内核的信号机制符合POSIX.4的规定,这是POSIX.1标准的一个超集。

每个进程的task_struct结构中都有个指针sig,指向一个singal_struct结构,结构中的数组action[]相当于一个信号向量表,每个元素确定了进程接收到一个具体的信号时应该采取的行动。

struct signal_struct {
       atomic_t count;
       struct k_sigaction action[_NSIG];
       spinlock_t siglock;
};


那么系统如何判断一个进程是否有信号在等待处理呢?这是通过task_struct结构中的sigpending成员。task_struct结构中的blocked成员则为屏蔽信号的集合,pending成员则为信号队列,每产生一个信号则把它挂入这个队列,信号位图signal也保存在其中。

用户常常要自己定义对信号的处理程序,并且用户的处理函数是位于用户空间的。LINUX提供了系统调用signal(sys_signal)和sigaction(sys_sigaction 或sys_rt_sigaction)为信号设置处理向量。用户设置信号处理的时机我们是不能确定的,可以在进程迁移前,也可以在进程迁移之后,进程可以在不同的节点间多次迁移,因此,如何保证信号不被丢失并且都能被正确处理就很重要。并且我们注意到,进程在迁移时,并不将信号向量表迁移到目标进程,而只是将进程的异步信号和强制信号信息传送到目标进程【参见mig_send_misc()和mig_do_receive_misc()】。

struct asig_h
{
       unsigned int sigs;/*信号*/
       int nforced;/*内核发送的强制信号的个数*/
};
struct mosix_task
{。。。。。。。
uint32_t asig; /*到达REMOTE的信号 */
siginfo_t *forced_sigs; /* REMOTE强制信号信息*/
int nforced_sigs; /* REMOTE强制信号的个数 */
short sigmig; /* 迁移时接收的信号 */
}


int mig_send_misc(int credit)
{ struct mig_misc_h m;
      register struct task_struct *p = current;
      ………………..
      m.asig.sigs = p->mosix.asig;
      m.asig.nforced = p->mosix.nforced_sigs;
      forced_sigs = p->mosix.forced_sigs;
      sti();
      if(comm_send(MIG_MISC, &m, sizeof(m), forced_sigs,
       m.asig.nforced * sizeof(siginfo_t), 0))
       ………………
}


因此,我们可以说进程信号处理的状态是保留在DEPUTY方的。这样做也是很自然的。首先在MOSIX中,对于REMOTE进程,几乎所有的系统调用都是请求DEPUTY来处理,和信号相关的一些系统调用也不了例外。例如,sigprocmask()改变本进程得信号屏蔽位图,sigpending()检查有哪些信号已到达而未被处理,signal()和sigaction()安装信号处理程序。其次,在不少内核操作中,进程进入睡眠以后刚被唤醒时,都会检测信号的存在从而提前返回到用户空间。而DEPUTTY和REMOTE可以分别看作对系统上下文和用户上下文的抽象,所以DEPUTY保留着信号处理的状态。



回页首

信号响应

对信号的检测和响应总是发生在系统空间,通常发生在两种情况下:第一,当前进程由于系统调用、中断或异常而进入系统空间以后,从系统空间返回到用户空间的前夕。第二,当前进程在内核中进入睡眠以后刚被唤醒时,由于信号的存在而提前返回到用户空间。

当进程由于中断进入系统空间以后,中断处理程序服务完后,将会转到入口ret_from_intr 。当进程由于异常而进入系统空间后,将会跳到error_code从而最终转到ret_from_exception处理【参见entry.S】。如果中断或异常发生于用户空间,则转移到ret_check_reschedule,否则发生于内核空间,则到达restore_all。当进程由于系统调用进入系统空间,将最终走到ret_from_sys_call。

ENTRY(ret_from_sys_call)
。。。。。
ret_check_reschedule:
cli # need_resched and signals atomic test
cmpl $0,need_resched(%ebx)/*判断是否需要调度*/
jne reschedule
cmpl $0,sigpending(%ebx) /*判断是否有悬挂的信号/
jne signal_return /*如果有信号待处理,则跳到signal_return */
straight_to_mosix:
call SYMBOL_NAME(mosix_pre_usermode_actions)
testl %eax,%eax
jne ret_from_sys_call
restore_all:
RESTORE_ALL
ALIGN
signal_return:
sti # 开中断
handler
testl $(VM_MASK),EFLAGS(%esp)/*是否处于VM86模式*/
movl %esp,%eax
jne v86_signal_return /*是VM86模式的话,则转到v86_signal_return */
call SYMBOL_NAME(do_signal) /* do_signal 对信号进行处理*/
jmp straight_to_mosix


从代码中我们可以看到,如果有信号待处理,则在退出系统空间前,会跳到signal_return,调用do_signal处理信号。

我们看看do_signal ,它对信号作出具体的反应。如果当前进程是REMOTE,它只是简单的返回0。否则,该函数根据当前进程的signal域,确定进程收到了那些信号。对进程收到的每一个信号,从进程的信号等待队列中找到该信号对应的附加信息,从进程的sig域的action数组中找到信号的处理程序及其相关的信息。于是,如果用户设置了信号处理程序(在用户空间中),则最终会通过函数handle_signal()准备好对处理程序的执行 。

用户提供的信号处理程序是在用户空间执行的,而且执行完毕以后还还要回到系统空间。LINUX实现的机制如下:

  1. 用户空间堆栈中为信号处理程序的执行预先创建一个框架,框架中包括一个作为局部量的数据结构,并把系统空间的"原始框架"保存在这个数据结构中
  2. 在信号处理程序中插入对系统调用sigreturn()的调用
  3. 将系统空间堆栈中"原始框架"修改成为执行信号处理程序所需的框架
  4. "返回"到用户空间,但是却执行信号处理程序
  5. 信号处理程序执行完毕后,通过系统调用sigreturn()重返系统空间
  6. 在系统调用sigreturn()中从用户空间恢复"原始框架"
  7. 最后再返回到用户空间,继续执行原先的用户程序

对于本地进程,这是在handle_signal()中由setup_rt_frame()或setup_frame()作出安排的。但是,对于DEPUTY进程,则是通过mosix_deputy_setup_frame()实现的。因为,我们已经知道,DEPUTY是永远运行在核心态中的;进程迁移后,代码段和数据段等都完全迁移到远程REMOTE进程。因此,信号处理程序必然是在REMOTE进程上运行的。那么,这又是如何实现的呢。

mosix_deputy_setup_frame()通过deputy_request()函数向REMOTE发送DEP_SETUPFRAME请求,REMOTE将在remote_wait()函数中接收到该请求,调用remote_setup_frame()来响应该请求。REMOTE进程在remote_setup_frame()中,根据DEPUTY传来的参数,通过setup_rt_frame()或setup_frame()安排好一个框架。

这样,当REMOTE进程从系统空间返回到用户空间时,将执行信号处理程序。然后,将通过sigreturn()系统调用重返系统空间。sys_sigreturn()的作用就是从用户空间执行信号处理程序的框架中恢复当初系统空间中的原始框架。它通过restore_sigcontext() 恢复框架的。但是对于DEPUTY进程,则是通过mosix_deputy_restore_sigcontext()函数来恢复系统空间的原始框架的【参见sys_sigreturn()】。这里,因为是REMOTE进程调用sigreturn()系统调用,因此根据我们前面对系统调用的分析,REMOTE进程向DEPUTY进程发送REM_SYSCALL请求,DEPUTY将在通过deputy_syscall()函数中调用sys_sigreturn()来响应该请求。

mosix_deputy_restore_sigcontext()则向REMOTE进程发送DEP_RESTORESIGCONTEXT请求。REMOTE在向DEPUTY发送REM_SYSCALL请求后,将处于remote_wait()循环中等待REM_SYSCALL请求的应答【参见remote_standard_system_call()】。REMOTE在remote_wait()中接收到DEP_RESTORESIGCONTEXT请求后,则通过remote_restore_sigcontext()函数调用restore_sigcontext()真正恢复核心空间的原始框架。此后,当REMOTE进程从系统空间返回后,将回到信号处理前原先的用户空间处继续往下执行。



回页首

信号发送

发送一个信号给进程可以在用户空间通过系统调用发送,如通过sys_kill和 syr_rt_sigqueueinfo调用发送。内核也可以通过force_sig()和force_sig_info()向进程强制发送信号,将屏蔽位强制清除,不允许目标进程忽略该信号。

在用户空间向一个进程发送信号由系统调用sys_kill()实现。该函数调用函数kill_something_info(),它根据情况,或者向单个进程发送信号(kill_proc_info()),或者向一个进程组中的所有进程发送信号(kill_pg_info()), 最终都会调用函数send_sig_info()来完成真正的信号发送。kill_pg_info()中,通过for_each_local_task(p)来查找属于同组的进程。这是因为MOSIX中, 信号只会发给本地进程,而不会发送给REMOTE进程的。对于REMOTE进程,当通过kill()发送信号时,根据我们前面对系统调用的分析,我们知道最终将是由DEPUTY来调用sys_kill()。因此,信号是被挂入DEPUTY的task_struct结构中的pending队列里。



回页首

异步和强制信号的处理

内核也会向进程发送信号,例如当页面异常而又无法恢复时,do_page_fault()页面异常处理程序会通过force_sig()zx向当前进程发送一个SIGBUS信号。内核发送的信号一般都是需要立即作出反应的。MOSIX对系统发送信号的处理方式也和用户发送信号有所不同,内核发送的"强制"信号都保存在mosix_task的forced_sigs指针中。

我们首先看看函数force_sig_info()。如果信号的目的地为REMOTE进程,则:

  1. 如果处于中断服务中,则系统panic
  2. 得到进程已有的强制信号数(n= t->mosix.nforced_sigs)并试图分配内存(x = kmalloc((n + 1) * sizeof(siginfo_t), GFP_KERNEL))用于保存这n+1个信号信息
  3. 如果内存分配失败,则尽量再次发送该信号send_sig(sig, t, 0);返回0
  4. 因为分配内存返回时,可能已经处理了一部分信号,因此要进行检测。如果是,则释放刚刚申请的内存,跳到第二步
  5. 将保存的siginfo_t和新的info拷贝到分配的内存中,保存在mosix结构的forced_sigs中,并增加强制信号计数t->mosix.forced_sigs = x; t->mosix.nforced_sigs++;

我们前面已经分析过,REMOTE进程从系统空间返回到用户态之前,将会调用remote_pre_usermode_actions()函数。remote_pre_usermode_actions()函数将会检测当前进程是否有异步或"强制"信号待处理。如果有,它会通过函数transfer_signals_to_deputy()发送REM_ASIG请求将信号传递给DEPUTY处理【参见remote_pre_usermode_actions()】。DEPUTY则会通过函数deputy_analyse_remote_signals()来处理REM_ASIG请求。它首先从连接中获得"强制"信号信息,通过force_sig_info()向当前进程(即DEPUTY本身)发送强制信息。然后得到每个信号,依次处理,一般都是通过send_sig发给当前进程。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论