ECF中的信号、并发与跳转详解 (二)

Linux提供阻塞信号的隐式和显式机制。

对于隐式机制,内核默认阻塞任何当前处理程序正在处理信号类型的待处理信号。

对于显式机制,程序可以通过sigprocmask函数与它的辅助函数,明确地阻塞与解除阻塞选定的信号。

#include 

int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signum);
int sigdelset(sigset_t *set,int signum);    //成功返回0,失败返回-1

int sigismember(const sigset_t *set,int signum); //signum是set成员返回1,不是返回0,出错返回-1

sigemptyset初始化set为空集合,sigfillset把每个信号都添加到set中,sigaddset把signum添加到set,sigdelset从set集合中删除signum。

下面代码展示如何利用sigprocmask来临时阻塞接受SIGINT信号:

sigset_t mask,prev_mask;
sigemptyset(&mask);
sigaddset(&mask,SIGINT);
sigprocmask(SIG_BLOCK,&mask,&prev_mask);

sigprocmask(SIG_SETMASK,&prev_mask,NULL);

在编写信号处理程序时,要尽可能使用异步安全函数,像printf()与sprintf()函数是不安全的,write()函数是安全的。

可以使用SIO包中的安全函数。

在不用exit()函数退出的信号处理程序中,应该保存error的状态,并在函数结束时恢复该状态。

建议读者自行搜索并了解volatile和sig_atomic_t。

另外未处理的信号是不会排队的。因为在pending位向量中对于每一个信号只有两种状态,当第一个信号被接受后,第二个信号就只会被简单的丢弃。

核心思想是:如果存在一个未处理的信号,那么表示至少有一个信号已经到达了。

我们来看下面一个程序:

void handler1(int sig){
	int olderrno=errno;
	if((waitpid(-1,NULL,0))<0){
		sio_error("waitpid error");
	sio_puts("Handler reaped child\n");
	sleep(1);
	errno=olderrno;
}

int main(){
	int i,n;
	char buf[MAXBUF];
	if(signal(SIGCHLD,handler1)==SIG_ERR)
		unix_error("signal error");
	
	for(i=0;i<3;i++){
		if(Fork()==0){
			printf("Hello from child %d\n",(int)getpid());
			exit(0);
		}
	}
	if((n=read(STDIN_FILENO,buf,sizeof(buf)))<0)
		unix_error("read");
	
	printf("Parent processing input\n");
	while(1);
	exit(0);
}

这里其实存在一个问题,三个子进程只会有两个被回收。因为第一个信号发送后立刻被接收,第二个信号存在待处理,第三个信号会被直接抛弃。

这里可以在handler中使用while去改进:

void handler2(int sig){
	int olderrno=errno;
	while(waitpid(-1,NULL,0)>0) Sio_puts("Handler reaped child\n");
	if(errno!=ECHLD) Sio_error("waitpid error");
	Sleep(1);
	errno=olderrno;
}

你可能感兴趣的:(深入理解计算机系统)