信号的概念理解:
信号的种类:使用Kill -l 命令可以查看有多少个信号
[gongruiyang@localhost TestSignal]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
查看信号的具体含义命令:
man 7 signal
#include
#include
int main()
{
while(1)
{
printf("while循环ing\n");
sleep(1);
}
return 0;
}
while循环ing
while循环ing
while循环ing
while循环ing
while循环ing
^C
[gongruiyang@localhost signalCreate]$ ./hardTestExe
while循环ing
while循环ing
while循环ing
while循环ing
^Z
[1]+ Stopped ./hardTestExe
[gongruiyang@localhost signalCreate]$ ps aux | grep ./hardTest
gongrui+ 10153 0.0 0.0 4216 348 pts/0 T 13:40 0:00 ./hardTestExe
gongrui+ 10157 0.0 0.0 112828 992 pts/0 R+ 13:40 0:00 grep --color=auto ./hardTest
在程序运行过程中我们按下 ctrl + z 就可以暂停进程运行,此时该进程的进程状态是T,意为暂停状态
组合键 ctrl + z 本质上是SIGTSTP信号(20号信号),是一个暂停信号,让正在运行的前台程序暂停运行
[gongruiyang@localhost signalCreate]$ ./hardTestExe
while循环ing
while循环ing
while循环ing
^\Quit(core dumped)
[gongruiyang@localhost signalCreate]$ ls
core.10925 hardTest.c hardTestExe
在程序运行过程中我们按下 ctrl + | 就可以退出运行的进程,并产生一个core.XXX的核心转储文件
组合键 ctrl + | 本质上是SIGQUIT信号(3号信号),是一个结束进程并产生核心转储文件 的信号
- 核心转储文件概念:核心转储文件中存储的是异常终止进程产生的一个文件,进程终止瞬间将进程地址空间的内容以及有关进程状态的其他信息写出的这个磁盘文件,其中信息常用于调试寻找错误原因
- 核心转储概念:在UNIX系统中, 核心映像(core image) 就是进程(process)执行时的内存内容。当进程发生错误或收到“信号”(signal) 而终止执行时,系统会将核心映像写入一个文件,以作为调试之用,这就是所谓的核心转储(core dump)。
当进程异常退出或收到信号退出时,却没有产生核心转储文件,此时可以通以下命令查看core file size设置情况,根据打印出来的内容可以看出core file size被设置为0,我们需要修改该设置值为unlimited后,进程异常退出后才能产生核心转储文件
[gongruiyang@localhost signalCreate]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 14950
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
修改方式如下:
[gongruiyang@localhost signalCreate]$ ulimit -c unlimited
[gongruiyang@localhost signalCreate]$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 14950
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
#include
void func()
{
int* p = NULL;
*p = 10; //访问空指针导致程序崩溃
}
int main()
{
func();
return 0;
}
[gongruiyang@localhost signalCreate]$ gcc corefiletest.c -g -o coretest
[gongruiyang@localhost signalCreate]$ ./coretest
段错误(吐核)
[gongruiyang@localhost signalCreate]$ ls
core.58181 corefiletest.c coretest hardTest.c hardTestExe
gdb调试核心转储文件寻找错误地方
[gongruiyang@localhost signalCreate]$ gdb [可执行文件名] [核心转储文件]
(gdb) bt
#0 0x00000000004004fd in func () at corefiletest.c:5
#1 0x0000000000400513 in main () at corefiletest.c:9
(gdb) f 0
#0 0x00000000004004fd in func () at corefiletest.c:5
5 *p = 10; //访问空指针导致程序崩溃
(gdb) p p
$1 = (int *) 0x0
非法行为 | 信号量 | 信号名 |
---|---|---|
解引用空指针 | 11号信号并产生核心转储文件 | SIGSEGV |
访问越界 | 11号信号并产生核心转储文件 | SIGSEGV |
动态分配空间free两次 | 6号信号并产生核心转储文件 | SIGABRT |
int kill(pid_t pid, int sig);
功能:
头文件:
参数:
返回值:
测试程序:测试kill函数
#include
#include
#include
#include
int main()
{
int ret_kill = kill(getpid(),2);
if(ret_kill == -1)
perror("kill");
else
printf("信号量:%d\n",ret_kill);
return 0;
}
[gongruiyang@localhost signalinterface]$ ./killTest
[gongruiyang@localhost signalinterface]$
kill命令可以指定给具体进程发送具体信号量
kill -signal pid
通过以下命令获取进程pid
ps aux | grep 进程名
[gongruiyang@localhost TestSignal]$ ps aux |grep deadCircle
gongrui+ 59789 0.0 0.0 4216 352 pts/0 S+ 16:38 0:00 ./deadCircle
gongrui+ 59797 0.0 0.0 112828 984 pts/1 R+ 16:39 0:00 grep --color=auto deadC
通过kill -2干掉该进程,也可以使用9号信号量,该信号量是强杀信号,可以干掉大部分进程
[gongruiyang@localhost TestSignal]$ kill -2 59789
void abort(void);
功能:可以向进程发送SIGABRT信号(6号信号),使进程异常终止,并关闭刷新进程打开的流
头文件:
哪一个进程调用该函数,便向该进程传送SIGABRT信号(6号信号)
其实abort内部封装了kill函数
进程的task_struct中定义了位图的初始定义
struct task_struct {
...
struct sigpending pending;
...
}
内核源码的include\linux\signal.h中sigpending
struct sigpending {
struct list_head list;
sigset_t signal;
};
内核源码的 include\asm-generic\signal.h中定义了sigset_t
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;
内核源码的 include\asm-generic\signal.h中定义了**_NSIG_WORDS**
#define _NSIG 64
#define _NSIG_BPW __BITS_PER_LONG
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
内核源码的 arch\alpha\include\asm\bitsperlong.h中定义了**__BITS_PER_LONG**
#define __BITS_PER_LONG 64
一番检查源码之后发现位图就是一个unsigned long sig[1]
Linux操作系统中long占8个字节,即64位
每一个信号,在该位图中存在一个与之对应的比特位
当信号对应的比特位为1时,表示当前进程接收到该信号
前提:当前进程收到了一个非可靠信号
前提:当前进程收到一个可靠信号
struct sigqueue {
struct list_head list;
int flags;
siginfo_t info;
struct user_struct *user;
};
前提:信号已经处理完
前提:信号已经处理完了
#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */
#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
SIG_DFL
就是 __sighandler_t结构体类型 的0典型的忽略处理方式:
僵尸进程的产生原因:子进程先于父进程退出,子进程向父进程发送了一个SIGCHLD信号,父进程接收到了该SIGCHLD信号,但是选择了忽略处理的方式,导致了子进程的退出资源未被父进程进行回收,进而导致子进程变成了僵尸进程
#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
SIG_IGN
就是 __sighandler_t结构体类型 的1自定义 信号处理方式 函数:程序员定义一个函数 去处理接收到的信号
typedef void (*sighandler_t)(int); // void handler(int)
sighandler_t signal(int signum, sighandler_t handler);
功能:当进程接收到了signum信号时,调用handler函数,执行handler函数中的代码,该信号以前需要执行的任务不再执行
头文件:
参数:
#include
#include
#include
void handler(int signum)
{
printf("接收到了2信号!\n");
}
int main()
{
signal(2,handler);
sleep(10);
return 0;
}
[gongruiyang@localhost signalinterface]$ ./test
^C接收到了2信号!
在Main进程在sleep时,按下ctrl + c组合键,向前台进程main发送了一个2号信号,此时Main函数调用Handler函数,进行对信号的处理
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); //预留信息
};
sa_handler
:函数指针,保存了内核对信号的处理方式:默认处理方式和忽略处理方式sa_sigaction
:函数指针,保存的时自定义处理函数sa_mask
:信号集位图,保存收到的信号sa_flags
:填入宏宏 | 含义 |
---|---|
SA_SIGINFO | 操作系统在处理信号的时候,调用的就是sa_sigaction函数指针当中保存的函数 |
0 | 操作系统在处理信号的时候,调用的就是sa_handler函数指针当中保存的函数 |
#include
#include
#include
void handler(int signum)
{
printf("signum : %d\n",signum);
}
int main()
{
struct sigaction act;
sigemptyset(&(act.sa_mask)); //用于将位图全部位 置为0
act.sa_flags = 0; //0表示 使用自定义函数
act.sa_handler = handler; //填写 自定义函数
sigaction(2,&act,NULL);
while(1)
{
printf("Hello World!\n");
sleep(1);
}
return 0;
}
[gongruiyang@localhost signalinterface]$ ./sigActionExe
Hello World!
Hello World!
Hello World!
^Csignum : 2
Hello World!
Hello World!
Hello World!
^Csignum : 2
Hello World!
^Csignum : 2
Hello World!
Hello World!
Hello World!
ctrl + c
组合键不会将前台程序进行中断,只会执行handler函数struct sigaction {
union {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask;
int sa_flags;
};
#define sa_handler _u._sa_handler
#define sa_sigaction _u._sa_sigaction
typedef char* __user __sighandler_t;
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;
task_struct{
...
struct sighand_struct *sighand;
...
}
struct sighand_struct {
...
struct k_sigaction action[_NSIG];
...
};
struct k_sigaction {
struct sigaction sa;
...
};
struct sigaction {
union {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask;
int sa_flags;
};
#define sa_handler _u._sa_handler
#define sa_sigaction _u._sa_sigaction
#ifdef CONFIG_64BIT
/* function pointers on 64-bit parisc are pointers to little structs and the
* compiler doesn't support code which changes or tests the address of
* the function in the little struct. This is really ugly -PB
*/
typedef char __user *__sighandler_t;
#else
typedef void __signalfn_t(int);
typedef __signalfn_t __user *__sighandler_t;
#endif
#ifdef CONFIG_64BIT
/* function pointers on 64-bit parisc are pointers to little structs and the
* compiler doesn't support code which changes or tests the address of
* the function in the little struct. This is really ugly -PB
*/
typedef char __user *__sighandler_t;
#else
typedef void __signalfn_t(int);
typedef __signalfn_t __user *__sighandler_t;
#endif
__sighandler_t
是一个函数指针(function pointer),并且很难看出来,十分的ugly刚开始没有注意看这个注释,博主就想char*咋能是函数指针呢?源码翻来覆去苦苦寻找__sighandler_t是否还有其它的宏定义,后来仔细读了一下源码注释,才豁然开朗,TMD,绝了,十分ugly
task_struct源码中定义了信号阻塞位图和信号注册位图
struct task_struct{
...
sigset_t blocked, real_blocked; // 信号阻塞位图
struct sigpending pending; // 信号注册位图 位于这个结构体内部
...
}
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:设置阻塞位图
头文件:
参数:
宏 | 含义 |
---|---|
SIG_BLOCK | 设置某个信号为阻塞 |
SIG_UNBLOCK | 解除对某个信号的阻塞 |
SIG_SETMASK | 替换阻塞位图 |
SIG_BLOCK设置阻塞原理:按位或,便将新的要阻塞的信号加入了阻塞位图中 (原阻塞位图 | 新阻塞位图)
SIG_UNBLOCK解除阻塞原理:按位与,便将要解除阻塞的信号的比特位变成0(原阻塞位图 & 新阻塞位图)
#include
#include
#include
void handler(int signum)
{
printf("signum : %d\n",signum);
}
int main()
{
signal(2,handler);
signal(40,handler);
sigset_t set;
sigfillset(&set); // 位图全部置为1
sigprocmask(SIG_SETMASK,&set,NULL);
while(1)
{
printf("Hello!\n");
sleep(1);
}
return 0;
}
该程序运行起来后,该进程阻塞了所有信号除了 kill -9 其他信号都没用