日期:2014-05-16  浏览次数:20717 次

请问在程序中调用signal()进行注册的时候,注册的过程是怎样实现的
最近在看linux0.11,有这么个问题,希望大家解答。
我想问的是在程序中出现signal()函数的时候,是不是仅仅只是注册这个函数,而当程序往下执行而发出信号的时候,这个时候才发生int0x80中断从而进行调用sys_signal和do_signal函数?另外,注册这个过程是怎样实现的?

为了表述的更清晰一些,我在网上找了个例子来说明,如下:

int   main()  
{  
  /*   进程号   */  
  pid_t   pid;  
  /*   想用信号?那先对信号处理函数进行注册,对应起来   */  
  signal(SIGCHLD,   handler);  
  /*   程序在这里“分叉”,新的进程创建了   */  
  pid   =   fork();  
  /*   通过fork()的返回值来判断是父进程还是子进程   */  
  switch(pid){  
      /*   返回   -1,很不幸,创建进程失败了。可能是没有足够的内存空间,也可能已经开起了太多的进程。   */  
      case   -1:  
          perror( "fork\n ");  
          break;  
      /*   返回   0,现在是在子进程里运行,那就调用子进程的操作函数。   */  
      case   0:  
          /*   一个运行65535次的循环,如果你的机子太快,不能看清楚两个进程同时运行的效果,那就再加大循环次数。或用sleep()函数   */  
          big_loop(FAC_N);  
          /*   取得子进程的PID,你可以看清楚子进程和父进程的PID是不同的(子进程的PID比父进程的要大,因为是在父进程运行后才创建的)。*/  
          printf( "PID:%d\n ",   getpid());  
          /*   子进程执行完了,发送信号   SIGCHLD。由于前面对这个信号进行了注册,这时就会转入信号处理操作。   */  
          break;  
      /*   哈哈,返回的即不是错误,又不是子进程,那就是父进程喽。*/  
      default:  
          /*   这里让用户输入了4个数   */  
          input_information();  
          /*   取得子进程的PID。*/  
          printf( "PID:%d\n ",   getpid());  
          break;  
  }  
  exit(0);      
}  


请问是不是在

  /*   想用信号?那先对信号处理函数进行注册,对应起来   */  
  signal(SIGCHLD,   handler);  

这里的时候还没有进行int0x80的系统调用,这里仅仅是注册?那注册如何实现和完成的?


而在

          /*   子进程执行完了,发送信号   SIGCHLD。由于前面对这个信号进行了注册,这时就会转入信号处理操作。   */  

这个地方才触发了真正的signal的系统调用的?

非常希望大家能回答我的问题,不甚感激


------解决方案--------------------
按我的理解,用户态的signal()执行后会调用核态的sys_signal(),并把信号处理函数以及其他信息添加到当前进程的struct task_struct的sigaction[]中,此时不会触发实际的信号处理函数。

信号的处理的一个很重要的入口在system_call.s的ret_from_sys_call(系统调用返回时)中,在发现有信号pending(且未被block)时,此时会调用do_signal(),将信号处理函数插入到用户态堆栈中,在系统调用返回后会立即执行信号处理函数。
------解决方案--------------------
分三步
第一步,注册信号处理函数,这个过程要在内核中记录处理函数的地址
第二步,其它进程或内核或自己递送信号,其实也是在内核中作个标记,意思是说发生了某个信号
第三步,从系统返回用户空间之前执行信号处理程序
------解决方案--------------------
1). 首先signal就是int 0x80实现的,没有更多的功能,就是添加进程的signal_handler
2). Linux属于内核非抢占调度,也就是说内核态不主动让出CPU,schedule没有办法切换进程的,只能在用户态抢占(通过中断/异常进入内核态并完成重新调度的)
3). 信号的处理是在从内核态到用户态切换之前完成的,比如中断/系统调用处理完毕之前:此时为了返回用户态的寄存器(这是由int 0x80导致的并保存在内核态堆栈的),会调用ret_from_intr/ret_from_syscall/ret_from_exception, 这三个函数都会检查有没有pending的signal,如果有,就调用do_signal(),当然这比较复杂,因为signal_handler属于用户态的空间,所以内核要在user-> stack上面建立frame,然后再回到内核态,最终返回系统调用/异常。
4). pending signal是由发送方调用kill(),这个系统调用同样进入内核态,找到信