信号

     #include

     kill -l :查看系统定义的信号列表。其中,1-31 普通信号(可能丢失),34-64 实时信号(不会丢失) --- 关注  当前信号有无产生

        [1]   SIGHUP   :  中止信号

          [2]   SIGINT    : 终止进程 Ctrl +c(前台终止)    Ctrl+\

     [3] SIGQUIT  : 终止进程 并 Core Dump 

     [9] SIGKILL  : 杀掉进程

     [18] SIGCONT : 继续进程

     [19] SIGSTOP : 停止进程

     用户执行信号处理动作,称为 捕捉 该信号(自定义 该信号)。

     Core Dump 可以使用gdb进行调试 查找 错误源。

     实际执行信号的处理动作:忽略,执行默认动作,自定义动作。


一、产生信号: 

 1、终端按键  ---  只能发给前台进程

     Ctrl+c   Ctrl+\   

 2、调用系统函数

kill 函数可以给一个指定的进程发送指定的信号。

raise 函数可以给当前进程发送指定的信号(自己给自己发信号)。

   #include

   #include

        int kill(pid_t pid, int sig);

                kill() : _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

         int raise(int signo);

    返回值:成功返回0,错误返回-1。

abort函数 使当前进程接收到SIGABRT信号而异常终止(保证成功) 。

     #include

     void abort(void);

     就像 exit 函数一样, abort 函数总是会成功的,所以  没有返回值

 3、软件条件产生

     #include

     unsigned int alarm(unsigned int seconds);  //预设闹钟,seconds后终止进程

     调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发 [14]SIGALRM 信号, 该信号的默认处理动作是终止当前进程

     返回值: 0    或者    以前设定的闹钟时间还余下的秒数。

     seconds=0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。

#include
#include
#include
int main()
{
	int count=6;
	alarm(1);
	while(1)
	{
		printf("%d\n",count);
		count++;
	}
	return 0;
}

运行结果:

(1)alarm(1):设置闹钟,1s后终止进程

        wKioL1chgsyjCaYPAAAMYYQkNEU757.jpg

(2)alarm(0):相当于取消以前设定的闹钟

        wKiom1chgdHjx13DAAAMl7nAHnA945.jpg

二、阻塞信号

1、内核下信号表示

     信号递达(Delivery) :实际执行信号的处理动作 (忽略,执行默认动作,自定义动作) 。

     常见 信号递达 方式(3种):忽略,执行默认动作,自定义动作(捕捉信号)。

     信号未决(Pending) :信号从产生到递达之间的状态 。

     进程可以选择阻塞(Block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才 执行递达的动作。

    注意:信号被 阻塞不会递达,而 忽略是在递达之后可选的一种处理动作

    每个信号 都有两个标志位分别表示 阻塞(block)和 未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达 才清除该标志。

    PCB

         

     block(信号阻塞):是否阻塞(状态)  // sigset_t block;

     pending(信号未决):记录当前信号是否产生(有无)  // sigset_t pending;

     handler:函数指针  表示处理动作 

          SIG_DFL:缺省 终止进程

          SIG_IGN:忽略 ignore

     一个信号,没有 pending,就不 block。

     未决和阻塞标志可以用相同的数据类型 sigset_t来存储,sigset_t称为信号集,

     阻塞信号集也叫做当前进程的 信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为 阻塞 而不是忽略。

2、信号集操作函数

     #include

     int sigemptyset(sigset_t *set);  //初始化set指向的信号集,使对应bit 清零

     int sigfillset(sigset_t *set);   //初始化set指向的信号集,使对应bit 置位

     int sigaddset(sigset_t *set, int signo); //添加有效信号

     int sigdelset(sigset_t *set, int signo); //删除有效信号

     返回值:成功返回 0,出错返回 -1 。

     int sigismember(const sigset_t *set, int signo);   //判断信号是否在信号集中

     返回值:若包含则返回 1, 不包含则返回 0, 出错返回 -1。

     

     在使用sigset_t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。

3、sigprocmask   // 读取或更改进程的信号屏蔽字(阻塞信号集)

       #include

       int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

       返回值:若成功则为0,若出错则为-1。

     如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则 更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,则:

    linux 信号 --- 产生 阻塞 捕捉_第1张图片

4、sigpending

      #include

      int sigpending(sigset_t *set);

      sigpending 读取 当前进程的未决信号集,通过set参数传出。

      返回值:调用成功则返回0,出错则返回-1。


#include
#include

void show_pending(sigset_t *sig_list)
{
	if(sig_list)
	{
		int i=0;
		while(i++ < 32)
		{
			if(sigismember(sig_list,i))
			{
				printf("1");
			}
			else
			{
				printf("0");
			}
			if(i%8 == 0)
			{
				printf(" ");
			}
		}
	}
	printf("\n");
}
void handler(int sig)
{
	printf("catched you!!! %d\n",sig);
}

int main()
{
	sigset_t sig_set;
	sigset_t sig_set_old;
	sigset_t sig_pending;
	sigemptyset(&sig_set);  //init *set 0
	sigemptyset(&sig_set_old);
	sigemptyset(&sig_pending);
	sigaddset(&sig_set,SIGINT);  //add valid signal 
	sigprocmask(SIG_SETMASK,&sig_set,&sig_set_old);  //read blocking
	
        signal(2,handler);
	int done=0,count=6;
	while(!done)
	{
		sigpending(&sig_pending); //read pending
		show_pending(&sig_pending);
		sleep(1);
		count--;
		if(count < 0)
		{
			sigprocmask(SIG_SETMASK,&sig_set_old,NULL); //reback blocking_old
		}
	}

	return 0;
}

运行结果:

    

    阻塞是一种状态,pending是信号的有无,handler表示处理动作。没有pending,就不能blocking。

三、捕捉信号

    如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为 捕捉信号


    信号的处理过程自定义动作

    linux 信号 --- 产生 阻塞 捕捉_第2张图片

地址空间 页表 内核地址空间 【0-3G用户使用】

内核模式:PSW寄存器(处理器状态字)2bit描述当前模式:  ----内核页表

03Linux 05Unix ---- 用户模式(非零)---- 自定义页表

1、内核如何实现信号的捕捉

   由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:

   1)用户程序注册了SIGQUIT信号的处理函数sighandler。

   2)当前正在执行main函数,这时发生中断或异常切换到内核态。

   3)在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。

   4)内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。

   5)sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。

   6)如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

2、 sigaction    // 读取和修改与指定信号相关联的处理动作  

     #include

     int sigaction(int signo, const struct sigaction *act, struct

                    sigaction *oact);      //signo:指定信号的编号  

     调用成功则返回 0,出错则返回 -1。

#include
#include
#include
#include

void handler(int sig)
{
	printf("I am coming,that signal: %d\n",sig);//sigaction
}
int main()//sigaction   
{
	struct sigaction act;
	struct sigaction old;
	act.sa_handler=handler;
	act.sa_flags=0;
	sigemptyset(&act.sa_mask);
	memset(&old,'\0',sizeof(old));

	signal(2,handler);
	sigaction(SIGINT,&act,&old);
	while(1)
	{
		printf("I am wating a signal...\n");
		sleep(1);
	}
	return 0;
}

运行结果:

    wKioL1chp0PDORtKAAAoko548rM715.jpg

3、 pause         //使调用进程挂起直到有信号递达

     #include

     int pause(void);

   如果信号的处理动作是  终止进程,则 进程终止, pause函数没有机会返回;

   如果信号的处理动作是  忽略,则 进程继续处于挂起状态, pause不返回;

   如果信号的处理动作是  捕捉,则 调用了信号处理函数之后 pause返回-1,errno设置为EINTR("被信号中断"), 所以pause只有出错的返回值.

     没有返回值,若返回 则 出错:exec(程序替换)   pause(进程挂起)  


//利用 pause() 和 alarm(_time) 模拟 sleep(seconds)

#include
#include
#include
#include

void handler(int sig)
{
//      DO Nothing //my_sleep
}
int my_sleep(int _time)
{
	struct sigaction act;
	struct sigaction old;
	act.sa_handler=handler;
	act.sa_flags=0;
	sigemptyset(&act.sa_mask);

	sigaction(SIGALRM,&act,&old);
	alarm(_time);
	pause();
	int ret=alarm(0);
	sigaction(SIGALRM,&old,NULL);
	return ret;
}
int main()
{
	int i=0;
	while(1)
	{
		my_sleep(1);
		printf("1s passed:%d\n",i++);
	}
	return 0;
}

运行结果:

     linux 信号 --- 产生 阻塞 捕捉_第3张图片