信号相关概念
信号是进程间通信方式;信号是唯一的一种异步通信方式;是软件层次上中断模拟。
信号的特点
1、
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
2、
信号可以在进程与进程之间,内核与进程之间产生。
3、
信号可以被阻塞,可以延迟送达,但信号不会丢失。
信号生存周期
用户进程对信号的响应方式
1、
捕捉信号
(
响应信号处理
)。
2、
忽略信号
(
视而不见
)。
3、
执行默认处理
(
缺省操作
)。
SIGKILL
和
SIGSTOP
是保留给系统管理和调试用的,不能被捕获,也不能忽略
,
只能执行默认动作(
SIGKILL
终止进程,SIGSTOP
暂停进程)。
信号处理流程
1、信号是内核发出;
2、信号处理很快;
常用信号说明
linux 信号是固定,内核已经定义好了。
kill -l 查看系统支持的信号列表,看到 linux 的信号。
一共64种,重点前面30多种。
标准
LINUX
是一个非实时的操作系统,它不用
SIGTMIN
(
34
)到
SIGTMAX
(
64
)之间的信号。
给前台进程中的每一个进程
,
信号默认处理就是终止。
常用信号说明:
1、SIGHUP
控制终端
退出,默认操作终止;
2、SIGINT,CTRL+C,
死循环结束;
3、SIGQUIT ctrl+\ ,
死循环也终止,类似
SIGINT;
4、SIGILL 堆栈溢出;SIGFPE 5/0;
编译通过有警告
:
1、运行时出错;
2、当你除
0
内核将给你发一个信号,
SIGFPG
默认处理是终止;
3、SIGKILL
自杀信息,不能阻塞,必死无疑,只执行默认动作,杀死病毒用它;
4、SIGALRM
时钟信号,定时器到时;
5、SIGSTOP
不能忽略,暂停进程;
6、SIGCHLD
父子进程之间;
7、SIGABORT
在
abort()
调用时异常退出进程,会发出该信号;
8、SIGSEGV
用户访问了非法内存内核给进程发出的;
9、SIGUSR1/SIGUSR2
用户自定义;
10、SIGCONT、SIGSTOP
成对、功能相反;
信号相关函数
1、信号处理函数 signal
注意:
SIGKILL
和
SIGSTOP
不能安装信号处理函数。
handler
填写
SIG_IGN:
忽略相应的信号;
handler
填写
SIG_DFL:
执行默认处理。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
//信号处理函数
void sig_ctrl_c_handler (int signo)
{
if (signo == SIGINT)
printf("receive a SIGINT signal\n");
}
int main(int argc, char **argv)
{
//注册信号处理函数
signal(SIGINT, sig_ctrl_c_handler);
while (1) {
printf("sleep\n");
sleep(1);
}
return 0;
}
2、产生信号的
kill
函数
sig:是信号前的数字;用 kill -l 查看;把 sig 信号发给 pid 进程。
给指定的进程发信号,有 3
种情况:
pid > 0:
把
sig
信号发送给
pid
指定的那个进程;
pid = 0:
把信号发送给当前进程所在的进程组;
pid = -1:
给有权限发送信号的所有进程都发送
sig
信号
(pid =1
的
init
进程除外
);
pid < -1:
不用理它;
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char **argv)
{
kill(atoi(argv[1]), SIGKILL);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
int main(void)
{
//同一个程序中执行不同的进程
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
} else if (pid > 0) { //fork后父进程执行代码入口
printf("parent pid=%d \n",getpid());
printf("parent ppid=%d \n",getppid());
sleep(30);
kill(0, SIGKILL);
} else { //fork后子进程执行代码入口
printf("child pid=%d \n",getpid());
printf("child ppid=%d \n",getppid());
sleep(100);
}
return 0;
}
3、
产生信号的
raise
函数
给自己发信号,在进程中等价于:kill(getpid(), sig); 在多线程中等价于: pthread_kill(pthread_self(), sig); 给自己发信号,只能给自己玩不跟别人玩。
4、
产生信号的
alarm
函数
闹钟到时,内核会向进程发送SIGALRM信号。alarm 函数不阻塞,SIGALRM 采用默认方式自杀。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char **argv)
{
alarm(3); //不阻塞,默认动作终止进程
printf("start ...\n");
sleep(5);
printf("end...\n");
return 0;
}
5、
接收信号的 pause
函数
pause 函数使调用进程挂起直到有信号递达。 如果信号的处理动作是终止进程,则进程终止,pause 函数没有机会返回; 如果信号的处理动作是忽略,则进程继续处于挂起状态,pause 不返回;
如果信号的处理动作是捕捉,则调用了信号处理函数之后
pause
返回
-1
,
errno
设置为
EINTR
,所以
pause
只有出错的返回值,错误码EINTR
表示“被信号中断”。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
//信号处理函数
void sig_alarm (int signo)
{
if (signo == SIGALRM)
printf("receive a SIGINT signal\n");
}
int main(int argc, char **argv)
{
int ret;
//注册SIGALM信号处理函数
//signal(SIGALRM, sig_alarm);//捕捉信号
signal(SIGALRM, SIG_IGN);//忽略信号
//signal(SIGALRM, SIG_DFL); //默认
alarm(3); //不阻塞,默认动作终止进程
ret = pause(); //让当前进程暂停
if (ret==-1 && errno==EINTR) {
printf("pause() return!\n");
}
printf("bye\n");
return 0;
}
示例:一个进程W不停的打印时间,遇到进程R发送信号则执行回调函数写入文件一条时间记录;另一个进程R在死循环,遇到ctrl+c,向进程W发送一个信号。
/*
实现一个往管道写pid,输出的功能
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
//信号处理函数
void sig_foo (int signo)
{
if (signo == SIGUSR1){
printf("receive a SIGUSR1 signal\n");
FILE *fp;
time_t tm;
if((fp = fopen("data.txt", "a+"))==NULL) {
perror("fopen");
exit(1);
}
time(&tm);
fputs(ctime(&tm), fp);
//fflush(fp)
fclose(fp);
}
}
int main(int argc, char **argv)
{
int fd;
pid_t pid;
time_t tm;
pid = getpid();
signal(SIGUSR1, sig_foo);
if (argc != 2) {
printf("usage: %s <filename> \n",argv[0]);
exit(1);
}
//打开有名管道
if ((fd = open(argv[1], O_RDWR))<0) {
perror("open");
exit(1);
}
write(fd, &pid, sizeof(pid));
//write(fd, &pid, sizeof(pid));
while(1) {
time(&tm);
printf("%s", ctime(&tm));
sleep(1);
}
close(fd);
return 0;
}
/*
实现一个进程读管道,输入的功能
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
pid_t pid;
//信号处理函数
void sig_int (int signo)
{
if (signo == SIGINT){
printf("receive a SIGINT signal\n");
kill(pid, SIGUSR1);
}
}
int main(int argc, char **argv)
{
int fd;
signal(SIGINT, sig_int);
if (argc != 2) {
printf("usage: %s <filename> \n",argv[0]);
exit(1);
}
//打开有名管道
if ((fd = open(argv[1], O_RDWR))<0) {
perror("open");
exit(1);
}
read(fd, &pid, sizeof(pid));
close(fd);
while (1)
sleep(1);
return 0;
}