Unix环境高级编程(十)信号续

1、signal函数

  Unix系统的信号机制最简单的接口是signal函数,函数原型如下:

  #include <signal.h>
  typedef void (*sighandler_t)(int);
  sighandler_t signal(int signum, sighandler_t handler);

signum表示信号名称,handler取值常量SIG_IGN(忽略此信号)、常量SIG_DFL(执行默认动作)或者接到此信号后要调用的函数的地址(调用信号处理程序)。

写个程序练习一下signal函数的使用,程序的功能是捕获子进程退出。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/wait.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <errno.h>
 7 #include <signal.h>
 8 //信号处理程序
 9 void sig_child(int signo)
10 {
11     int status;
12     if(waitpid(-1,&status,0) != -1)
13         printf("child process exit.status= %d\n",status);
14     else
15     {
16         perror("waitpid() error");
17         exit(-1);
18     }
19 }
20
21 int main()
22 {
23     pid_t  pid;
24     //创建子进程退出信号
25     signal(SIGCHLD,sig_child);
26     if((pid=fork()) == -1)
27         perror("fork() error");
28     else if(pid == 0)
29     {
30         printf("I am child process.\n");
31         exit(0);
32     }
33     else
34     {
35         sleep(3);  //让子进程先执行
36         printf("I am parent process.\n");
37     }
38     exit(0);
39 }

程序执行结果如下:

2、中断的系统调用

  如果进程在执行一个低速系统调用而阻塞期间捕获到一个信号,则该系统调用就被中断不再继续执行。系统调用返回出错,将errno的值设置为EINTR。当一个信号发生时,进程捕捉到,意味着已经发生了某种事情,可以唤醒阻塞系统调用进行相关操作。低速系统调用是可能使进程永远阻塞的一类系统调用。与被中断的系统调用相关的问题是必须显示的处理出错返回。例如在进行多操作时候,多的过程被中断了,中断结束后希望重新启动读操作。代码如下:

1 again:
2     if((n=read(fd,buf,BUFSIZE))<0)
3    {
4        if(errno == EINTR)
5           goto again;
6        /*handle other errnors*/
7     }

自动重新启动的系统调用包括:ioctl、read、readv、write、writew、wait和waitpid。
3、可重入函数

  进程捕捉到信号并对其进行处理,进程正在执行的指令序列就被信号处理程序临时中断,首先执行该信号处理程序中的指令,如果从信号处理程序返回,则继续在捕捉到信号时进程正在执行的正常指令中返回。在信号处理程序中,不能判断捕捉到信号时进程在何处执行,这样不能保证在中断处理结束后能够正确返回到进程的执行指令中。为了保证进程在处理完中断后能够正确返回,需要保证调用的是可重入的函数。不可重入函数包括:(1)使用静态数据结构,(2)调用malloc或free,(3)标准I/O函数。信号处理程序中调用一个不可重入的函数,则结果是不可预测的。例如getpwnam函数是个不可重入的,因为其结果存放在静态存储单元中,信号处理程序调用后,返回给正常调用者的信息可能是被返回给信号处理程序的信息覆盖。程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <pwd.h>
#include <string.h>

static void my_alarm(int signo)
{
    struct passwd *rootptr;
    printf("in signal handler.\n");
    if ((rootptr = getpwnam("root")) == NULL)
    {
        perror("getpwnam() error");
        exit(-1);
    }
    printf("return value:,pw_name = %s\n",rootptr->pw_name);
    alarm(1);
}

int main()
{

    struct passwd  *ptr;
    signal(SIGALRM,my_alarm);
    alarm(1);
    while(1)
    {
        if((ptr = getpwnam("anker")) == NULL)
        {
             perror("getpwnam() error");
            exit(-1);
        }
        if(strcmp(ptr->pw_name,"anker") != 0)
            printf("return value corrupted!,pw_name = %s\n",ptr->pw_name);
        else
            printf("return value not corrupted!,pw_name = %s\n",ptr->pw_name);

    }
}

编译执行程序,没有发现出现什么异常,此处有些不明白,日后过来想想。
4、kill和raise函数

  kill函数将信号发送给进程或者进程组,raise函数则允许进程向自身发送信号。进程将信号发送给其他进程需要权限,超级用户可以将信号发送给任一进程。调用kill产生的信号时不被阻塞的。函数原型如下:

  #include <sys/types.h>
  #include <signal.h>
  int kill(pid_t pid, int sig);
  int raise(int sig);    //等价于kill(getpid(),signo)

5、alarm和pause函数

  使用alarm函数设置计时器,在将来某个指定时间该计时器会超时,产生SIGALRM信号,默认动作时终止调用alarm函数的进程,每个进程只能有一个闹钟时钟,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。当在调用alarm()前已经设置了一个闹钟,那么我们可以调用alarm(0)来取消此闹钟,并返回剩余时间。puase函数使调用进程挂起直到捕捉到一个信号。只有执行了一个信号处理程序并从其返回时,pause才返回-1,并将errno设置为EINTR。函数原型如下:

  #include <unistd.h>
  unsigned int alarm(unsigned int seconds);  //返回0或者以前设置的闹钟时间的余留秒数
  int pause(void);

写个程序练习以上函数,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/wait.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <errno.h>
 7 #include <signal.h>
 8 static void sig_kill(int signo)
 9 {
10     printf("Received from kill().\n");
11 }
12 static void sig_alarm(int signo)
13 {
14     printf("Receiver form aralm().\n");
15 }
16 int main()
17 {
18
19     signal(SIGHUP,sig_kill);
20     signal(SIGALRM,sig_alarm);
21
22     printf("kill() is called.\n");
23     kill(getpid(),SIGHUP);
24     printf("alarm() is called.\n");
25     alarm(3);
26     printf("pause() is called.\n");
27     pause();
28     printf("raise() is called.\n");
29     raise(SIGHUP);
30     exit(0);
31 }

程序执行结果如下:

alarm只设定一个闹钟,时间到达并执行其注册函数之后,闹钟便失效。如果想循环设置闹钟,需在其注册函数中在调用alarm函数。写个程序可以设置一个循环闹钟,使得程序每个多少秒执行以下,执行完成之后开始计时,时间都接着执行,循环指定主程序结束。程序如下:

 1 #include <unistd.h>
 2 #include <signal.h>
 3 #include <stdio.h>
 4 void sig_alarm(int signo)
 5 {
 6     printf("Alarming.\n");
 7     signal(SIGALRM, sig_alarm); //让内核做好准备,一旦接受到SIGALARM信号,就执行sig_alarm
 8     alarm(5);
 9 }
10 void main()
11 {
12     int i;
13     signal(SIGALRM, sig_alarm);//让内核做好准备,一旦接受到SIGALARM信号,就执行sig_alarm
14     alarm(5);
15     for(i=1;i<21;i++)
16     {
17         printf("sleep %d ...\n",i);
18         sleep(1);
19     }
20 }

程序执行结果如下:

6、信号集

  通过信号集(signal set)表示多个信号,这样方便操作多个信号。信号集的数据类型为sigset_t,信号集操作函数如下: 

  #include <signal.h>
  int sigemptyset(sigset_t *set);  //初始化由set指向的信号集,清除其中所有信号
  int sigfillset(sigset_t *set);    //初始化由set指向的信号集,使其包括所有信号
  int sigaddset(sigset_t *set, int signum);   //添加一个指定的信号
  int sigdelset(sigset_t *set, int signum);   //删除一个指定信号
  int sigismember(const sigset_t *set, int signum);   //判断signum是否在set所指向的信号集中

  一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集,调用sigprocmask函数可以检测或更改其信号屏蔽字。函数原型如下:

  int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

  how的取值为:SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK

  sigpending函数返回信号集,其中的各个信号对于调用进程是阻塞的而不能传递。函数原型为:int sigpending(sigset_t *set)。

写个程序进行信号屏蔽测试,程序如下:

 1 #include <sys/wait.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <errno.h>
 5 #include <signal.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8
 9 static void sig_quit(int signo);
10
11 int main()
12 {
13     sigset_t    newmask,oldmask,pendmask;
14     if(signal(SIGQUIT,sig_quit) == SIG_ERR)
15     {
16         perror("signal() error");
17         exit(-1);
18     }
19     sigemptyset(&newmask);
20     //添加一个退出信号
21     sigaddset(&newmask,SIGQUIT);
22     //将newmask信号机设置为阻塞,原信号集保存在oldmask中
23     if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) == -1)
24     {
25         perror("sigprocmask() error");
26         exit(-1);
27     }
28     sleep(5);
29     //获取阻塞的信号集
30     if(sigpending(&pendmask) == -1)
31     {
32         perror("sigpending() error");
33         exit(-1);
34     }
35     //判断SIGQUIT是否是阻塞的
36     if(sigismember(&pendmask,SIGQUIT))
37         printf("\nSIGQUIT is pending.\n");
38     //回复原理的信号集
39     if(sigprocmask(SIG_SETMASK,&oldmask,NULL) == -1)
40     {
41         perror("sigprocmask() error");
42         exit(-1);
43     }
44     printf("SITQUIT unblocked\n");
45     sleep(5);
46     exit(0);
47 }
48
49 static void sig_quit(int signo)
50 {
51     printf("caught SIGQUIT.\n");
52     if(signal(SIGQUIT,SIG_DFL) == SIG_ERR)
53     {
54         perror("signal() error");
55         exit(-1);
56     }
57 }

程序执行结果如下:在中断上键入Ctlr+\退出字符。

第二次运行时候,在进程休眠的时候产生多次SIGQUIT信号,但是解除了该信号的阻塞后,只会向进程发送一个SIGQUIT,系统没有对信号进行排队。

  接着昨天学习Unix信号机制,信号内容挺多了,发了两天的时间才了解各大概,日后遇到问题需要多看几遍,掌握核心应用。

7、sigaction函数

sigaction函数的功能是检查或修改与指定信号相关联的处理动作或同时执行这两种操作,可以用sigaction函数实现signal函数。函数原型及结构参数如下:

  int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

  struct sigaction {
      void     (*sa_handler)(int);
      void     (*sa_sigaction)(int, siginfo_t *, void *);
      sigset_t   sa_mask;
      int        sa_flags;
      void     (*sa_restorer)(void);
  };

现用sigaction函数实现signal函数,被信号中断的系统调用都能够重启。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <signal.h>
 6
 7 typedef void Sigfunc(int);
 8
 9 Sigfunc* mysignal(int signo,Sigfunc *func)
10 {
11     struct sigaction    act,oact;
12     act.sa_handler = func;  //设置中断处理程序
13     sigemptyset(&act.sa_mask);  //初始化信号集
14     act.sa_flags = 0;
15     if(signo == SIGALRM)   //将SIGALRM信号设置为系统调用不会自动重启
16     {
17     #ifdef SA_INTERRUPT
18         act.sa_flags |= SA_INTERRUPT;
19     #endif
20     }
21     else   //其余信号设置为系统调用自动重启
22     {
23     #ifdef  SA_RESTART
24         act.sa_flags |= SA_RESTART;
25     #endif
26     }
27     if(sigaction(signo,&act,&oact)<0)
28         return (SIG_ERR);
29     return (oact.sa_handler);
30 }
31
32 static void sig_func(int signo)
33 {
34     printf("Recevied a SIGALRM signal.\n");
35 }
36
37 int main()
38 {
39     printf("Starting.\n");
40     mysignal(SIGALRM,sig_func);
41     alarm(2);
42     pause();  //等待信号出现
43     exit(0);
44 }

程序执行结果如下:

8、sigsetjmp和siglongjmp函数

 这两个函数是对非局部转移的setjmp和longjmp函数的改进,在信号处理程序中进行非局部转移时应当使用这两个函数。函数原型如下:  

int sigsetjmp(sigjmp_buf env, int savesigs);
   void siglongjmp(sigjmp_buf env, int val);

在sigsetjmp函数中,如果savesigs非0,则sigsetjmp在env中保存进程的当前信号屏蔽字,调用siglongjmp从其中恢复保存的信号屏蔽字。

写个程序练习一下,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <errno.h>
 6 #include <signal.h>
 7
 8 static void printcharacter(const char* str);
 9 static void sig_usr(int signo);
10 void TELL_WAIT(void);
11 void TELL_PARENT(pid_t pid);
12 void TELL_CHILD(pid_t pid);
13 void WAIT_PARENT(void);
14 void WAIT_CHILD(void);
15 static volatile sig_atomic_t sigflag;
16 static sigset_t newmask,oldmask,zeromask;
17
18 int main()
19 {
20     pid_t   pid;
21     TELL_WAIT();
22     pid = fork();
23     switch(pid)
24     {
25     case -1:
26         perror("fork() error");
27         exit(-1);
28     case 0:   //让子进程优先执行
29         //WAIT_PARENT();
30         printcharacter("output from child prcess.\n");
31         TELL_PARENT(getppid());
32         break;
33     default:
34         WAIT_CHILD();
35         printcharacter("output from parent prcess.\n");
36         //TELL_CHILD(pid);
37     }
38     exit(0);
39 }
40
41 static void printcharacter(const char* str)
42 {
43     const char *ptr;
44     setbuf(stdout,NULL);
45     for(ptr=str;*ptr!=‘\0‘;ptr++)
46         putc(*ptr,stdout);
47 }
48 static void sig_usr(int signo)  //信号处理程序
49 {
50     sigflag = 1;
51 }
52 void TELL_WAIT(void)
53 {
54     signal(SIGUSR1,sig_usr);
55     signal(SIGUSR2,sig_usr);
56     sigemptyset(&zeromask);
57     sigemptyset(&newmask);
58     //添加信号集
59     sigaddset(&newmask,SIGUSR1);
60     sigaddset(&newmask,SIGUSR2);
61     sigprocmask(SIG_BLOCK,&newmask,&oldmask);  //设置信号为阻塞
62 }
63 void TELL_PARENT(pid_t pid)
64 {
65     kill(pid,SIGUSR2); //向子进程发送信号
66 }
67 void TELL_CHILD(pid_t pid)
68 {
69     kill(pid,SIGUSR1);//向父进程发送信号
70 }
71 void WAIT_PARENT(void)
72 {
73     while(sigflag == 0)
74       sigsuspend(&zeromask);
75     sigflag = 0;
76     sigprocmask(SIG_SETMASK,&oldmask,NULL);
77 }
78 void WAIT_CHILD(void)
79 {
80     while(sigflag == 0)
81       sigsuspend(&zeromask); //阻塞进程
82     sigflag = 0;
83     sigprocmask(SIG_SETMASK,&oldmask,NULL);
84 }

程序执行结果如下:

当调用一个信号处理程序时,被捕捉到的信号添加到进程的当前信号屏蔽字中,当从信号处理程序返回时,恢复原来的屏蔽字,siglongjmp恢复了有sigsetjmp保存的信号屏蔽字。

9、sigsuspend函数

  该函数在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。函数原型如下:

  int sigsuspend(const sigset_t *mask); //返回-1,并将errno设置为EINTR

  将信号屏蔽字设置为mask指向的值,在捕捉到一个信号或发生一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号程序返回,则sigsuspend返回,并且该进程的信号屏蔽字设置为调用sigsuspend之前的值。该函数可以保护不希望由信号中断的代码临界区,写个程序,使用该函数保护临界区,使其不被特定信号中断,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <signal.h>
 6 #include <errno.h>
 7 #include <setjmp.h>
 8 void pr_mask(const char *str);
 9 static void sig_int(int);
10
11 int main()
12 {
13     sigset_t    newmask,oldmask,waitmask;
14     pr_mask("program start: ");
15     signal(SIGINT,sig_int);
16     sigemptyset(&waitmask);
17     sigaddset(&waitmask,SIGUSR1);
18     sigemptyset(&newmask);
19     sigaddset(&newmask,SIGINT);
20     sigprocmask(SIG_BLOCK,&newmask,&oldmask);
21     pr_mask("in critical region");
22     //修改进程屏蔽字,在捕捉信号之前,将进程挂起
23     sigsuspend(&waitmask);
24     pr_mask("after return form sigsuspend: ");
25     sigprocmask(SIG_SETMASK,&oldmask,NULL);
26     pr_mask("program exit: ");
27     exit(0);
28 }
29
30 void pr_mask(const char *str)
31 {
32     sigset_t sigset;
33     int errno_save;
34     errno_save = errno;
35     if(sigprocmask(0,NULL,&sigset)<0)
36     {
37         perror("sigprocmask() error");
38         exit(-1);
39     }
40     printf("%s\n",str);
41     if(sigismember(&sigset,SIGINT))
42         printf("SIGINT \n");
43     if(sigismember(&sigset,SIGQUIT))
44         printf("SIGQUIT \n");
45     if(sigismember(&sigset,SIGUSR1))
46         printf("SIGUSR1 \n");
47     if(sigismember(&sigset,SIGALRM))
48         printf("SIGALRM \n");
49     errno = errno_save;
50 }
51
52 static void sig_int(int signo)
53 {
54     pr_mask("\nin sig_int: ");
55 }

程序执行结果如下:

从结果可以看出在调用sigsuspend函数,将SIGUSR1信号添加到进程信号屏蔽字中,当从sissuspend返回时,信号屏蔽字恢复为调用它之前的值。

进程之间存在资源竞争,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <errno.h>
 6
 7 static void printcharacter(const char* str)
 8 {
 9     const char *ptr;
10     setbuf(stdout,NULL);
11     for(ptr=str;*ptr!=‘\0‘;ptr++)
12         putc(*ptr,stdout);
13 }
14
15 int main()
16 {
17     pid_t   pid;
18     pid = fork();
19     switch(pid)
20     {
21     case -1:
22         perror("fork() error");
23         exit(-1);
24     case 0:
25         printcharacter("output from child prcess.\n");
26         break;
27     default:
28         printcharacter("output from parent prcess.\n");
29     }
30     exit(0);
31 }

程序执行结果如下:

  从结果看出,父子进程之间存在资源竞争,导致输出结果是随机的。接下来采用信号机制实现父子进程之间的同步实现TELL_WAIT、TELL_PARENT、TELL_CHILD、  WAIT_PARENT和WAIT_CHILD。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <errno.h>
 6 #include <signal.h>
 7
 8 static void printcharacter(const char* str);
 9 static void sig_usr(int signo);
10 void TELL_WAIT(void);
11 void TELL_PARENT(pid_t pid);
12 void TELL_CHILD(pid_t pid);
13 void WAIT_PARENT(void);
14 void WAIT_CHILD(void);
15 static volatile sig_atomic_t sigflag;
16 static sigset_t newmask,oldmask,zeromask;
17
18 int main()
19 {
20     pid_t   pid;
21     TELL_WAIT();
22     pid = fork();
23     switch(pid)
24     {
25     case -1:
26         perror("fork() error");
27         exit(-1);
28     case 0:
29         //WAIT_PARENT();
30         printcharacter("output from child prcess.\n");
31         TELL_PARENT(getppid());
32         break;
33     default:
34         WAIT_CHILD();
35         printcharacter("output from parent prcess.\n");
36         //TELL_CHILD(pid);
37     }
38     exit(0);
39 }
40
41 static void printcharacter(const char* str)
42 {
43     const char *ptr;
44     setbuf(stdout,NULL);
45     for(ptr=str;*ptr!=‘\0‘;ptr++)
46         putc(*ptr,stdout);
47 }
48 static void sig_usr(int signo)
49 {
50     sigflag = 1;
51 }
52 void TELL_WAIT(void)
53 {
54     signal(SIGUSR1,sig_usr);  //设置信号
55     signal(SIGUSR2,sig_usr);
56     sigemptyset(&zeromask);
57     sigemptyset(&newmask);
58     sigaddset(&zeromask,SIGUSR1);
59     sigaddset(&newmask,SIGUSR2);
60     sigprocmask(SIG_BLOCK,&newmask,&oldmask);  //将信号设置为阻塞
61 }
62 void TELL_PARENT(pid_t pid)
63 {
64     kill(pid,SIGUSR2); //向子进程发生信号
65 }
66 void TELL_CHILD(pid_t pid)
67 {
68     kill(pid,SIGUSR1); //想父进程发送信号
69 }
70 void WAIT_PARENT(void)
71 {
72     while(sigflag == 0)
73       sigsuspend(&zeromask);//将进程挂起。等待信号处理程序返回
74     sigflag = 0;
75     sigprocmask(SIG_SETMASK,&oldmask,NULL);
76 }
77 void WAIT_CHILD(void)
78 {
79     while(sigflag == 0)
80       sigsuspend(&zeromask);  //将进程挂起。等待信号处理程序返回
81     sigflag = 0;
82     sigprocmask(SIG_SETMASK,&oldmask,NULL);
83 }

程序执行结果如下:

10、abort函数

  abort函数的功能是使异常终止,此函数将SIGABRT信号发送给调用进程,让进程捕捉SIGABRT信号目的是在进程终止之前由其执行所需的清理操作。默认情况是终止调用进程。可以采用sigaction和kill函数来实现abort,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <errno.h>
 6 #include <signal.h>
 7
 8 void myabort()
 9 {
10     sigset_t  mask;
11     struct sigaction action;
12     sigaction(SIGABRT,NULL,&action);
13     if(action.sa_handler == SIG_IGN)
14     {
15         action.sa_handler = SIG_DFL;
16         sigaction(SIGABRT,&action,NULL);
17     }
18     if(action.sa_handler == SIG_DFL)
19         fflush(NULL);
20     sigfillset(&mask);
21     sigdelset(&mask,SIGABRT);
22     sigprocmask(SIG_SETMASK,&mask,NULL);
23     kill(getpid(),SIGABRT);
24     fflush(NULL);
25     action.sa_handler = SIG_DFL;
26     sigaction(SIGABRT,&action,NULL);
27     sigprocmask(SIG_SETMASK,&mask,NULL);
28     kill(getpid(),SIGABRT);
29     exit(1);
30 }
31 static void sig_abort(int signo)
32 {
33     printf("abort signal.\n");
34 }
35
36 int main()
37 {
38     signal(SIGABRT,sig_abort);
39     myabort();
40     pause();
41     exit(0);
42 }

执行结果如下:

11、system函数

  POSIX.1要求system函数忽略SIGINT和SITQUIT信号,阻塞SIGCHLD。采用信号实现一个system函数,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <errno.h>
 6 #include <signal.h>
 7
 8 int mysystem(const char*cmdstring)
 9 {
10     pid_t   pid;
11     int     status;
12     struct  sigaction   ignore,saveintr,savequit;
13     sigset_t    chldmask,savemask;
14
15     if(cmdstring == NULL)
16         return 1;
17     ignore.sa_handler = SIG_IGN;
18     sigemptyset(&ignore.sa_mask);
19     ignore.sa_flags = 0;
20     if(sigaction(SIGINT,&ignore,&savequit)<0)
21     {
22         perror("sigaction() error");
23         exit(-1);
24     }
25     if(sigaction(SIGQUIT,&ignore,&savequit) <0)
26     {
27         perror("sigaction() error");
28         exit(-1);
29     }
30     sigemptyset(&chldmask);
31     sigaddset(&chldmask,SIGCHLD);
32     if(sigprocmask(SIG_BLOCK,&chldmask,&savemask) < 0)
33     {
34         perror("sigprocmask() error");
35         exit(-1);
36     }
37     if((pid = fork()) == -1)
38     {
39         perror("fork() error");
40         exit(-1);
41     }
42     else if(pid == 0)
43     {
44         sigaction(SIGINT,&saveintr,NULL);
45         sigaction(SIGQUIT,&savequit,NULL);
46         sigprocmask(SIG_SETMASK,&savemask,NULL);
47         execl("/bin/sh","sh","-c",cmdstring,(char *)0);
48         _exit(-127);
49     }
50     else
51     {
52         while(waitpid(pid,&status,0) < 0)
53         {
54             if(errno != EINTR)
55             {
56                 status = -1;
57                 break;
58             }
59         }
60     }
61     if (sigaction(SIGINT,&saveintr,NULL)<0)
62         return -1;
63 }
64
65 int main()
66 {
67     printf("Pint date:\n");
68     mysystem("date");
69     printf("Print process:\n");
70     mysystem("ps");
71     exit(0);
72 }

程序执行结果如下:

12、sleep函数

此函数使调用进程被挂起,直到满足下列条件之一:(1)已经经过seconds所指定的墙上时钟时间(2)调用进程捕捉到一个信号并从信号处理程序返回。sleep的可靠实现如下:


 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <errno.h>
 6 #include <signal.h>
 7
 8 static void sig_alrm(int signo)
 9 {
10
11 }
12
13 unsigned int mysleep(unsigned int nsecs)
14 {
15     struct sigaction    newact,oldact;
16     sigset_t            newmask,oldmask,suspmask;
17     unsigned int        unslept;
18
19     newact.sa_handler = sig_alrm;
20     sigemptyset(&newact.sa_mask);
21     newact.sa_flags = 0;
22     sigaction(SIGALRM,&newact,&oldact);
23     sigemptyset(&newmask);
24     sigaddset(&newmask,SIGALRM);
25     sigprocmask(SIG_BLOCK,&newmask,&oldmask);
26     alarm(nsecs);
27     suspmask = oldmask;
28     sigdelset(&suspmask,SIGALRM);
29     sigsuspend(&suspmask);
30     unslept = alarm(0);
31     sigprocmask(SIG_SETMASK,&oldmask,NULL);
32     return unslept;
33 }
34
35 int main()
36 {
37     int i;
38     printf("Program starting.\n");
39     printf("sleep 5 seconds.....\n");
40     for(i=1;i<=5;++i)
41     {
42         printf("The %dth second.\n",i);
43         mysleep(1);
44     }
45     printf("wake up.\n");
46     exit(0);
47 }

程序执行结果如下:

原文地址:https://www.cnblogs.com/alantu2018/p/8466075.html

时间: 2024-10-10 12:24:31

Unix环境高级编程(十)信号续的相关文章

UNIX环境高级编程之-----信号signal

参考书籍:unxi环境高级编程 信号函数: <span style="font-family:Microsoft YaHei;font-size:18px;">typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); </span> 其原型为: <span style="font-family:Microsoft YaHei;

Unix环境高级编程(十五)高级I/O

1.非阻塞I/O 对低速设备的I/O操作可能会使进程永久阻塞,这类系统调用主要有如下情况:(1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道.终端设备和网络设备).(2)如果数据不能立即被接受,则写这些同样的文件也会使调用者永远阻塞:(3)在某些条件发生之前,打开文件会被阻塞(例如以只写方式打开一个FIFO,那么在没有其他进程已用读方式打开该FIFO时):(4)对已经加上强制性锁的文件进行读.写:(5)某些ioctl操作:(6)某些进程间通信函数: 非阻塞I/O调用open.re

Unix环境高级编程(十二)线程控制

本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制: Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询.和其它的限制使用目的一样,为了应用程序的在不同操作 系统的可移植性. 一些限制: PTHREAD_DESTRUCTOR_ITERATIONS: 销毁一个线程数据最大的尝试次数,可以通过_SC_THREAD_DESTRUCTOR_ITERATIONS作为sysconf的参数查询. PTHREAD_K

Unix环境高级编程(十六)进程间通信

进程间通信(IPC)是指能在两个进程间进行数据交换的机制.现代OS都对进程有保护机制,因此两个进程不能直接交换数据,必须通过一定机制来完成. IPC的机制的作用: (1)一个软件也能更容易跟第三方软件或内核进行配合的集成,或移植.如管道,在shell 下执行 ps –aux | grep bash. (2)简化软件结构, 可以把一个软件划分多个进程或线程,通过IPC,集成在一起工作.如消息队列. (3)让操作系统各个模块交换数据,包括内核与应用程序机制. (4)提供进程之间或同一进程之间多线程的

(十二) 一起学 Unix 环境高级编程 (APUE) 之 进程间通信(IPC)

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字 . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APU

Unix环境高级编程

Advanced Programming in the UNIX Environment Second Edition Unix 环境高级编程 第二版 目录: 第一章:UNIX基础知识 第二章:UNIX标准化及实现 第三章:文件I/O 第四章:文件和目录 第五章:标准I/O库 第六章:系统数据文件盒信息 第七章:进程环境 第八章:进程控制 第九章:进程关系 第十章:信号 第十一章:线程 第十二章:线程控制 第十三章:守护线程 第十四章:高级I/O 第十五章:进程间通信 第十六章:网络IPC:套接

(十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

(十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)