1.信号
信号是进程间通信的一种方式,这种方式没有数据传输。
只是在内核中传递一个信号(整数),信号的表示是一个整数。
不同的信号值,代表不同的含义,当然用户可以自定义信号。
那么自定义的信号的含义和值由程序员来定和解释
Term:Terminate 中止
signal value Action Comment
--------------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
控制终端的挂起操作,或者是控制进程死亡时,
控制终端上的所有进程都会收到 SIGHUP 信号
SIGINT 2 Term Interrupt from keyboard
从键盘上收到的中断信号, ctrl + C
SIGQUIT 3 Core Quit from keyboard
CTRL + Z
SIGILL 4 Core Illegal Instruction
非法指令
SIGABRT 6 Core Abort signal from abort(3)
调用abort这个函数时,进程会收到
SIGABRT这个信号
SIGFPE 8 Core Floating-point exception
浮点运算异常的时候,产生SIGFPE信号
SIGKILL 9 Term Kill signal 任何进程只要收到这两个信号就必死无疑
SIGSTOP 17,19,23 Stop Stop process 而且不能捕获的
SIGSEGV 11 Core Invalid memory reference
非法内存引用时,会收到SIGSEGV信号
=》segamation Fault
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers; see pipe(7)
当你往一个管道写数据时,没有读端进程时
就会产生SIGPIPE信号。
SIGALRM 14 Term Timer signal from alarm(2)
定时信号,在进程调用alarm时
会在超时的时候,产生SIGALRM信号
SIGTERM 15 Term Termination signal
终止信号
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
用户自定义信号
SIGCHLD 20,17,18 Ign Child stopped or terminated
当子进程停止或中止时,父进程会
收到SIGCHLD这个信号
SIGCONT 19,18,25 Cont Continue if stopped
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
进程在收到一个信号时,通常会有三种处理方式:
(1)默认行为
收到一个信号时,采用操作系统默认的行为。
大部分信号的默认行为,会把进程给干掉。
只有一个SIGCHLD是被忽略的。
(2)捕捉信号
把一个信号 与 用户自定义的信号处理函数关联起来。
那么在收到该信号时,就会自行调用该处理函数。
(3)忽略该信号
2.信号处理过程
通过"软件中断/软中断"来实现的,
你的信号处理函数其实是在 中断上下文 执行,
信号处理函数 -》 “软中断函数”
//信号处理函数 是 线程安全的/可重入
进程上下文:进程的大环境下,“时间片轮转”
一个进程的执行状态又分为:
用户态:在执行用户自己的代码
内核态:进入操作系统内核执行的
中断上下文:就是中断的环境下。“中断模式”
中断处理
interrupt
当一个进程接收到信号时,操作系统会对该进程进行处理和调度。以下是信号处理的一般过程:
信号传递:当进程接收到信号时,操作系统会将信号传递给进程。这是通过向进程发送一个特定的软中断(software interrupt)或触发硬件中断(hardware interrupt)来实现的。
信号处理函数调用:接收到信号的进程会根据之前对信号进行的处理注册,执行与该信号关联的信号处理函数。每个信号都有一个默认的处理行为,但进程可以选择为特定信号注册自己的信号处理函数。
信号处理:当操作系统调用信号处理函数时,进程会执行与信号关联的操作。这可能包括异常处理、资源回收、状态更新等操作。在处理完信号后,进程可以恢复执行原来的程序。
需要注意的是,操作系统对接收到信号的进程的调度方式可能会根据不同的操作系统和调度策略而有所不同。一般来说,操作系统可能会采用以下方式处理接收到信号的进程:
抢占式调度:操作系统会立即中断当前进程的执行,将控制权转移到信号处理函数。这种方式会立即响应信号,但可能会导致进程的执行中断。
非抢占式调度:操作系统会等待进程主动释放CPU控制权后,再调用信号处理函数。这种方式会等待进程处于合适的执行点,然后才会响应信号。
对于实时操作系统(Real-Time Operating System),会有更灵活的机制来处理信号和调度进程,以满足实时性的要求。
综上所述,当一个进程接收到信号时,操作系统会将信号传递给进程,并根据调度策略执行相应的信号处理函数。具体的调度方式可能会因操作系统和调度策略而有所不同
3.linux下信号相关的API函数
1.发送信号
1.kill函数
头文件
#include <sys/types.h>
#include <signal.h>
函数功能
用来把一个信号发送到一个指定的进程或多个进程
函数原型
int kill(pid_t pid, int sig);
函数参数
pid_t pid //指定信号的接收者(可能是多个进程)
pid > 0:pid表示接收者进程
pid = 0:发送信号给调用进程同组的所有进程
pid = -1:发送信号给所有进程(有权限发送的所有)
pid < -1:发送信号给组id等于 pid绝对值的所有进程
int sig //要发送的信号的信号值
函数返回值
成功: (至少有一个进程成功接收到信号)返回0
失败: 返回-1,同时errno被设置
练习:
1.写一个程序,发送一个SIGKILL 给某一个进程或多个进程
-------------------------------------------------------------
2.raise:发送信号给自己
头文件
#include <signal.h>
函数功能
发送一个信号给调用者进程
函数原型
int raise(int sig);
函数参数
int sig //要发送的信号
函数返回值
成功:返回0
失败:返回非0
《=》 kill(getpid(),sig);
---------------------------------------------------------------
3.alarm发送一个闹钟信号
头文件
#include <unistd.h>
函数功能
定时发送一个闹钟信号(SIGALRM)给本进程。
"闹钟":每个进程都有属于自己的一个“闹钟”
“闹钟”时间到了,进程就会收到SIGALRM的信号,
但是同一时刻一个进程只有一个“闹钟”生效
函数原型
unsigned int alarm(unsigned int seconds);
函数参数
unsigned int seconds //多少秒后,发送一个“闹钟信号”
0,cancal a alarm 取消一个闹钟
函数返回值
返回上一个闹钟的剩余秒数
例子:
alarm(5);
int r = alarm(10);//10秒后会收到一个"闹钟信号"
//前面的那个闹钟将替换了。
...
alarm(0); //取消闹钟
--------------------------------------------------
大部分信号的默认行为,是把收到信号的进程kill掉。
那么我们能不能改变这个默认行为呢?
改变信号的处理方式 --》 捕捉信号
(2)捕捉信号:改变信号的处理方式
使用这个函数 需要在头文件前面添加#define __GNU__SOURCE 这个宏定义
头文件
#include <signal.h>
函数功能
捕捉和改变信号的处理方式
函数原型
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数参数
int signum //要捕捉的那个信号的信号值
sighandler_t handler //sighandler_t是一个函数指针的类型
它可以用来定义函数指针变量。
信号的处理方式
(1)SIG_IGN:忽略该信号
(2)SIG_DFL:default,采用操作系统默认的处理方式
(3) 自定义的处理函数
无返回值,但是带一个int的参数(用来保存收到的那个信号的信号值)
void my_sig_handler(int sig)
{
}
函数返回值
成功: 返回 用户改变的信号处理方式的上一次的处理方式,就是指向那个处理函数 ,当我们通过
这个函数指针调用那个函数就可以看到上一次的处理方式,如果没有上一次,则指向NULL
失败: 返回SIG_ERR,同时errno被设置
练习:
我们的程序,通常可以被键盘的CTRL + C 干掉,
请写一个程序,CTRL + C 干不掉你了?
(3) 等待信号到来
pause:让进程停在那里,等待某个信号的到来。
直到收到信号。
头文件
#include <unistd.h>
函数功能
让进程停在那里,等待某个信号的到来。直到收到信号。
函数原型
int pause(void);
函数参数
无
函数返回值
只在捕捉到信号时返回。返回-1,errno被设置为EINTR。
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char * argv [ ])
{
int k = kill(2647, 19); //指定进程发送信号
if(k == -1)
{
perror("kill send failed\n");
}
/*k = kill(2853, 3);
if(k == -1)
{
perror("kill send failed\n");
}*/
return 0;
}
#include <stdio.h>
#include <signal.h>
int main(int argc, char * argv [ ])
{
int r = raise(9);
if(r != 0)
{
printf("raise failed\n");
}
while(1);
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(int argc, char * argv [ ])
{
int r = alarm(5);
printf("r =%d\n",r);
r = alarm(2);
printf("r = %d\n",r);
r = alarm(0);
printf("r = %d\n",r);
while(1);
return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//信号的新的处理方式
void signal_handler(int sig)
{
if(sig == 1)
{
printf("test last\n");
}
if(sig == 2)
{
printf("kill buliao I\n");
}
}
void signal_handlerxx(int sig)
{
if(sig == 2)
{
printf("you kill buliao I\n");
}
}
int main(int argc, char * argv [ ])
{
sighandler_t p = signal(SIGINT, signal_handler); //信号捕获
if(p == SIG_ERR)
{
perror("signal failed\n");
}
if(p == NULL)
{
printf("no last\n");
}
p = signal(SIGINT, signal_handlerxx); //信号捕获
if(p == SIG_ERR)
{
perror("signal failed\n");
}
if(p == NULL)
{
printf("no last\n");
}
else
{
p(1); //===》signal_handler(1);
}
int s = pause();
if(s == -1)
{
perror("");
}
printf("qqqqqqqqqqqq\n");
while(1);
return 0;
}
#include <stdio.h>
int main(int argc, char * argv [ ])
{
while(1);
return 0;
}