2. 信号发生条件 用户敲击终端,比如按下ctrl+c (SIGINT) 发给所有前台进程组, ctrl+z 发 SIGSTOP 挂起前台进程,ctrl+\ 发SIGQUIT 给进程。而 ctrl+D 只是发EOF给终端
-
硬件异常。 比如遇到除以0, 或者内存访问越界的情况。
-
kill 函数发相应的信号
-
软件条件发生。 比如向一个已经关闭的管道写数据会产生SIGPIPE,以及闹钟信号SIGALRM。或者网络上传来一个非规定波特率的数据,SIGURG
SIGPIPE的例子#include <stdio.h> #include <signal.h> static void sig_alrm(int signo) { printf("Caught alarm signal\n"); } int main() { if (signal(SIGALRM,sig_alrm)==SIG_ERR) { printf("Register SIGALRM failed\n"); return -1; } //register one second alarm alarm(1); pause(); printf("Exit main\n"); } //输出 //Caught alarm signal //Exit main
输出#include <stdio.h> #include <stdlib.h> #include <signal.h> static void sig_pipe(int signo) { printf("Caught signal pipe signal\n"); } int main() { int fd[2]; if(pipe(fd)<0) { printf("Create pipe failed\n"); exit(-1); } pid_t pid; pid = fork(); if (pid<0) { printf("fork error\n"); } else if (pid>0) { close(fd[0]); // register pipe signal if(signal(SIGPIPE,sig_pipe)==SIG_ERR) { printf("register SIGPIPE error\n"); sleep(1); exit(-1); } sleep(1); printf("Parent Prcess begin\n"); //try to write a closed pipe if(write(fd[1],'c',1)!=1) printf("write pipe error\n"); close(fd[1]); printf("Parent Prcess end\n"); } else { printf("Child Prcess begin\n"); //here close read and write channel close(fd[0]); close(fd[1]); } }
Child Prcess begin
Parent Prcess begin
Caught signal pipe signal
write pipe error
Parent Prcess end
Kill 函数的例子
3. 对待信号的三种方式: 捕抓,忽视或者系统默认。 SIGKILL, SIGSTOP不可忽略。#include <stdio.h> #include <signal.h> static void sig_usr1(int signo) { printf("Get signal user1 \n"); } int main() { if (signal(SIGUSR1, sig_usr1)==SIG_ERR) { printf("Register sig user1 failed\n "); } kill(getpid(),SIGUSR1); printf("End of main\n"); }
4. Signal 函数 void(* signal(int signo, void(*func)(int) ) ) (int), 好复杂。
typedef void Sigfunc (int)
然后再定义 Sigfunc* signal(int signo, Sigfunc*)
5. 程序后台启动时候shell 自动对SIGINT 和SIGQUIT 选择忽略
6. 进程创建时候子进程复制父进程的信号处理方式
输入ctrl+\ 时候会得到两行,分别为子进程和父进程产生的#include <stdio.h> #include <stdlib.h> #include <signal.h> void sig_quit(int signo) { printf("caught SIGQUIT \n"); } int main() { if (signal(SIGQUIT,sig_quit)==SIG_ERR) { printf("Register sigquit error\n"); } pid_t pid ; pid = fork(); if (pid< 0) { printf("fork error\n"); exit(-1); } else if(pid>0) { while(1) pause(); } else { while(1) pause(); } }
caught SIGQUIT
caught SIGQUIT7. 中断的系统调用。
系统调用分为低速系统调用和其他系统调用。低速系统调用,信号发生时, 其不再执行。
低速系统调用包括:
a. pause 函数
b. 读某些文件,数据不存在;如果某进程为读打开FIFO,且此时没有为写的进程打开该FIFO
上面代码将一直阻塞,一直等到有其他程序写fifo为止。#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #define FIFO_NAME "/tmp/fifotest" #define BUF_LEN 512 int main() { int res; extern int errno; res = mkfifo(FIFO_NAME, O_CREAT| O_EXCL); if ((res<0) && (errno!= EEXIST)) { printf("We get an error %d, reason %s\n", errno, strerror(errno)); } int fd = open(FIFO_NAME,O_RDONLY,0); char buf[BUF_LEN]; int n; if ((n=read(fd, buf,BUF_LEN))<0) { printf("read error %d, reason %s\n", errno, strerror(errno)); } buf[n]= '\0'; printf("read data from fifo: %s\n", buf); }
下面代码将写相应的fifo,运行下面代码可使得上面读fifo的进程激活,并输出内容。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #define FIFO_NAME "/tmp/fifotest" int main() { int fd; fd = open(FIFO_NAME, O_WRONLY,0 ); char* buf = "Greeting from fifo message\n"; if(write(fd, buf, strlen(buf))!= strlen(buf)) { printf("Write error. errno= %d, reason is : %s\n", errno, strerror(errno)); } }
c. 写某些文件,不能立即接受这些数据;打开文件时; 某种ioctl函数;某些进程间通信函数。
8. 可再入函数
典型的不可再入函数包括 a)malloc, free b) 操作静态区内容的函数, 比如getpwnam c)标准IO 函数
9. 信号未决
信号未决指的是信号产生,却还没有传递到进程的状态。
如果一个信号发生多次,那最终进程接收信号时,只会被传递一次。
10. Kill ,raise函数
Kill函数参数: a) pid>0, 则发送给对应pid进程。 b) pid == 0 , 发送信号给所在进程的进程组所有进程 c) pid< 0, 发送给绝对值等于PID的进程组中所有进程
测试了kill pid>0 和pid=0的情况#include <stdio.h> #include <signal.h> static void sig_usr1(int signo) { printf("Get signal user1 \n"); } int main() { if (signal(SIGUSR1, sig_usr1)==SIG_ERR) { printf("Register sig user1 failed\n "); } kill(getpid(),SIGUSR1); printf("Test kill pid > 0 senario done\n"); pid_t pid; if ((pid=fork())<0) { printf("Fork error\n "); } else if (pid> 0) { printf("pid = %d, group id = %d\n", getpid(), getpgrp()); while(1) pause(); } else { printf("pid = %d, group id = %d\n", getpid(), getpgrp()); kill(0,SIGUSR1); } }
输出为:
Get signal user1
Test kill pid > 0 senario done
pid = 28220, group id = 28220
pid = 28221, group id = 28220
Get signal user1
Get signal user1
11. alarm 和pause 函数
alarm 提示系统多长时间向进程发送SIGALRM信号. alarm 函数后注册的闹钟会把前面注册的给覆盖掉。比方说下面的例子,printf两次之间应该只是相隔一秒钟时间。
time now is 1440655538
Caught alarm signal
time now is 1440655539
#include <stdio.h> #include <signal.h> #include <time.h> static void sig_alrm(int signo) { printf("Caught alarm signal\n"); } int main() { if (signal(SIGALRM,sig_alrm)==SIG_ERR) { printf("Register SIGALRM failed\n"); return -1; } //register one second alarm printf("time now is %d\n", time(NULL)); alarm(2); alarm(1); pause(); printf("time now is %d\n", time(NULL));
pause函数则阻塞进程,直到进程得到信号。
用alarm, pause 和setjmp 设置的sleep 函数。缺点是当和其他信号发生冲突时,会有些问题。
#include <stdio.h> #include <setjmp.h> #include <signal.h> static jmp_buf buf; static void sig_alrm(int signo) { longjmp(buf,1); } int sleep2(unsigned int seconds) { if (signal(SIGALRM, sig_alrm)==SIG_ERR) { printf("Register SIGALRM failed\n"); } alarm(seconds); if(!setjmp(buf)) { printf("set jump first return\n"); pause(); } else { printf("Set jump returned\n"); } return seconds; } int main() { sleep2(1); }
下面代码,在5秒总之内发生SIGINT 信号,将导致sig_int后面的语句无法执行完成。#include <stdio.h> #include <setjmp.h> #include <signal.h> static jmp_buf buf; static void sig_alrm(int signo) { longjmp(buf,1); } int sleep2(unsigned int seconds) { if (signal(SIGALRM, sig_alrm)==SIG_ERR) { printf("Register SIGALRM failed\n"); } alarm(seconds); if(!setjmp(buf)) { printf("set jump first return\n"); pause(); } else { printf("Set jump returned\n"); } return seconds; } static void sig_int(int signo) { printf("Sum is 0\n"); volatile int sum; int i; for(i=0;i<2000000000;i++) sum += i*i; printf("Sum is %d\n", sum); } int main() { if(signal(SIGINT,sig_int)==SIG_ERR) { printf("Register SIGINT failed\n"); } sleep2(5); }
12 信号集sigset_tint sigemptyset(sigset_t * )
int sigfillset(sigset_t *)
int sigdelset(sigset_t* ,int signo)
int sigaddset(sigset_t* ,int signo)
int sigismember(const sigset_t *, int signo)
int sigprocmask(int how, const sig_set* new, sig_set* old)
sigprocmask是个好函数,一个函数既可以获取当前信号屏蔽字,也可以设置信号屏蔽。
how 等于SIG_BLOCK, SIG_UNBLOCK,SIG_SETMASK。前面为取并,第二个为取交,最后一个直接设置。 打印当前屏蔽字的demo
#include <stdio.h> #include <signal.h> #include <errno.h> #include <string.h> void printmask() { int result; sigset_t new,old; if (sigprocmask(SIG_BLOCK,NULL, &old)<0) { printf("Sigprocmast failed. errno = %d, error is: %s\n", errno, strerror(errno)); return; } if (sigismember( &old,SIGALRM)) printf("sig_alrm has been in signal mask\n"); printf("End of print mask\n"); } int main() { sigset_t new,old; if(sigemptyset(&new)<0 || sigemptyset(&old)<0 || sigaddset(&new, SIGALRM)<-1) { printf("sigemtpy set error. errno = %d, error is : %s\n",errno,strerror(errno)); return -1; } if (sigprocmask(SIG_BLOCK,&new, &old)<-1) { printf("sigprocmask set error. errno = %d, error is : %s\n",errno,strerror(errno)); return -1; } printmask(); }
int sigpending(sigset_t*) 正确返回0,未决信号存储在参数中。下面代码执行后,中间按两次ctrl + c,得到结果
SIGINT is in pending status
Caught sig int
#include <stdio.h> #include <signal.h> #include <errno.h> #include <string.h> static void sig_int(int signo) { printf("Caught sig int \n"); } int main() { sigset_t new,old; sigset_t pending; if (signal(SIGINT,sig_int) ==SIG_ERR) { printf("signal error\n"); return; } if(sigemptyset(&new)<0 || sigemptyset(&old)<0 || sigemptyset(&pending)<0 || sigaddset(&new, SIGINT)<-1) { printf("sigemtpy or sigemptyset error. errno = %d, error is : %s\n",errno,strerror(errno)); return -1; } if (sigprocmask(SIG_BLOCK,&new, &old)<-1) { printf("sigprocmask set error. errno = %d, error is : %s\n",errno,strerror(errno)); return -1; } sleep(5); if (sigpending(&pending)<-1) { printf("sigpending error. errno = %d, error is : %s\n",errno,strerror(errno)); return -1; } if (sigismember(&pending, SIGINT)) printf("SIGINT is in pending status\n"); if (sigprocmask(SIG_SETMASK,&old, NULL)<-1) { printf("sigprocmask set error. errno = %d, error is : %s\n",errno,strerror(errno)); return -1; } }
13. sigaction 函数int sigaction(int signo, const sigaction* act, sigaction* oact)
struct sigaction
{
void (*sighandler)(); //signal process function
sigset_t mask; //mask 为信号屏蔽字,在sighandler 函数返回之前,会屏蔽信号。返回后再解除屏蔽,这样做的目的是保证sighandler的执行不会受到影响。
int sa_flags;
};
14. sigsetjmp, setjmp, setlongjmp 和longjmp
siglongjmp 从信号函数跳出后会恢复信号屏蔽字,而longjmp从信号函数跳出后则不会恢复信号。
下面代码,当用sig开头函数时,每次向进程发送SIGUSR1信号时都会输出一次:
Get signal usr1
Returned from sigsetjmp
而不用sig开头函数时,只有第一次向进程发送SIGUSR1才会输出:
Get signal usr1
Returned from sigsetjmp
#include <stdio.h> #include <setjmp.h> #include <signal.h> #include <string.h> #include <errno.h> #include <stdlib.h> static jmp_buf buf; static void sig_usr(int signo) { printf("Get signal usr1\n"); //siglongjmp(buf,1); longjmp(buf,1); } int main() { if (signal(SIGUSR1,sig_usr)==SIG_ERR) { printf("Register singal error %d. reason:%s\n", errno, strerror(errno)); exit(-1); } //if(!sigsetjmp(buf,1)) if(!setjmp(buf)) { printf("sigsetjmp buf\n"); } else { printf("Returned from sigsetjmp\n"); } while(1) pause(); }
15. sigsuspend 函数 解决pause会丢失信号,从而造成进程永远阻塞问题。sigsuspend相当于unblock 信号mask 和pause()的原子操作。如下程序kill 信号SIGINT将立即唤醒进程。#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <errno.h> #include <string.h> static void sig_usr1(int signo) { printf("Get signal\n"); } int main() { printf("Begin main()\n"); if (signal(SIGUSR1,sig_usr1)==SIG_ERR ||signal(SIGINT,sig_usr1)==SIG_ERR ) { printf("Register sig_usr1 failed. errno=%d, error = %s", errno,strerror(errno)); return -1; } sigset_t oldset, newset, pendmask; if(sigemptyset(&oldset)<0 || sigemptyset(&newset)<0 ||sigemptyset(&pendmask)<0 || \ sigaddset(&newset,SIGUSR1)<0 ||sigaddset(&newset,SIGINT)<0 || sigaddset(&pendmask,SIGUSR1)<0) { printf("sigemtpyset or sigaddset error. errno=%d, error = %s", errno,strerror(errno)); return -1; } if (sigprocmask(SIG_BLOCK,&newset,&oldset)<0) { printf("sigprocmask error. errno=%d, error = %s", errno,strerror(errno)); return -1; } printf("Critical section\n"); sleep(5); sigsuspend(&pendmask); printf("Awake by signal"); if (sigprocmask(SIG_SETMASK,&oldset,NULL)<0) { printf("sigprocmask error. errno=%d, error = %s", errno,strerror(errno)); return -1; } }
16. abort函数
书上的abort函数有些啰嗦,不懂后面哪一段代码有什么必要。懂的同学可以评论下,让我知道。
#include <stdio.h> #include <signal.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <stdlib.h> void mabort(void) { struct sigaction action; sigset_t set,oldset; if(sigaction(SIGABRT,NULL,&action)<0) { printf("sigaction error:%d, reason: %s\n", errno,strerror(errno)); exit(-1); } if(action.sa_handler==SIG_IGN) { action.sa_handler == SIG_DFL; sigaction(SIGABRT,&action,NULL); } if(action.sa_handler==SIG_DFL) fflush(NULL); if(sigfillset(&set)<0 || sigdelset(&set,SIGABRT)<0) { printf("sigfillset error:%d, reason: %s\n", errno,strerror(errno)); exit(-1); } if(sigprocmask(SIG_SETMASK,&set,NULL)<0) { printf("sigaction error:%d, reason: %s\n", errno,strerror(errno)); exit(-1); } kill(getpid(),SIGABRT); printf("we are here\n"); printf("we are here\n"); fflush(NULL); } int main() { printf("we are in main\n"); mabort(); printf("we are end main\n"); return; }
17 system 函数POSIX 2.0 要求system 函数忽略SIGINT , SIGQUIT,并且阻塞SIGCHLD。测试代码如下:
18 sleep 函数实现#include <signal.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> int mysystem(const char* cmd) { struct sigaction ignore,saveintptr, savequitptr; ignore.sa_handler = SIG_IGN; sigemptyset(&ignore.sa_mask); pid_t pid; int status; //ignore sigint and sigquit if (sigaction(SIGINT,&ignore,&saveintptr)<0 || sigaction(SIGQUIT,&ignore, &savequitptr)< 0 ) { printf("sigaction failed. errno: %d, reason: %s",errno, strerror(errno)); return -1; } sigset_t childmask,savemask; if (sigemptyset(&childmask)<0 || sigaddset(&childmask,SIGCHLD)<0) { printf("sigemtpyset failed. errno: %d, reason: %s",errno, strerror(errno)); return -1; } if (sigprocmask(SIG_BLOCK,&childmask,&savemask)<0) { //printf("sigaction failed. errno: %d, reason: %s",errno, strerror(errno)); return -1; } if((pid=fork())<0) { printf("fork error. errno: %d, reason: %s",errno, strerror(errno)); return -1; } else if(pid==0) { //child sleep(5); sigaction(SIGINT,&saveintptr,NULL); sigaction(SIGQUIT,&savequitptr,NULL); sigprocmask(SIG_SETMASK,&savemask,NULL); execl("/bin/sh","sh","-c", cmd, (char*)0); //why not print this, because, execl with no return if succeed printf("returning child %d\n", pid); fflush(NULL); _exit(127); } else { //here should be blocked, just while in case of error while(waitpid(pid, &status,0) <0) { printf("wait pid is %d\n", pid); //why we use EINTR, EINTR is for slow system, which get signal, here in case of execl for slow function if(errno!=EINTR) { status =-1; break; } } //waitpid(pid, &status,0) ; printf("waiting %d\n", pid); } sigaction(SIGINT,&saveintptr,NULL); sigaction(SIGQUIT,&savequitptr,NULL); sigprocmask(SIG_SETMASK,&savemask,NULL); return status; } int main() { int status = mysystem("ls -lt"); printf("status is %d\n", status); }
#include <stdio.h> #include <signal.h> static void sig_alrm() { return; } unsigned int msleep(unsigned int nsecs) { sigset_t pendmask,oldmask,susmask; struct sigaction action,oldaction; action.sa_handler = sig_alrm; if(sigemptyset(&action.sa_mask)<0) return -1; if(sigaction(SIGALRM,&action,&oldaction)<0) return -1; sigemptyset(&pendmask); sigaddset(&pendmask, SIGALRM); sigprocmask(SIG_BLOCK,&pendmask,&oldmask); alarm(nsecs); susmask = oldmask; sigdelset(&susmask,SIGALRM); sigsuspend(&susmask); int uslept = alarm(0); sigaction(SIGALRM,&oldaction,NULL); sigprocmask(SIG_BLOCK,&oldmask,NULL); return uslept; } int main() { msleep(5); }