官方参考链接
Linux信号
| 序号 | 名称 | 默认行为 | 相应事件 |
|---|---|---|---|
| 1 | SIGHUP | 终止 | 终端线挂断 |
| 2 | SIGINT | 终止 | 来自键盘的终端 |
| 3 | SIGQUIT | 终止 | 来自键盘的退出 |
| 4 | SIGILL | 终止 | 非法指令 |
| 5 | SIGTRAP | 终止并转储内存 | 跟踪陷阱 |
| 6 | SIGABRT | 终止并转储内存 | 来自abort函数的终止信号 |
| 7 | SIGBUS | 终止 | 总线错误 |
| 8 | SIGFPE | 终止并转储内存 | 浮点异常 |
| 9 | SIGKILL | 终止 | 杀死进程,这个信号既不能被捕获也不能被忽略 |
| 10 | SIGUSR1 | 终止 | 用户自定义的信号1 |
| 11 | SIGSEGV | 终止并转存内存 | 无效的内存引用(段错误) |
| 12 | SIGUSR2 | 终止 | 用户自定义的信号2 |
| 13 | SIGPIPE | 终止 | 向一个没有读用户的管道做写操作。比如,如果一个 socket 在接收到了 RST packet 之后,程序仍然向这个 socket 写入数据,那么就会产生SIGPIPE信号。 |
| 14 | SIGALRM | 终止 | 来自alarm函数的定时器信号 |
| 15 | SIGTERM | 终止 | 软件终止信号,与SIGKILL不同的是该信号可以被阻塞和处理 |
| 16 | SIGSTKFLT | 终止 | 协处理器上的栈故障 |
| 17 | SIGCHLD | 忽略 | 一个子进程终止或者停止 |
| 18 | SIGCONT | 忽略 | 继续进程,如果该进程停止 |
| 19 | SIGSTOP | 停止直到下一个SIGCONT | 不是来自终端的停止信号,不能被修改 |
| 20 | SIGTSTP | 停止直到下一个SIGCONT | 来自终端的停止信号 |
| 21 | SIGSTTIN | 停止直到下一个SIGCONT | 后台进程从终端读 |
| 22 | SIGSTTOU | 停止直到下一个SIGCONT | 后台进程向终端写 |
| 23 | SIGURG | 忽略 | 套接字上的紧急通知 |
| 24 | SIGXCPU | 终止 | CPU事件限制超出 |
| 25 | SIGXFSZ | 终止 | 文件大小限制超出 |
| 26 | SIGVTALRM | 终止 | 虚拟定时器期满 |
| 27 | SIGPROF | 终止 | 剖析定时器期满 |
| 28 | SIGWINCH | 忽略 | 终端窗口大小变化 |
| 29 | SIGIO | 终止 | 在某个描述符上可执行I/O操作 |
| 30 | SIGPWR | 终止 | 电源故障 |
eval 命令解析
eval(char *cmdline){
char* argv[MAXARGS]; //存放参数,供execve函数使用
char buf[MAXLINE]; //限制行最大数据
int bg; //后台运行or前端运行标记位
pid_t pid, jid;
sigset_t mask_all, mask_one, prev_one;
sigemptyset(&mask_one);
sigaddset(&mask_one, SIGCHLD);
sigfillset(&mask_all);
strcpy(buf, cmdline);
bg = parseline(buf, argv);
if(argv[0] == NULL){ //忽略空行
return;
}
if(!builtin_cmd(argv)){ //是不是内置命令
sigprocmask(SIG_SETMASK, &mask_one, &prev_one); //阻塞SIGCHLD信号,防止deletejob和addjob的竞争
if((pid = fork()) == 0){
setpgid(0, 0);
sigprocmask(SET_MASK, &prev_one, NULL); //子进程继承了父进程的阻塞信号,需要解除。
if(execve(argv[0], argv, environ) < 0){ //execve函数使得继承自父进程的信号处理函数恢复为默认
printf("command not found!");
exit(0);
}
}
sigprocmask(SIG_SETMASK, &mask_all, &prev_one); //阻塞所有信号,以便执行addjob
if(!bg){
flag = 0;
addjob(jobs, pid, FG, buf);
waitfg(pid);
}else{
jid = addjob(jobs, pid, BG, buf);
printf("[%d] (%d) %s", jid, pid, buf);
}
sigprocmask(SIG_SETMASK, &prev_one, NULL);
}
return;
}
builtin_cmd
builtin_cmd(char** argv){
if(!strcmp(argv[0], "quit")){
exit(0);
}else if(!strcmp(argv[0], "jobs"){
listjobs(jobs);
return 1;
}else if(!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")){
do_bgfg(argv);
return 1;
}else if(!strcmp(argv[0], "&"){
return 1;
}
return 0;
}
do_bgfg
do_bgfg(char** argv){
int bg = 0;
if(!strcmp(argv[0], "bg")){
bg = 1;
}
struct job_t *job_ptr = NULL;
int pid, jid;
if(sscanf(argv[1], "%d", &pid) > 0){ // 进程ID
job_ptr = getjobpid(jobs, pid); //根据pid查找job
if(job_ptr == NULL || job_ptr == UNDEF){
printf("(%d): no such process", pid);
return;
}
jid = job_ptr->jid;
}else if(sscanf(argv[1], "%%%d", &jid) > 0){
job_ptr = getjobjid(jobs, jid); // 通过jid获得job
if(job_ptr == NULL || job_ptr == UNDEF){
printf("[%d]: no such jid", pid);
return;
}
pid = job_ptr->pid;
}else{
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
if(bg){
if(job_ptr->state == BG){
printf("[%d] (%d) %s", jid, pid, job_ptr->cmdline);
return;
}
if(job_ptr->state == ST){
kill(pid, SIGCONT);
job_ptr->state = BG;
printf("[%d] (%d) %s", jid, pid, job_ptr->cmdline);
}
}else{
flag = 0;
sigset_t mask_all, prev;
sigprocmask(SIG_SETMASK, &mask_all, &prev);
if(job_ptr->state == ST){
kill(pid, SIGCONT);
}
job_ptr->state = FG; //确保这一行在waitfg前面
waitfg(pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
return ;
}
waitfg 阻塞当前进程直到pid所指进程不再是前端进程(或者终止或者挂起)
waitfg(pid_t pid){
sigset_t mask_empty;
sigemptyset(&mask_empty);
while(!flag){
sigsuspend(&mask_empty); //挂起进程,且不阻塞任何信号
}
}
sigchld_handler SIGCHLD信号处理程序
void sigchld_handler(int sig){
int olderrno = errno;
struct job_t *job_ptr = NULL;
pid_t child_pid, fg_pid = fgpid(jobs);
// WUNTRACED - 挂起调用进程,直到等待集合中的一个进程终止或停止
// WNOHANG - 如果等待集合中的任何子进程都没有终止,那么立即返回
while((child_pid = waitpid(-1, &status, WUNTRACED | WNOHANG) > 0){
if(fg_pid == child_pid){
flag = 1;
}
if(WIFEXITED(status)){ //由exit终止
deletejob(jobs, child_pid);
}else if(WIFSIGNALED(status)){ // 由一个未被捕获的信号终止 比如SIGKILL
job_ptr = getjobpid(jobs, child_pid);
printf("Job [%d] (%d) terminated by signal %d \n", job_ptr->jid, job_ptr->pid, WTERMSIG(status));
deletejob(jobs, child_pid);
}else if(WIFSTOPPED(status)){ // 引起返回的子进程是否停止
job_ptr = getjobpid(jobs, child_pid);
job_ptr->status = ST;
printf("Job [%d] (%d) stopped by signal %d \n", job_ptr->jid, job_ptr->pid, WTERMSIG(status));
}
}
errno = olderrno;
}
sigint_handler Ctrl-c终止进程将触发SIGINT信号
void sigint_handler(int sig){
int olderrno = errno;
pid_t fg_pid = fgpid(jobd);
if(fp_pid == 0){
return ;
}
kill(fg_pid, SIGINT);
errno = olderrno;
return;
}
sigtstp_handler Ctrl-z挂起进程触发SIGTSTP信号
void sigtstp_handler(int sig){
int olderrno = errno;
pid_t fg_pid = fgpid(jobs);
if(fg_pid <= 0){
return;
}
sigset_t mask_all, prev;
sigfillset(&mask_all);
sigprocmask(SIG_SETMASK, $mask_all, &prev); // 屏蔽其他信号
kill(fg_pid, SIGTSTP); //传递停止信号给前端程序
struct job_t *job_ptr = getjobpid(jobs, fg_pid);
job_ptr->state = ST;
sigprocmask(SIG_SETMASK, &prev, NULL);
errno = olderrno;
return ;
}
fork与execve程序对信号处理函数的影响
如果在fork函数执行前,父进程已经注册了信号处理函数,那么fork函数执行后,子进程也将继承父进程注册的信号处理函数。特殊的是execve函数,如果在子进程中执行了execve函数,子进程的信号处理函数将恢复为默认。例如,对eval中的execve部分的代码修改为
if ((pid = fork()) == 0) {
setpgid(0, 0);
sigprocmask(SIG_SETMASK, &prev_one, NULL); //子进程继承了父进程的阻塞信号,需要解除。
while(1);
}
此时,子进程收到的信号也会由从继承自父进程的信号处理函数处理。那么上述sigint_handler和sigtstp_handler函数将不能实现kill函数的效果。