《APUE》Chapter 10 Signals (学习笔记加上自己的代码)

 Signals



signal is software exception !



看源码绝对是一种享受.

Programmer view the source code , something like detective find the truth!

                                                                                                --   jason leaster


/* Signal number definitions.  Linux version.
   Copyright (C) 1995,1996,1997,1998,1999,2003 Free Software Foundation, Inc.
   This file is part of the GNU C Library.


   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.


   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.


   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */


#ifdef  _SIGNAL_H


/* Fake signal functions.  */
#define SIG_ERR ((__sighandler_t) -1)           /* Error return.  */
#define SIG_DFL ((__sighandler_t) 0)            /* Default action.  */
#define SIG_IGN ((__sighandler_t) 1)            /* Ignore signal.  */


#ifdef __USE_UNIX98
# define SIG_HOLD       ((__sighandler_t) 2)    /* Add signal to hold mask.  */
#endif




/* Signals.  */
#define SIGHUP          1       /* Hangup (POSIX).  */
#define SIGINT          2       /* Interrupt (ANSI).  */
#define SIGQUIT         3       /* Quit (POSIX).  */
#define SIGILL          4       /* Illegal instruction (ANSI).  */
#define SIGTRAP         5       /* Trace trap (POSIX).  */
#define SIGABRT         6       /* Abort (ANSI).  */
#define SIGIOT          6       /* IOT trap (4.2 BSD).  */
#define SIGBUS          7       /* BUS error (4.2 BSD).  */
#define SIGFPE          8       /* Floating-point exception (ANSI).  */
#define SIGKILL         9       /* Kill, unblockable (POSIX).  */
#define SIGUSR1         10      /* User-defined signal 1 (POSIX).  */
#define SIGSEGV         11      /* Segmentation violation (ANSI).  */
#define SIGUSR2         12      /* User-defined signal 2 (POSIX).  */
#define SIGPIPE         13      /* Broken pipe (POSIX).  */
#define SIGALRM         14      /* Alarm clock (POSIX).  */
#define SIGTERM         15      /* Termination (ANSI).  */
#define SIGSTKFLT       16      /* Stack fault.  */
#define SIGCLD          SIGCHLD /* Same as SIGCHLD (System V).  */
#define SIGCHLD         17      /* Child status has changed (POSIX).  */
#define SIGCONT         18      /* Continue (POSIX).  */
#define SIGSTOP         19      /* Stop, unblockable (POSIX).  */
#define SIGTSTP         20      /* Keyboard stop (POSIX).  */
#define SIGTTIN         21      /* Background read from tty (POSIX).  */
#define SIGTTOU         22      /* Background write to tty (POSIX).  */
#define SIGURG          23      /* Urgent condition on socket (4.2 BSD).  */
#define SIGXCPU         24      /* CPU limit exceeded (4.2 BSD).  */
#define SIGXFSZ         25      /* File size limit exceeded (4.2 BSD).  */
#define SIGVTALRM       26      /* Virtual alarm clock (4.2 BSD).  */
#define SIGPROF         27      /* Profiling alarm clock (4.2 BSD).  */
#define SIGWINCH        28      /* Window size change (4.3 BSD, Sun).  */
#define SIGPOLL         SIGIO   /* Pollable event occurred (System V).  */
#define SIGIO           29      /* I/O now possible (4.2 BSD).  */
#define SIGPWR          30      /* Power failure restart (System V).  */
#define SIGSYS          31      /* Bad system call.  */
#define SIGUNUSED       31


#define _NSIG           65      /* Biggest signal number + 1
                                   (including real-time signals).  */


#define SIGRTMIN        (__libc_current_sigrtmin ())
#define SIGRTMAX        (__libc_current_sigrtmax ())


/* These are the hard limits of the kernel.  These values should not be
   used directly at user level.  */
#define __SIGRTMIN      32
#define __SIGRTMAX      (_NSIG - 1)


#endif  /* <signal.h> included.  */

整个这章就围着上面这家伙死磕了。。。




signal Function

一开始很畏惧这家伙,但是我后来就明白了,很多事情是因为不了解所以畏惧。

Just test it, I am not afraid
The simplest interface to the signal features of the UNIX System is the signal function.

#include <signal.h>
void (*signal(int signo,void (*func )(int)))(int);
Returns: previous disposition of signal (see following) if OK, SIG_ERRon error

这是个非常有意思的函数

APUE给的demo:

#include"apue.h"
#include"stdio.h"
#include"myerr.h"


static void sig_usr(int);


int main()
{
        if(signal(SIGUSR1,sig_usr) == SIG_ERR)
        {
                err_sys("signal error\n");
        }


        if(signal(SIGUSR2,sig_usr) == SIG_ERR)
        {
                err_sys("can't catch SIGUSR2");
        }


        for(;;)
        {
                pause();
        }


        return 0;
}


static void sig_usr(int signo)
{
        if(signo == SIGUSR1)
        {
                printf("received SIGUSR1\n");
        }
        else if(signo == SIGUSR2)
        {
                printf("received SIGUSR2\n");
        }
        else
        {
                err_dump("received signal %d\n",signo);
        }
}


 

       这段代码创建了一个signal handler处理两种信号,SIGUSR1和SIGUSR2 然后for循环里面暂停pause() 关于这个函数, 我很详细的在这里讨论了

http://blog.csdn.net/cinmyheart/article/details/22655659

 不再赘述

Unreliable  Signals

In earlier versions of the UNIX System (such as Version 7),signals were unreliable.

 

        The  problem  with this  code  fragment  is  that  there is awindow  of  time —after  the signal  has occurred,  but before the  call  to signal in  the signal  handler —when  the interrupt  signal could  occur  another  time. This second  signal would  cause  the  default action  to occur,which  for  this  signal  terminates  the process.

 

      虽然APUE这么说,仅仅只是从表面上指出这种现象,真正的会default 导致termination的是这个signalhandler是建立在stack上面的。我的详细http://blog.csdn.net/cinmyheart/article/details/22655659

 

      Another problem with these earlier systemswas that the process was unable to turn a signal off when it didn’t wantthe signal to occur.

 

关闭信号,让这个信号会被触发,貌似我现在还木有想到为什么不要触发这个信号。。。忽略掉不就好了么。。。

 

 

interrupted system call

 A characteristic of earlier UNIX systems was that if a process caught a signal while the process  was blocked  in a  ‘‘slow’’system  call,  the  system call was  interrupted. 

还是在讲早期Unix的事情。。。

 

注意system call 和function的区别。

        Here, we have to differentiate betweena system call and a function. It is a system call within the kernel thatis interrupted when a signal is caught。

 

有些行为可能导致系统调用一直block 那个它想捕捉的signal。

The  slow  system  calls  are those  that can  block  forever.Included in this category are

Reentrant Functions

           The Single UNIXSpecification specifies the functions that are guaranteed to be safe to call  from  within  a  signal  handler.These functions  are reentrant  and  arecalled async-signal  safe by  the  Single  UNIX Specification.

 

 

           就是说,类似于malloc之类的函数,他是可以申请动态内存的,并且有malloc来维护,而这个过程中就可能会对当前进程造成破坏(你说malloc出来的内存如果内存泄漏怎么办,malloc出来的内存越界了怎么办,所以说很危险,不安全。。。)

 

 

           从安全角度出发,不包含全局变量,和malloc family 和free的函数,我们觉得他是安全的。变量都是内部的,函数在stack上面活动,于是这个时候函数是被使用完pop之后,还可以再次被load到stack上面,执行完全一样的功能。不改变堆和全局变量。安全的对信号做出进行处理。

 

           Most  of  the  functions  that  are not included  in  follow  are missing  because  (a)  they  are known  to  use  static data structures, (b) they call malloc or free, or (c) they are part of the standard I/O library.

             值得注意的就是errno 这个变量。是个全局变量,前提是include <errno.h>

多个进程都可能修改这个东东,所以使用的时候记得copy一下,用个中间变量保存到当前的作用域中

 

if we call a nonreentrant function from a signalhandler, the results are unpredictable

 

 

SIGCLD Semantics

 

Two signals  that continually  generate  confusion  are SIGCLD and SIGCHLD

 

linux不存在混淆,他们被定义为同样的值。。。

This older handling ofSIGCLD consists of the following behavior:

If the process specifically setsits disposition to SIG_IGN, children of the calling process  will not  generate  zombie  processes.

 

If we set the disposition ofSIGCLD to be caught, the kernel immediately checks whether  any child  processes  are ready  to  be waited  for and,  if  so,  calls  the SIGCLD handler

      子进程结束之后是会触发SIGCHID的,父进程可以通过waitpid捕捉这个信号。也可以通过pause 挂起当前进程,等待信号重新唤醒当前进程。

demo:

#include "apue.h"
#include "sys/wait.h"
#include "stdio.h"
#include "myerr.h"


static void sig_cld(int signo);

int main()
{
        pid_t pid;

        if(signal(SIGCLD,sig_cld) == SIG_ERR)
        {
                perror("signal error\n");
        }

        if((pid = fork()) < 0)
        {
                perror("fork error\n");
        }
        else if(pid == 0)
        {
                sleep(2);
                _exit(0);
        }


        pause();
        exit(0);
}


static void
sig_cld(int signo)
{
        pid_t pid;
        int status;


        printf("SIGCLD received\n");
        if(signal(SIGCLD,sig_cld) == SIG_ERR)
        {
                printf("signal error\n");
        }


        if((pid = wait(&status)) < 0)
        {
                printf("wait error\n");
        }


        printf("pid %d\n",pid);
}

test result:

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
SIGCLD received
pid 4449





Reliable-Signal  Terminology  and  Semantics


            A signal  is generated for  a  process  (or  sent  to  a  process)  when  the  event  that  causes  the signal  occurs.



           We  say  that  a  signal  is delivered to  a  process  when  the  action  for  a  signal  is  taken.

During the time between the generation of a signal and its delivery ,the signal is said to be pending.




           If  a  signal  that  is blocked is generated for a process, and if the action for that signal is either the default action  or to  catch  the  signal,  then  the  signal  remains  pending  for  the  process  until  the process either (a) unblocks the signal or (b) changes the action to ignore the signal.




          对于一个进程在处理信号之前同时接收到多个相同的信号怎么处理呢?

         POSIX.1  allows  the system  to  deliver the  signal  either  once  or more than once. If  the  system  delivers  the signal more than  once,  we  say  that the signals  are queued.  Most UNIX  systems, however,do not queue signals  unless  they support the real-time extensions to POSIX.1. Instead, the UNIX kernel simply delivers the signal once.

 

如果有多个信号同时送给同一个进程呢?

           POSIX.1  does  not  specify the order  in  which  the  signals  are delivered to  the  process. The  Rationale  for POSIX.1  does  suggest,  how ever ,that  signals  related  to  the  current state of the process be delivered before other signals. (SIGSEGV is one such signal.)

 



         Each process has a signal mask that defines the set of signals currently blocked from delivery to that process.

 

 

 

 

 

 

 

kill and raise Functions

 

           The kill function  sends a  signal  to  a  process or  a  group of  processes.  The raise function allows a process to send asignal to itself

 

#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);
Both return: 0 if OK, −1 on error

 

The call

raise(signo);

is equivalent to the call

kill(getpid(), signo);

 

kill 第一个参数pid的四种情况:

There are f our different conditions for the pid argument to kill.

pid > 0 The signal is sent to the process whose process ID is pid.


pid == 0 The signal  is  sent to  all  processes whose  process  group ID  equals  the process group ID of the sender and for which the sender has permission to send  the  signal. Note that  the  term all processes excludes  an implementation-defined  set of  system  processes. For most  UNIX systems, this  set  of system  processes  includes the  kernel  processes and init (pid 1).


pid < 0The signal  is  sent to  all  processes whose  process  group ID  equals  the absolute value of pid and for which thesender has permission to send the signal. Again, the  set  of all  processes  excludes certain  system  processes, as described earlier.


pid == −1 The signal is sent to all processes on the system for which the sender has permission to  send  the signal. As  before,  the set  of  processes excludes certain system processes.


我有一点比较不明白的是 <0 和 -1 这两个情况划分可能不是很好,虽然软件上可以通过if else if 语句实现,但是我觉得还是不太好,逻辑上

 

        One special  case  for the  permission  testing also  exists:  if the  signal  being sent  is SIGCONT, a process cansend it to any other process in the same session.


值得注意的是process group ID是有循环的,以前的group ID和现在新的group ID可能是一样的,但是不是同一个group 了。以前的group 已经更改ID或者挂掉了。

 

           Be aware, however ,that  UNIX systems  recycle  process IDs  after  some amount  of  time, so  the existence  of a  process  with  a  given process  ID  does not  necessarily  mean that  it’s  the process that you think it is.

 

 

 

Alarm and pause Functions

             The alarm function  allows us  to  set  a  timer that  will  expire at a specified  time in  the future.  When the timer expires, the SIGALRM signal isgenerated. If we ignore or don’t catch this signal, its default action is to terminate the process.


#include <unistd.h>
unsigned int alarm(unsigned int seconds);
Returns: 0 or number of seconds until previously set alarm


 

         The seconds value is the number of clockseconds in the future when the signal should be generated.

If when we call alarm more than once. That previouslyregistered alarm clock is replaced by the new value.

 

we need to be careful to install its signal handler before calling alarm.

 

The pause function suspends the calling process until a signal is caught.


#include <unistd.h>
int pause(void);
Returns: −1 with errno set to EINTR

The only time pause returns is if a signal handler is executed and that handler returns.

In that case, pause returns −1 with errno set to EINTR.

 

 

Code one:

#include"unistd.h"
static void
sig_alrm()
{
       //
}

unsigned int
sleep1(unsigned int nsecs)
{
       if(signal(SIGALRM,sig_alrm) == SIG_ERR)
       {
                return (nsecs);
       }
       alarm(nsecs);
       pause();
       return (alarm(0));
}

 

Code two:

#include"apue.h"
#include"signal.h"
#include"unistd.h"
#include"setjmp.h"


static jmp_buf env_alrm;


static void
sig_alrm(int signo)
{
        longjmp(env_alrm,1);
}


unsigned int
sleep2(unsigned int nsecs)
{
        if(signal(SIGALRM,sig_alrm) == SIG_ERR)
        {
                return (nsecs);
        }
        
        if(setjmp(env_alrm) == 0)
        {
                alarm(nsecs);
                pause();
        }
        
        return (alarm(0));
} 


Code two 很好的消除了race condition。。。。想了很久才明白,确实这招很妙,消除race condition

 

首先说明为什么代码一有竞争

由于alarm(nsecs)和pause()指令太接近了。如果nsecs太小,可能发生一种情况,就是alarm的时间短,而在这个短时间内,pause还没开始执行,alarm就执行完毕,并产生了SIGALRM信号,此时pause尚未执行,于是信号被丢失,这样以来,接下来的pause会把进程永久性挂起(没有其他信号被接受的话)

 

代码二为什么就能解决竞争?

巧妙的利用了setjmp 和longjmp

       if(setjmp(env_alrm) == 0)

       {

                alarm(nsecs);

                pause();

       }

          这段代码简直是漂亮。Setjmp 没有被longjmp直接返回的情况下是返回0的,于是进入if语句,这是开启alarm

还是上面那样分析


          如果时间很长,自然不用说,alarm时间长,于是接下来执行pause挂起进程,然后alarm时间到了之后,触发SIGALRM,这个时候signal handler里的longjmp就会当前被执行过的指令的下一个指令,程序正常继续执行。


           如果时间很短,alarm立刻触发SIGALRM然后signal handler处理,longjmp直接跳回当前被执行过的指令的下一个指令(就是if语句后面)这个时候就没有pause了!!!


Even if the pause is never executed, thesleep2 function returns when the SIGALRM occurs.


熬了一两个小时。。。终于明白了。。。

 

 设置block的时间上限,防止slow system call 引起的超长时间的block。


#include <apue.h>
#include <myerr.h>
#include <setjmp.h>
 
static void sig_alrm(int);
 
static jmp_buf env_alrm;
 
int main()
{
        int n;
        char line[MAXLINE];


        if(signal(SIGALRM,sig_alrm) == SIG_ERR)
        {
                err_sys("signal (SIGALRM) error\n");
        }


        if(setjmp(env_alrm) != 0)
        {
                err_sys("read timeout\n");
        }


        printf("hi! input something no more than 10 secons\n");
        alarm(10);
        if((n = read(STDIN_FILENO,line,MAXLINE)) < 0)
        {
                err_sys("read error\n");
        }
        alarm(0);


        write(STDOUT_FILENO,line,n);


        return 0;
}
static void
sig_alrm(int signo)
{
       longjmp(env_alrm,1);
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
hi! input something no more than 10 secons
hello world
hello world


Signal Sets

          We  need a data type to represent multiple signals —a signal set .We’ll use this data type with such functions as sigprocmask (in the next section) to tell the kernel not to allow any of the signals in the set to occur



#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);
int sigdelset(sigset_t *set,int signo);

All four return: 0 if OK,−1 on error

int sigismember(const sigset_t *set,int signo);
Returns: 1 if true, 0 if false, −1 on error


linux里面宏定义实现的。。。

/* Clear all signals from SET.  */
extern int sigemptyset (sigset_t *__set) __THROW __nonnull ((1));


/* Set all signals in SET.  */
extern int sigfillset (sigset_t *__set) __THROW __nonnull ((1));


/* Add SIGNO to SET.  */
extern int sigaddset (sigset_t *__set, int __signo) __THROW __nonnull ((1));


/* Remove SIGNO from SET.  */
extern int sigdelset (sigset_t *__set, int __signo) __THROW __nonnull ((1));


/* Return 1 if SIGNO is in SET, 0 if not.  */
extern int sigismember (const sigset_t *__set, int __signo)
     __THROW __nonnull ((1));

然后这里的__set  和sigset_t 是个什么东西呢?上面只是declaration而已,我们找定义去


/* A `sigset_t' has a bit for each signal.  */


# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
  {
    unsigned long int __val[_SIGSET_NWORDS];
  } __sigset_t;


#endif

# if defined __GNUC__ && __GNUC__ >= 2
#  define __sigemptyset(set) \
  (__extension__ ({ int __cnt = _SIGSET_NWORDS;                      
                    sigset_t *__set = (set);                         
                    while (--__cnt >= 0) __set->__val[__cnt] = 0;    
                    0; }))
#  define __sigfillset(set) \
  (__extension__ ({ int __cnt = _SIGSET_NWORDS;                               \
                    sigset_t *__set = (set);                                  \
                    while (--__cnt >= 0) __set->__val[__cnt] = ~0UL;          \
                    0; }))


我实在没搞懂那个

__THROW __nonnull ((1));

什么意思。。。


#include <apue.h>
#include <signal.h>


int main()
{
        sigset_t hehe;


        printf("return value of sigemptyset :%x\n",sigemptyset(&hehe));


        printf("return value of sigefillset :%x\n",sigfillset(&hehe));


        printf("now we insert the (SIGINT)\n");


        printf("return value of sigeaddset :%d\n",sigaddset(&hehe,SIGCLD));
        return 0;
}
正确执行返回值是0,于是这里返回值都是0


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
return value of sigemptyset :0
return value of sigefillset :0
now we insert the (SIGINT)
return value of sigeaddset :0



sigprocmask Function


#include <signal.h>
int sigprocmask(int how ,const sigset_t *restrict set,sigset_t *restrict oset );
Returns: 0 if OK,−1 on error
       if oset is  a  non-null  pointer ,t he  current  signal  mask  for  the  process  is  returned through oset . Second,  if set is  a  non-null  pointer ,the how argument  indicates  how  the  current signal  mask  is  modified.

       if set is  a  non-null  pointer ,the how argument  indicates  how  the  current signal  mask  is  modified.

how 的宏定义在/usr/include/x86**/bits/sigaction.h里面。。。。


/* Values for the HOW argument to `sigprocmask'.  */
#define SIG_BLOCK     0          /* Block signals.  */
#define SIG_UNBLOCK   1          /* Unblock signals.  */
#define SIG_SETMASK   2          /* Set the set of blocked signals.  */


If set is  a  null  pointer ,the  signal  mask  of  the  process  is  not  changed,  and how is ignored.



sigpending Function


      The sigpending function returns the set of signals that are blocked from delivery and currently pending for the calling process.  The set of signals is returned through the set argument.

#include <signal.h>
int sigpending(sigset_t *set);
Returns: 0 if OK,−1 on error




sigaction Function


以后所有的demo都不再使用signal,instead of sigaction!

伤心伤透了。。。T-T

#include <signal.h>
int sigaction(int signo,const struct sigaction *restrict act,struct sigaction *restrict oact );
Returns: 0 if OK,−1 on error

sigaction 结构体在linux中的定义:

struct sigaction
  {
    /* Signal handler.  */
#ifdef __USE_POSIX199309
    union
      {
        /* Used if SA_SIGINFO is not set.  */
        __sighandler_t sa_handler;
        /* Used if SA_SIGINFO is set.  */
        void (*sa_sigaction) (int, siginfo_t *, void *);
      }
    __sigaction_handler;
# define sa_handler     __sigaction_handler.sa_handler
# define sa_sigaction   __sigaction_handler.sa_sigaction
#else
    __sighandler_t sa_handler;
#endif


    /* Additional set of signals to be blocked.  */
    __sigset_t sa_mask;


    /* Special flags.  */
    int sa_flags;


    /* Restore handler.  */
    void (*sa_restorer) (void);
  };




        /* Used if SA_SIGINFO is set.  */
        void (*sa_sigaction) (int, siginfo_t *, void *);

上面的定义中可以看到,linux是选择性的使用siginfo的

结构体定义如下:

typedef struct
  {
    int si_signo;               /* Signal number.  */
    int si_errno;               /* If non-zero, an errno value associated with
                                   this signal, as defined in <errno.h>.  */
    int si_code;                /* Signal code.  */


    union
      {
        int _pad[__SI_PAD_SIZE];


         /* kill().  */
        struct
          {
            __pid_t si_pid;     /* Sending process ID.  */
            __uid_t si_uid;     /* Real user ID of sending process.  */
          } _kill;


        /* POSIX.1b timers.  */
        struct
          {
            int si_tid;         /* Timer ID.  */
            int si_overrun;     /* Overrun count.  */
            sigval_t si_sigval; /* Signal value.  */
          } _timer;


        /* POSIX.1b signals.  */
        struct
          {
            __pid_t si_pid;     /* Sending process ID.  */
            __uid_t si_uid;     /* Real user ID of sending process.  */
            sigval_t si_sigval; /* Signal value.  */
          } _rt;
        /* SIGCHLD.  */
        struct
          {
            __pid_t si_pid;     /* Which child.  */
            __uid_t si_uid;     /* Real user ID of sending process.  */
            int si_status;      /* Exit value or signal.  */
            __sigchld_clock_t si_utime;
            __sigchld_clock_t si_stime;
          } _sigchld;


        /* SIGILL, SIGFPE, SIGSEGV, SIGBUS.  */
        struct
          {
            void *si_addr;      /* Faulting insn/memory ref.  */
          } _sigfault;


        /* SIGPOLL.  */
        struct
          {
            long int si_band;   /* Band event for SIGPOLL.  */
            int si_fd;
          } _sigpoll;


        /* SIGSYS.  */
        struct
          {
            void *_call_addr;   /* Calling user insn.  */
            int _syscall;       /* Triggering system call number.  */
            unsigned int _arch; /* AUDIT_ARCH_* of syscall.  */
          } _sigsys;
      } _sifields;
  } siginfo_t __SI_ALIGNMENT;


/* X/Open requires some more fields with fixed names.  */
# define si_pid         _sifields._kill.si_pid
# define si_uid         _sifields._kill.si_uid
# define si_timerid     _sifields._timer.si_tid
# define si_overrun     _sifields._timer.si_overrun
# define si_status      _sifields._sigchld.si_status
# define si_utime       _sifields._sigchld.si_utime
# define si_stime       _sifields._sigchld.si_stime
# define si_value       _sifields._rt.si_sigval
# define si_int         _sifields._rt.si_sigval.sival_int
# define si_ptr         _sifields._rt.si_sigval.sival_ptr
# define si_addr        _sifields._sigfault.si_addr
# define si_band        _sifields._sigpoll.si_band
# define si_fd          _sifields._sigpoll.si_fd
# define si_call_addr   _sifields._sigsys._call_addr
# define si_syscall     _sifields._sigsys._syscall
# define si_arch        _sifields._sigsys._arch






/*********************************************************************************
code writer :EOF
code date : 2014.04.01
e-mail:jasonleaster@gmail.com


code purpose :
        I would like to share my code with people who is interesting
in APUE. Sharing make our improve together. If you find there is 
something wrong with my code, please touch me by e-mail. Thank you.


*********************************************************************************/
#include <stdio.h>
#include <signal.h>


void signal_handler(int signo);


int main()
{
        struct sigaction act;
        
        act.sa_handler = signal_handler;
        
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;//don't use flags
        
        if(sigaction(SIGUSR1,&act,NULL) < 0)
        {
                printf("sigaction error\n");
        }
        
        kill(getpid(),SIGUSR1); 
        sleep(2);//avoid to exit rapaidly so that the signal handler didn't finish
        return 0;
}


void signal_handler(int signo)
{
        printf("signal received\n");
}







sigsetjmp and siglongjmp Functions


            To  allow either form of behavior , POSIX.1 does not specify the effect of setjmp and longjmp on signal masks. Instead, two new functions, sigsetjmp and siglongjmp, are defined  by  POSIX.1. These  two  functions  should  always  be  used  when  branching from a signal handler.



#include <setjmp.h>
int sigsetjmp(sigjmp_buf env ,int savemask );
Returns: 0 if called directly ,nonzero if returning from a call to siglongjmp

void siglongjmp(sigjmp_buf env ,int val);




            The only difference between these functions and the setjmp and longjmp functions is that sigsetjmp has an additional argument.  If save mask is nonzero, then sigsetjmp also saves the current signal mask of the process in env .When siglongjmp is called, if the env argument  was  saved  by  a  call  to sigsetjmp with  a  nonzero save mask ,then siglongjmp restores the saved signal mask.


/*********************************************************************************
code writer : EOF
code date : 2014.04.01
e-mail: jasonleaster@gmail.com


code purpose :
        just a demo for siglongjmp and sigsetjmp
        
        I would like to share my code with someone. Sharing make our imporve 
together. Just share your code with us. Open source make our world better and
 better. If there are something wrong with my code, please touch me by e-mail.
Thank you. I am glad to receive your feedback.


*********************************************************************************/
#include <setjmp.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <myerr.h>


static void             sig_usr1(int),sig_alrm(int);
static sigjmp_buf               jmpbuf;
static volatile sig_atomic_t    canjump;

void  pr_mask(const char* str);


int main()
{
        struct sigaction act_usr,act_alrm;
        act_usr.sa_flags  = 0;
        act_alrm.sa_flags = 0;//you may initialize the sa_flags. There are some bit operation on these varibles.


        act_usr.sa_handler = sig_usr1;
        sigemptyset(&act_usr.sa_mask);
        act_usr.sa_flags = 0;
        if(sigaction(SIGUSR1,&act_usr,NULL) <0)
        {
                printf("sigaction error\n");
        }


        act_alrm.sa_handler = sig_alrm;
        sigemptyset(&act_alrm.sa_mask);
        act_alrm.sa_flags |= SA_INTERRUPT;


        if(sigaction(SIGALRM,&act_alrm,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        pr_mask("starting main:\n");


        if(sigsetjmp(jmpbuf,1))
        //reset the signal mask if the second parameter of sigsetjmp is non-zero.
        //If the second parameter is zero,the maskwould be leave.
        {
                pr_mask("ending main\n");
                exit(0);
        }


        canjump = 1;//now setjmp is OK


        for( ; ;)
        {
                pause();
        }


}

static void
sig_usr1(int signo)
{
        time_t  starttime;


        if(canjump == 0)
        {
                return;
        }


        pr_mask("starting usr1:\n");


        alarm(3);
        starttime = time(NULL);


        for( ; ;)
        {
                if(time(NULL) > starttime +5)
                {
                        break;
                }
        }


        pr_mask("finishing sig_usr1:\n");


        canjump = 0;


        siglongjmp(jmpbuf,1);
}


static void
sig_alrm(int signo)
{
        pr_mask("in sig_alrm\n");
}
void
pr_mask(const char* str)
{
        sigset_t sigset;
        int errno_save;


        errno_save = errno;


        if(sigprocmask(0,NULL,&sigset) < 0)
        {
                err_sys("sigprocmask error\n");
        }


        printf("%s",str);


        if(sigismember(&sigset,SIGINT))
        {
                printf("SIGINT\n");
        }


        if(sigismember(&sigset,SIGQUIT))
        {
                printf("SIGQUIT\n");
        }


        if(sigismember(&sigset,SIGUSR1))
        {
                printf("SIGUSR1\n");
        }


        if(sigismember(&sigset,SIGALRM))
        {
                printf("SIGALRM\n");
        }


        errno = errno_save;
}






jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out &
[1] 5357
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ starting main:
kill -USR1 5357
starting usr1:
SIGUSR1
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ in sig_alrm
SIGUSR1
SIGALRM
finishing sig_usr1:
SIGUSR1
ending main


[1]+  Done                    ./a.out


值得注意的是,SIGALRM的mask会被自动的清零,当alarm到达预定时间之后。

如果sigsetjmp 的第二个参数是0,那么最后的mask里面会有SIGUSR1




sigsuspend Function


#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
Returns: −1with errnoset to EINTR


        The  signal  mask  of  the  process  is  set  to  the  value  pointed  to  by sigmask.Then  the process is suspended until a signal is caught or until a signal occurs that terminates the process.  If asignal  is  caught  and  if  the  signal handler  returns,  then sigsuspend returns,  and  the  signal  mask  of  the  process  is  set  to  its  value  before the call  to sigsuspend



demo:

#include <setjmp.h>
#include <myerr.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include </Ad_Pro_in_Unix/chapter_10/pro_10_14.c>
//pro_10_14.c is the implementation of pr_mask. I have show the definition of  pr_mask in this blog.


static void sig_int(int signo);


int main()
{
        sigset_t newmask,oldmask,waitmask;
        struct sigaction act_int,oact_int;


        sigemptyset(&act_int.sa_mask);


        sigemptyset(&newmask);
        sigemptyset(&oldmask);
        sigemptyset(&waitmask);//initialize the signal mask.


        act_int.sa_handler = sig_int;
        act_int.sa_flags = 0;//I don't use flags. You know, it is bits operation and nothing would be set if the flag is zero.


        pr_mask("program start: \n");


        if(sigaction(SIGINT,&act_int,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        sigemptyset(&waitmask);
        sigaddset(&waitmask,SIGUSR1);
        sigemptyset(&newmask);
        sigaddset(&newmask,SIGINT);


        if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0)//add the newmask's bits into oldmask
        {
                printf("sigprocmask error\n");
        }


        pr_mask("in critical region\n");


        if(sigsuspend(&waitmask) != -1)// suspend the signal in waitmask.
        {
                err_sys("sigprocmask error\n");
        }


        pr_mask("after return from sigsuspend\n");


        if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0)
        {
                printf("sigprocmask error\n");
        }


        pr_mask("program exit\n");


        return 0;
}


static void sig_int(int signo)
{
        pr_mask("\nin sig_int\n");
}


test result:

liuzjian@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
program start: 
in critical region
SIGINT
^C
in sig_int
SIGINT
SIGUSR1
after return from sigsuspend
SIGINT
program exit


another demo:

#include <apue.h>


volatile sig_atomic_t quitflag = 0;


static void sig_int(int signo)
{
        if(signo == SIGINT)
        {
                printf("\ninterrupt\n");
        }
        else if(signo == SIGQUIT)
        {
                quitflag = 1;
        }
}


int main()
{
        sigset_t newmask,oldmask,zeromask;


        struct sigaction act_int,act_qt;
        sigemptyset(&act_int.sa_mask);
        sigemptyset(&act_qt.sa_mask);
        act_int.sa_flags = 0;
        act_qt.sa_flags  = 0;


        sigemptyset(&newmask);
        sigemptyset(&oldmask);
        sigemptyset(&zeromask);


        act_int.sa_handler = sig_int;
        act_qt.sa_handler  = sig_int;


        if(sigaction(SIGINT,&act_int,NULL) < 0)
        {
                printf("sigaction error\n");
        }

        if(sigaction(SIGQUIT,&act_qt,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        sigemptyset(&act_int.sa_mask);
        sigemptyset(&act_qt.sa_mask);
        sigaddset(&act_int.sa_mask,SIGINT);
        sigaddset(&act_qt.sa_mask,SIGQUIT);


        if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0)
        {
                printf("SIG_BLOCK error\n");
        }


        while(quitflag == 0)
        {
                sigsuspend(&zeromask);
        }


        quitflag = 0;


        if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0)
        {
                printf("sigprocmask error\n");
        }


        exit(0);
}


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
^C
interrupt
^C
interrupt
^C
interrupt
^C
interrupt
^\


abort Function

#include <stdlib.h>
void abort(void);
This function never returns


          If  the  process  doesn’t  terminate  itself  from  this  signal  handler, POSIX.1  states  that,  when  the  signal  handler returns, abort terminates the process.


           The  ISO  C  specification  of  this  function  leaves  it  up  to  the  implementation  as  to whether  output  streams are flushed  and  whether  temporary  files  (Section  5.13)  are deleted.  POSIX.1 goes  further  and  allows  an  implementation  to  call fclose on  open standard I/O streams before terminating if the call to abort terminates the process.


对于这种不同标准要求不同,我只能说无奈。。。

这个POXIS标准的实现我木有看懂,热心的viewer知道的话,希望能帮忙讨论

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


void abort(void)
{
        sigset_t mask;
        struct sigaction action;


        sigaction(SIGABRT,NULL,&action);
        if(action.sa_handler == SIG_IGN)
        {
                action_sa_handler = SIG_DFL;
                sigaction(SIGABRT,&action,NULL);
        }


        if(action.sa_handler == SIG_DFL)
        {
                fflush(NULL);
        }


        sigfillset(&mask);
        sigdelset(&mask,SIGABRT);


        sigprocmask(SIG_SETMASK,&mask,NULL);


        kill(getpid(),SIGABRT);


        fflush(NULL);


        action.sa_handler = SIG_DFL;


        sigaction(SIGABRT,&action,NULL);


        sigprocmask(SIG_SETMASK,&mask,NULL);
        kill(getpid(),SIGABRT);
        exit(1);
}







system  Function


#include <apue.h>


static void sig_int(int signo)
{
        printf("caught SIGINT\n");
}


static void sig_child(int signo)
{
        printf("caught SIGCHILD\n");
}


int main()
{
        struct sigaction act_int,act_child;


        act_int.sa_flags = 0;
        act_child.sa_flags = 0;


        sigemptyset(&act_int.sa_mask);
        sigemptyset(&act_child.sa_mask);


        act_int.sa_handler = sig_int;
        act_child.sa_handler = sig_child;


        if(sigaction(SIGINT,&act_int,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        if(sigaction(SIGCHLD,&act_child,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        if(system("/bin/ed") < 0)
        {
                printf("system() error\n");
        }
        exit(0);
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
a
hello world
.
1,$p
hello world
w temp.foo
12
q
caught SIGCHILD


对于ed的操作我也不熟悉。。。所以。。。如果这个demo看不懂就看APUE书上的解释吧。。。









system  Function


#include <apue.h>


static void sig_int(int signo)
{
        printf("caught SIGINT\n");
}


static void sig_child(int signo)
{
        printf("caught SIGCHILD\n");
}


int main()
{
        struct sigaction act_int,act_child;


        act_int.sa_flags = 0;
        act_child.sa_flags = 0;


        sigemptyset(&act_int.sa_mask);
        sigemptyset(&act_child.sa_mask);


        act_int.sa_handler = sig_int;
        act_child.sa_handler = sig_child;


        if(sigaction(SIGINT,&act_int,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        if(sigaction(SIGCHLD,&act_child,NULL) < 0)
        {
                printf("sigaction error\n");
        }


        if(system("/bin/ed") < 0)
        {
                printf("system() error\n");
        }
        exit(0);
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
a
hello world
.
1,$p
hello world
w temp.foo
12
q
caught SIGCHILD


对于ed的操作我也不熟悉。。。所以。。。如果这个demo看不懂就看APUE书上的解释吧。。。



        关于abort function sleep function和system functon很多实现都是用信号实现的,例如sleep 函数实现的时候就用到了alarm 和SIGARLM,但是我觉得我写程序还是不会涉及到具体这些函数是怎么实现的,所以就没有继续死磕实现的代码了。



Signal Names  and  Numbers


#include <signal.h>
void psignal(int signo,const char *msg);



         The  string msg (which  normally  includes  the  name  of  the  program)  is  output  to  the standard error, followed by a colon and a space, followed by a description of the signal, followed  by  a  newline.

我一开始还纠结怎么写demo。。。
#include <stdio.h>
#include <signal.h>

int main()
{
        psignal(SIGCHLD,"hello world");
        return 0;
}


就是个提示信息的东西。。。。
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_10$ ./a.out
hello world: Child exited




#include <signal.h>
void psiginfo(const siginfo_t *info ,const char *msg);


这个涉及了前面讲的siginfo结构体。。。有时间在update demo 吧

#include <signal.h>
int sig2str(int signo,char *str);
int str2sig(const char *str,int *signop );
Both return: 0 if OK, −1 on error


linux 下面没这东西。。。

So 这个章节。。。终于告一段落了。。。


三天 signal










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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值