操作系统与系统编程(3)——信号


目录

信号:

信号产生方式:

进程处理信号方式:

信号捕捉:

时序竞态

SIGCHLD信号:

      写一个mysleep函数

      SIGCHLD信号通知父进程回收子进程


信号:

 

信号产生方式:

软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

进程处理信号方式:

当进程获得调度机会后,从内核态返回到用户态之前要做很多事情,其中一件事就是将 mask 位图和 padding 位图进行 & 运算,当计算的结果不为 0 时就需要调用相应的信号处理函数或执行信号的默认动作

 

未决信号集:1表示收到这个信号,0表示没收到

阻塞信号集:1表示信号可以响应,0表示信号不可以响应。

1)屏蔽某个信号,只需将对应的 mask 位置为 0 即可。这样当程序从内核态返回用户态进行 mask & padding 时,该信号位的计算结果一定为 0。

2)信号从收到到响应是存在延迟的。因为只有程序被打断并且重新被调度的时候才有机会发现收到了信号,当我们按下 Ctrl+C 时程序并没有立即挂掉,只不过这个时间非常短暂,我们自己以为程序是立即挂掉了。

3)当一个信号没有被处理,无论再次接受到多少个相同的信号都只能保留一个,因为 padding是位图,位图的特点就是只能保留最后一次的状态。如果想不丢失信号就只能使用实时信号了。(实时信号会按照先到先响应的顺序处理,并且信号会排队,不会丢失。信号是否排队、是否丢失,不取决于使用哪个函数,而是取决于使用哪种信号。实时信号采用链式结构实现的。其它方面与标准信号没有区别)

4)信号处理函数的执行时间越短越好,因为信号处理函数是在用户态执行的,执行过程中也会不停的被内核打断,所以如果信号处理函数执行的时间过长会使情况变得复杂。

5)信号的响应是嵌套执行的。就是说假设进程先收到了 SIGINT 信号,当它的信号处理函数还没有执行完毕时又收到了另一个信号 SIGQUIT,那么当进程从内核态返回到用户态时会优先执行 SIGQUIT 的信号处理函数,等 SIGQUIT 的信号处理函数执行完毕后再回到 SIGINT 信号处理函数上次被打断时的地方继续执行,函数调用栈看上去就像在 SIGINT 的信号处理函数中调用了 SIGQUIT 的信号处理函数一样。这也是上面所说的为什么信号处理函数的执行时间要越短越好,要尽量避免这种复杂的情况发生。

6)同时到来多个优先级差不多的信号,无法保证优先响应哪个信号,它们的响应没有严格意义上的顺序。除非是收到了优先级较高的信号,系统会保证高优先级的先被处理。

信号捕捉:

Sigreturn返回内核

时序竞态

   其中sigsuspend()12是原子操作。

信号集函数 sigprocmask(更改当前进程信号集) sigpending(读取未决信号集)

一个发生中断导致永久挂起的sleep函数

如果在“alarm(nsecs); 设置定时”和 “pause(); 挂起进程”之间由于时间片被用完,导致该进程被调出(这时alarm函数已经被执行,内核已经开始计时),而且在该系统中进程数很多,导致“饥饿现象”,所以该进程很久没有被调入,假设三秒都没有被掉入(我们设置的定时是2秒),这时在内核中其实在它被调入前已经倒数完毕并发出SIGALRM信号,所以执行它的信号处理函数(什么都不做),然后它在第三秒后被调入,继续执行,这时执行的函数是pause(),进程被挂起,但是又由于alarm发出的SIGALRM信号在之前已经被执行,所以pause()会永远等待不到SIGALRM信号的到达,那么该进程会永远的被挂起! 

 

SIGCHLD信号:

产生条件:

三个宏函数: 处理

写一个mysleep函数

/*************************************************************************
	> File Name: dm10_sleepAdv.c
	> Author: ma6174
	> Mail: ma6174@163.com 
	> Created Time: 2018年09月06日 星期四 22时40分42秒
 ************************************************************************/

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>

void sig_alarm(int n)
{
	//do nothing
}

unsigned int mysleep(int nsecs)
{
    struct sigaction newact,oldact;
	sigset_t new_set,old_set,susp_set;
		unsigned int unslept;

	//set handler and save previous inf
	newact.sa_handler = sig_alarm;
	sigemptyset(&newact.sa_mask);
	newact.sa_flags = 0;
	sigaction(SIGALRM,&newact,&oldact);

	//block SIGALRM and save old sigset_t
	sigemptyset(&new_set);
	sigaddset(&new_set,SIGALRM);
	sigprocmask(SIG_BLOCK,&new_set,&old_set);

	alarm(nsecs);

	susp_set = old_set;
	sigdelset(&susp_set,SIGALRM);
	sigsuspend(&susp_set);
	//
	unslept = alarm(0);

	sigaction(SIGALRM,&oldact,NULL);//reset previous action
	sigprocmask(SIG_SETMASK,&old_set,NULL);//reset previous UN_BLOCK
	return unslept;

}

int main(int argc,char*argv[])
{
	int n,m;
	if(argc < 2)
	{
		printf("./sleepAdv n seconds\n");
		exit(1);
	}
	m = atoi(argv[1]);

	while(1)
	{
	    n = mysleep(m);
     	printf("should sleep %d seconds\n",m);
     	printf("unslept is %d\n",n);
	}
	return 0;
}

SIGCHLD信号通知父进程回收子进程

/*************************************************************************
	> File Name: dm11_SIGCHLD.c
	> Author: ma6174
	> Mail: ma6174@163.com 
	> Created Time: 2018年09月07日 星期五 16时04分58秒
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<signal.h>
#include<sys/wait.h>

void do_sig(int signo)
{
	pid_t pid;
	int status;
	while((pid = waitpid(0, &status, WNOHANG))>0)
	   {
	           if (WIFEXITED(status)) 
				   {
                       printf("exited, status=%d\n", WEXITSTATUS(status));
                   } 
				   else if (WIFSIGNALED(status)) 
				   {
                       printf("killed by signal %d\n", WTERMSIG(status));
                   } 
				   else if (WIFSTOPPED(status))
				   {
                       printf("stopped by signal %d\n", WSTOPSIG(status));
                   }
		}

}

void sys_err(char *str)
{
	perror(str);
	exit(1);
}


int main()
{
    pid_t pid;
	int i;
	for(i = 0;i< 10;i++)
	{
	   if((	pid =fork()) == 0)
		   break;
	   else if(pid < 0)
	       sys_err("fork:");
	}

	if(pid == 0)
	{
		int n = 18;
		while(n--)
		{
			printf("Child pid is %d\n ",getpid());
			sleep(1);
		}
		return i;
	}

	if(pid > 0)
	{
		struct sigaction act;

		act.sa_handler = do_sig;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		sigaction(SIGCHLD,&act,NULL);

		while(1)
		{
			printf("Parent pid is %d\n",getpid());
			sleep(1);
		}
	}
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值