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 }
程序执行结果如下:
Unix环境高级编程(十)信号续的更多相关文章
- Unix环境高级编程(十五)高级I/O
1.非阻塞I/O 对低速设备的I/O操作可能会使进程永久阻塞,这类系统调用主要有如下情况:(1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道.终端设备和网络设备).(2)如果数据不能立 ...
- Unix环境高级编程(十二)线程控制
本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制: Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询 ...
- UNIX环境高级编程--10. 信号
第十章 信号 信号是软中断,提供了一种处理异步事件的方法.例如,终端用户键入终端键,会通过信号机制停止一个进程,或及早终止管道中的下一个程序. 每个信号都有一个名字,SIG开 ...
- UNIX环境高级编程——可靠信号与不可靠信号
在早期的UNIX中信号是不可靠的,不可靠在这里指的是:信号可能丢失,一个信号发生了,但进程却可能一直不知道这一点. 现在Linux 在SIGRTMIN实时信号之前的都叫不可靠信号,这里的不可靠主要是不 ...
- Unix环境高级编程(九)信号
信号时软中断,很多比较重要的应用程序都需要处理信号,信号提供了一种处理异步事件的方法.每个信号都一个名字,以SIG开头,在<signal.h>头文件中定义信号类型,都是正整数(信号编号). ...
- Unix环境高级编程(十八)高级进程间通信
本章主要介绍了基于STREAM的管道和UNIX域套接字,这些IPC可以在进程间传送打开文件描述符.服务进程可以使用它们的打开文件描述符与指定的名字相关联,客户进程可以使用这些名字与服务器进程通信. 1 ...
- Unix环境高级编程(十四)守护进程实现时间服务器
守护进程是在后台运行不受终端控制的进程(如输入.输出等),一般的网络服务都是以守护进程的方式运行.守护进程脱离终端的主要原因有两点:(1)用来启动守护进程的终端在启动守护进程之后,需要执行其他任务.( ...
- Unix环境高级编程(十九)终端I/O
终端I/O应用很广泛,用于终端.计算机之间的直接连线.调制解调器以及打印机等等.终端I/O有两种不同的工作模式: (1)规范模式输入处理:终端输入以行为单位进行处理,对于每个读要求,终端驱动程序最多返 ...
- Unix环境高级编程(十六)进程间通信
进程间通信(IPC)是指能在两个进程间进行数据交换的机制.现代OS都对进程有保护机制,因此两个进程不能直接交换数据,必须通过一定机制来完成. IPC的机制的作用: (1)一个软件也能更容易跟第三方软件 ...
随机推荐
- 巧妙利用函数的惰性载入提高javascript 代码性能
在 javascript 代码中,因为各浏览器之间的行为的差异,我们经常会在函数中包含了大量的 if 语句,以检查浏览器特性,解决不同浏览器的兼容问题. 例如,我们最常见的为 dom 节点添加事件的函 ...
- 【Storm】学习笔记
Storm 1 基本概念 1.1 分布式.可扩展.高容错.实时流处理.跨语言 1.2 应用场景 1.2.1 实时分析 1.2.2 在线机器学习 1.2.3 分布式RPC 1.2.4 ETL数据抽取 1 ...
- Android 如何去掉手机中横竖屏切换时的转屏动画?
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- Linux shell 脚本入门教程+实例
原文:http://www.wiquan.com/article/136 为什么要进行shell编程 在Linux系统中,虽然有各种各样的图形化接口工具,但是shell仍然是一个非常灵活的工具.She ...
- [Functional Programming] Fst & Snd, Code interview question
cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last element of that ...
- 浅谈Android五大布局(一)——LinearLayout、FrameLayout和AbsoulteLayout
Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦.组件按照布局的要求依次排列,就组成了用户所看见的界面.Android的五大布局分别是LinearLay ...
- VC操作MPP文件
1.背景简介 因需要对Office系列进行程序操作,特需要使用COM编程. Microsoft Project生成进度计划,office家族软件,文件后缀为.mpp. 具体信息见维基百科http:// ...
- UILabel文字竖排
方法一: UILabel *mindName = [[UILabel alloc]initWithFrame:kCR(, , ,)]; mindName.text = @"苏\n小\n明&q ...
- 将 Shiro 作为应用的权限基础 二:shiro 认证
认证就是验证用户身份的过程.在认证过程中,用户需要提交实体信息(Principals)和凭据信息(Credentials)以检验用户是否合法.最常见的“实体/凭证”组合便是“用户名/密码”组合. 一. ...
- poj 1879 Truck History
本题链接:点击打开链接 题目大意: 输入n表示卡车辆数,输入每辆卡车编号.即长度为7的字符串,每辆卡车编号均可由其他类型编号衍生过来,求由当中一辆衍生出其他全部的最小衍生次数(有一个字符不同就需衍生一 ...