从windows到linux的转换(1):信号与消息以及SIGPIPE 的处理

一.信号与消息
     linux里边的信号和win下边的的消息有基本相同的同能,都有各种各样的信号(消息)及其相应的信号(消息)处理函数。
    1. 信号处理函数添加。
     signal函数。
    下边是从高级编程上拷贝来。平平无奇,一看就明白了。
    定义:
        typedef void Sigfunc(int);
        Sigfunc *signal(int, Sigfunc *);
        将一个信号和他的处理函数绑定在一起。非常简单的东西。 在wind下,用了很多遍了把?
        需要注意的是他的返回值:
        #define SIG_ERR (void (*)())-1
        #define SIG_DFL  (void (*)())  0
        #define SIG_IGN  (void (*)())  1
 
        以前曾经讲过,一定要注意返回值的处理。一定要判断函数的返回值。
                   
    2. 所有的wind message都是可以截获的,但是Linux下,SIGSTOP,SIGKILL信号是不能被截获的。保留这两个信号不被截获是为了让系统,用户,可以直接kill掉任何的进程(当然是有权限杀的进程).
   
    3. 系统函数是可以被信号打断的。
        高级编程这样写道:
            进程捕捉到信号并继续执行时,它首先执行该信号处理程序中的指令。如果从信号处理程序返回(例如没有调用e x i t或l o n g j m p),则继续执行在捕捉到信号时进程正在执行的正常指令序列(这类似于硬件中断发生时所做的)。但在信号处理程序中,不能判断捕捉到信号时进程执行到何处。
                  
            也就是说,系统不给你保留当时的栈环境,直接跳出正在执行的系统函数。这与对硬中断的处理(中断环境保存,执行中断代码,中断环境恢复)是不同的。
                  
            比如说,sleep(60)的过程中,进程收到一个信号。这个信号会打断这个函数的执行,也就是没有睡够,睡了20秒,30秒就出来了。这个造成的后果还小一点,你想想,要是在写文件的过程中,写了一半就被打断,那会出现什么后果?            
                  
            我们常用的对这个事情的处理办法:          
            1. 屏蔽掉不需要的或者所有的的信号.
                sigset_t sets;//一个空的sigset集合。
                sigfillset(&sets);//填充所有的信号。
                sigprocmask(SIG_BLOCK, &sets, 0);
                顺便介绍一下sigprocmask的一个参数。需要注意,进程有当前的阻塞信号的集合。对集合的操作。
                    SIG_BLOCK:
                        The resulting set shall be the union of the current set and the signal set pointed to by set.
                    SIG_SETMASK:
                        The resulting set shall be the signal set pointed to by set.
                    SIG_UNBLOCK
                        The resulting set shall be the intersection of the current set and the complement of the signal set pointed to by set.
        
                所有的信号都屏蔽了,那么如何用信号让程序正常退出那? linux可不比 windows还有一个关闭按钮让你按。大多数程序得用到信号让程序正常退出,而且不能把程序直接kill掉,因正常退出的过程中通常需要做一部份(比如清理等)工作。这时,你可以让程序阻塞等待一个被屏蔽掉的信号上,通常是,SIGTERM.有点相当于windows里边的close消息。
                    while( sigwaitinfo(&sets, 0) != SIGTERM );
                   
                SIGTERM信号来了,程序准备退出。呵呵!
                          
                第二个参数是sig的info,不用的话设置为NULL. 用的话,自己查帮助。
               
                sigtimedwait只不过加了一个时间参数。不过,linux下的超时和win下边的不太一样。他的超时是与现在的时间作对比的。下边是我写的一个函数。用来获得超时的时间struct timespec结构。
               
                int getTimeOut(int sec,long nsec,struct timespec& abstimeout)
                {
                     struct timeval now;
                    
                     gettimeofday(&now, 0);
                     abstimeout.tv_sec = now.tv_sec + sec;
                     abstimeout.tv_nsec = (now.tv_usec * 1000) + nsec;
                     if (abstimeout.tv_nsec >= 1000000000)
                     {
                           abstimeout.tv_sec++;
                           abstimeout.tv_nsec -= 1000000000;
                     }
                    
                     return 0;
                }
                                        
                                        
                一般有这这种需要的程序,都是在主线程里边屏蔽掉所有信号,然后,在另外的线程里边阻塞等待一个信号。那么,其他线程的函数继承主线程里边的信号屏蔽的办法,屏蔽掉所有信号!不过,用gdb调试的时候,gdb产生的信号,用这些办法是拦不住的。这种办法只能拦住系统发来的信号。

                                 
            2. 对返回值进行处理。 "手工" 支持这种软中断。
                对可能中断的函数做应用级别的处理,例如:
                   
                                     while( ::close(fd) == -1 && errno == EINTR );
                                    
                sleep被中断,会返回剩余的秒数.write会返回已经正确写入的字节数目。等等。利用这些信息,可以做一下函数重入,只是写起来比较麻烦。
                           
                           
               
                需要注意的是:在Linux里边,系统函数分成可再入的和不可再入的。区分的原则是这个函数是否更改了全局数据结构。也就是,这个函数在重入以前,要考虑上一次进入的时候,对全局数据结构的更改。也就是需要的话,自己做点环境保存和恢复。比如:
               
                高级编程这么写到:
                    要了解在信号处理程序中即使调用列于表1 0 - 3中的函数,因为每个进程只有一个e r r n o变量,所以我们可能修改了其原先的值。考虑一个信号处理程序,它恰好在m a i n刚设置e r r n o之后被调用。如果该信号处理程序调用r e a d,则它可能更改e r r n o的值,从而取代了刚由m a i n设置的值。因此,作为一个通用的规则,当在信号处理程序中调用表1 0 - 3中列出的函数时,应当在其前保存,在其后恢复errno。
                                                       
                       
               还需要注意的是,sigaction里边有个参数,SIG_RESTART. 他的意思是,当函数被信号中断以后,系统可以自动重新调用这个函数一遍。而且有的版本还不支持这个参数。
              
               试着想像一下:参数不变的条件下,你的write函数重新调用一遍会出现什么后果?
              
               高级编程里边是这么讲的:
                    P O S I X . 1允许实现再起动系统调用,但这并不是必需的。系统V的默认工作方式是不再起动系统调用。但是S V R 4使用s i g a c t i o n时(见1 0 . 1 4节),可以指定SA_RESTART选择项以再起动由该信号中断的系统调用。在4 . 3 + B S D中,系统调用的再起动依赖于调用了哪一个函数设置信号处理方式配置。早期的与4 . 3 B S D兼容的s i g v e c函数使被该信号中断的系统调用自动再起动。但是,使用较新的与POSIX . 1兼容的sigaction则不使它们再起动。但如同在S V R 4中一样,在sigaction中可以使用SA_RESTART选择项,使内核再起动由该信号中断的系统调用。
                           
                  
               在windows下,你不用担心消息会中断系统调用。不过,也可以认为丧失的是对软中断信号的的快速相应。

二. 几个特殊的信号
    1. SIGPIPE:
    在linux下写socket的程序的时候,如果尝试send到一个disconnected socket上,就会让底层抛出一个SIGPIPE信号。
    这个信号的缺省处理方法是退出进程,大多数时候这都不是我们期望的。因此我们需要重载这个信号的处理方法。调用以下代码,即可安全的屏蔽SIGPIPE:
        struct sigaction sa;
        sa.sa_handler = SIG_IGN;
        sigaction( SIGPIPE, &sa, 0 );
   
    把这几行代码,看作是windows下的WSAStartUp()就好了。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值