《APUE》读书笔记—第十章信号(下)
接着昨天学习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 }
程序执行结果如下: