1.信号:
每个信号都有一个自己的名字和编号,这些名字都以“SIG”开头,例如“SIGIO”,“SIGCHLD”等。
信号定义在#include <signal.h>中,信号名都定义为正整数。具体信号名称可以用kill -l来查询信号的名字和序号,信号都是从1开始编号,不存在0信号,因为程序中kill 0有额外的特殊应用。
信号的处理:
信号的处理一般有三种方法:忽略、捕捉、默认动作
- 忽略:大多数信号多可以使用它,但是SIGKILL和SIGSTOP无法被忽视
- 捕捉:信号产生的时候,由内核来调用用户自定义的信号处理函数,来处理某种信号
- 系统默认动作:发生什么信号,系统执行默认的处理动作(具体可查阅man 7 signal查看系统信号默认动作,新手不建议看)
当系统中有需要结束的进程,不仅可以使用ctrl+c了,现在也可以使用kill -9 + 进程id。而其中的9就是信号SIGKILL。(可以自己去写个死循环然后试试)
信号处理的注册函数:
signal(入门)、sigaction(高级)
信号处理的发送函数:
kill(入门)、sigqueue(高级)
signal的demo:
#include <signal.h>
#include <stdio.h>
//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum){
printf("signum = %d\n",signum);
printf("Quit failure!\n");
}
//handler函数可以不带参数,收到后信号的处理方式也是可以自己定义的
int main(){
signal(SIGINT,handler);
//signal相当于是修改该指令
//ctrl+c终止程序是信号SIGNINT
while(1){};
return 0;
}
对于SIGKILL来说是无法进行更改的(捕捉到之后无法修改原有的指令)
kill的demo:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char** argv){
pid_t pid;
int signal;
pid = atoi(argv[1]);
signal = atoi(argv[2]);
//这两个位置用atoi是因为在命令行里定义的是char**类型,所以要转换成int类型,所以用asc to int函数
kill(pid,signal);
//这里kill还可以换成system的写法
//char cmd[128];
//sprintf(cmd,"kill -%d %d",pid,signal);
//system(cmd);
printf("Done successfully!\n");
return 0;
}
~
sigaction:
int sigaction(int signnum,const struct sigaction* act,const struct sigaction* oldcast);
第二个参数就是捕捉到指令后要做什么,第三个参数是备份(一般回传个一个NULL),代表原信号的动作
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
1.sa_handler和sa_sigaction是信号处理函数指针,而这使用其一,此参数和signal()的参数handler相同,代表新的信号处理函数
2.sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
3.sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
- SA_SIGINFO 指定信号处理函数需要三个参数,所以应使用sa_sigaction替代sa_handler。
- SA_NODEFER 在信号处理函数处置信号的时段中,核心程序不会把这个间隙中产生的信号阻塞。
- SA_INTERRUPT 由此信号中断的系统调用不会自动重启
- SA_RESTART 核心会自动重启信号中断的系统调用,否则返回EINTR错误值。(重启被中断的系统调用)
- SA_RESETHAND 信号处理函数接收到信号后,会先将对信号处理的方式设为预设方式,而且当函数处理该信号时,后来发生的信号将不会被阻塞。
- SA_ONSTACK 如果利用sigaltstack()建立信号专用堆栈,则此标志会把所有信号送往该堆栈。
- SA_NOCLDSTOP 假如signum的值是SIGCHLD,则在子进程停止或恢复执行时不会传信号给调用本系统调用的进程。
- SA_NOCLDWAIT 当调用此系统调用的进程之子进程终止时,系统不会建立zombie进程。
siginfo_t(sa_sigaction的第二个参数)
{
int si_signo; /* 信号值,对所有信号有意义*/
int si_errno; /* errno值,对所有信号有意义*/
int si_code; /* 信号产生的原因,对所有信号有意义*/
int si_trapno; /* Trap number that caused hardware-generated signal (unused on most architectures) /
pid_t si_pid; / 发送信号的进程ID,对kill(2),实时信号以及SIGCHLD有意义 /
uid_t si_uid; / 发送信号进程的真实用户ID,对kill(2),实时信号以及SIGCHLD有意义 /
int si_status; / 退出状态,对SIGCHLD有意义*/
clock_t si_utime; /* 用户消耗的时间,对SIGCHLD有意义 /
clock_t si_stime; / 内核消耗的时间,对SIGCHLD有意义 /
sigval_t si_value; / 信号值,对所有实时有意义,是一个联合数据结构,可以为一个整数(由si_int标示,也可以为一个指针,由si_ptr标示)/
int si_int; / POSIX.1b signal /
void si_ptr; / POSIX.1b signal /
int si_overrun;/ Timer overrun count; POSIX.1b timers /
int si_timerid;/ Timer ID; POSIX.1b timers /
void * si_addr; / 触发fault的内存地址,SIGILL,SIGFPE,SIGSEGV,SIGBUS 信号有意义/
int si_band; /* 对SIGPOLL信号有意义 /
int si_fd; / 对SIGPOLL信号有意义 */
}
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
//int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
void handler(int signum, siginfo_t* info, void* context){
printf("signum = %d\n",signum);
if(context != NULL){
printf("Signal number is %d\n",info->si_signo);
printf("Receive signal from %d\n",info->si_pid);
printf("POSIX.1b signal = %d\n",info->si_int);
printf("get data = %d\n",info->si_value.sival_int);
}
return;
}
int main(){
struct sigaction act;
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
//SA_SIGINFO是接受信号的宏
printf("pid = %d\n",getpid());
sigaction(SIGUSR1,&act,NULL);
while(1);
return 0;
}
sigqueue:
int sigqueue(pid_t pid, int sig, const union sigval value);
//sigval不用自己定义
union sigval
{
int sival_int;
void* sival_ptr;
};
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <signal.h>
#include <stdlib.h>
//int sigqueue(pid_t pid, int sig, const union sigval value);
int main(int argc,char** argv){
pid_t pid = atoi(argv[1]);
int sig = atoi(argv[2]);
union sigval value;
value.sival_int = 100;
//传递字符串的话,就用value.sival_ptr = "Hello world!";
if(sigqueue(pid,sig,value) == 0){
printf("Send successfully!\n");
}else{
printf("ERROR!\n");
perror("Why");
}
return 0;
}
运行结果:
sigaction和sigqueue函数的用法相对比较复杂,需要结合API去进行使用,里边的结构体嵌套,和一些变量的意义需要多看多写去领悟。