Chinaunix首页 | 论坛 | 博客
  • 博客访问: 95316
  • 博文数量: 46
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 505
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-22 19:56
文章分类
文章存档

2008年(46)

我的朋友

分类: LINUX

2008-05-02 15:37:59

信号机制是unix采用的一种很古老的进程通信方式(linux搬过来用),用来向一个进程通知另一个进程(也可以是自己)发生了什么事,要怎样处理。
1.信号:
    信号就是在signal.h中定义的一系列以SIG开头的宏,实质是整数。信号可以通过进程(调用kill,raise,alarm,abort等,实质还是内核发信号)发出,也可以是内核(通过用户按下ctl+c)发出。信号也叫软中断。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
2. 信号处理方法:
   收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:第一种方法是通过定义一个函数指针(原型为 void (*)(int)作为处理函数对需要处理的信号进行处理。第二种方法是通过宏SIG_IGN(定义在signal.h中的一个函数指针)忽略某个信号,对该信号不做任何处理,就象未发生过一样。第三种方法是通过SIG_DFL(定义在signal.h中的一个函数指针)对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。
进程通过系统调用signal来指定进程对某个信号的处理行为。
void (*signal(int _sig, void (*_func)(int)))(int);
上面这个是signal.h中声明的信号管理函数的标准c版,但是这个函数比较难读(其实也不难啊,就是声明了2个函数指针而已),所以gnu重新定义了该函数,原型是
typedef void (* sighandler_t)(int);
sighaneler_t  signal(int signum, sighandler_t haneler);函数改变signum的处理函数后,返回signum信号的上一次处理,返回也可以是SIG_ERR(定义在signal.h中的一个宏),表示出错。
 
在进程表的表项中(struct task_struct)有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。
信号的类型
3.信号分类:
发出信号的原因很多,这里按发出信号的原因简单分类,以了解各种信号:
(1) 与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
(2) 与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
(3) 与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
(4) 与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
(5) 在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。
(6) 与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
(7) 跟踪进程执行的信号。
4.信号机制:
弄明白内核如何向一个进程发送信号、进程如何接收一个信号、进程怎样控制自己对信号的反应、内核在什么时机处理和怎样处理进程收到的信号
 1.内核通过在进程的struct task_struct中的信号域中设置相应的为其来实现向一个进程发送信号。
 2. 进程在由内核态返回用户态时检查是否收到信号(进程处于运行running状态,会对信号进行处理。),或者是在要进入或离开一个适当的低调度优先级睡眠状态时(看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程,进程就不会对信号进行)。
 
说了这么多,还是写个例子加深理解了。
 
catchint.c
 
#include
#include
#include
 
void handler(int signum)  // 自定义的信号处理函数,参数是信号编号
{
   char buffer[200];
   char * cp;
   int offset;
   strcpy(buffer, "handler:caught signal");
   cp=buffer+strlen(buffer);
   if(signum >100)
      offset=3;   // 不可能,因为信号编号在100以内。
   else if(signum>10)
      offset =2;
   else offset=1;
   cp+=offset;
   *cp--='\0'; // 相对于*cp='\0'; cp-- .写上新的字符串结束标记,然后指针上移准备记录信号
                   编 号
 while(signum>0)  // 填充数字
{*cp--=(signum%10)+'0';
 signum/=10;
}
strcat(buffer,"\n");
write(2,buffer,strlen(buffer)); // 终端输出字符串
}
 
int main()
{
   signal(SIGINT,handler); (按ctrl+c产生该信号,用于终止进程);
    for(;;)
      pause(); // 该函数定义在unist.h中,用于挂起本进程,函数原型为int pause();等待信号接收并且信号被处理才返回 
     return 0;
}
 
编译:gcc -o catchint catchint.c
运行 ./catchint
     handler:caught signal      // 按ctrl+c
     handler:caught signal      // 按ctrl+c
     handler:caught signal      // 按ctrl+c
从上述结果可以看出,进程捕捉到了SIGINT信号,并对其进行处理。
 
信号屏蔽:
int sigemptyset(sigset_t *set) //将信号集清空
int sigaddset( sigset_t *set,int signum)//将信号加入信号集中
int sigprocmask(int how,const sigset_t * set, sigset_t *oset)
 // how主要为 SIG_BLOCK,SIG_UNBLOCK,将set放入(或者放出)进程的信号阻塞集合中,并将信号阻塞集合保存在oset中
修改程序
 
catchint.c
 
#include
#include
#include
int main()
{ sigset_t mask;
  sigempty(&mask);
  sigaddset(&mask,SIGINT);
  //sigprocmask(SIG_BLOCK,&mask,NULL);
  for(;;) printf("%s\n","xunhuan!");
  return o;
}
 
结果:注释掉sigprocmask,进程通过ctrl+c接受SIGINT信号终止死循环
     不注释sigprocmask,进程屏蔽SIGINT信号,就不可以通过ctrl+c接受SIGINT信号终止死循环
 
 




 
阅读(733) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~