unix高级环境编程 例子 代码实现练习 第十章:信号

程序清单 10-1 捕捉SIGUR1和SIGUSR2的简单程序

/**
 * 程序清单 10-1 捕捉SIGUR1和SIGUSR2的简单程序 P241
 *
 * zy:
 * 这两个信号用于用户随意使用
 *
 * 另外在测试中使用了kill命令,注意kill只是发送信号的命令,而不是真的杀死
 * 
 */


#include "error.c"
#include "apue.h"

static void sig_usr(int signo){
	if(signo==SIGUSR1){
		printf("接受到SIGUSR1\n");
	}else if(signo==SIGUSR2){
		printf("接受到SIGUSR2\n");
	}else{
		err_dump("received signal %d\n",signo);
	}
}


int main() {
	if(signal(SIGUSR1,sig_usr)==SIG_ERR){
		err_sys("can't catch SIGUSR1");
	}
	if(signal(SIGUSR2,sig_usr)==SIG_ERR){
		err_sys("can't catch SIGUSR2");
	}
	while(1){
		pause(); //Suspend the process until a signal arrives.
	}

}


结果:

asd@asd-desktop:~/workspace/test/src$ gcc test2.c -g
asd@asd-desktop:~/workspace/test/src$ ./a.out &
[1] 5077
asd@asd-desktop:~/workspace/test/src$ kill -USR1 5077
接受到SIGUSR1
asd@asd-desktop:~/workspace/test/src$ kill -USR2 5077
接受到SIGUSR2
asd@asd-desktop:~/workspace/test/src$

程序清单 10-2 在信号处理程序中调用不可重入的函数

/**
 * 程序清单 10-2 在信号处理程序中调用不可重入的函数 P247
 *
 * zy:
 * 信号来了会打断正在执行的函数,
 * 当处理完信号处理程序后,
 * 再返回执行刚被打断的函数时,会发生异常情况,
 * 这是因为某些函数就不能被打断
 *
 * 比如getpwnam函数,根据name返回指向passwd的指针,那么其是将结果放入一个静态数据结构
 * 如果信号处理函数也执行这个getpwnam函数,那么结果将是不可预测的。
 *
 * 我出现的结果非常怪,
 * 首先不是每次运行SIGALRM都会有效果,也就是my_alarm函数不是每次都执行
 * 其次,即使掉用了my_alarm函数,但是执行完了
 * printf("in signal handler \n");之后就没有了,
 * 我个人怀疑是my_alarm中的getpwnam根本没有执行成功,然后后面那句
 * printf("执行完了getpwnam");
 * 从调试里面来看是执行了,但是根本没有在控制台输出
 */


#include "error.c"
#include "apue.h"
#include "pwd.h"


static void my_alarm(int signo){
	struct passwd *rootptr;

	printf("in signal handler \n");
	if((rootptr=getpwnam("root"))==NULL){
		err_sys("getpwnam(root) error");
	}
	printf("执行完了getpwnam");
	alarm(1);

}

int main() {
	struct passwd *ptr;
	signal(SIGALRM,my_alarm);

	alarm(1);
	while(1){
		if((ptr=getpwnam("asd"))==NULL){
			err_sys("getpwnam(root) error");
		}
		if(strcmp(ptr->pw_name,"asd")!=0){
			printf("return value corrupted!,pw_name =%s passwd=%s\n",ptr->pw_name,ptr->pw_passwd);
		}else{
			printf("return value correct!,pw_name =%s passwd=%s\n",ptr->pw_name,ptr->pw_passwd);
		}
	}

}



程序清单 10-3 不能正常工作的系统V SIGCLD处理程序

/**
 * 程序清单 10-3 不能正常工作的系统V SIGCLD处理程序 P249
 *
 * zy:
 * 由于操作错误,导致我之前写的这个没有保存好。
 * 所以我在这里再简单的重复一下。
 *
 * SIGCLD和SIGCHLD在我看来就类似,只是SIGCLD是旧的,书上建议我们用新的SIGCHLD
 *
 * 下面的代码主要是可能会引起一个错误,但是这个错误,在linux版本上没有的。
 *
运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
接收到 SIGCLDpid = 6404
asd@asd-desktop:~/workspace/test/src$

 * 没有反复调用,一次就结束了
 */



#include "error.c"
#include "apue.h"
#include <sys/wait.h>
static void sig_cld(int signo){
	pid_t pid;
	int status;

	printf("接收到 SIGCLD");

	if(signal(SIGCLD,sig_cld)==SIG_ERR) //这句在wait处理子进程的前面,就是一个错误
										//因为现在有没有处理的子进程,所以它会立刻又调用sig_cld,
										//其实这个子进程就是第一次引发这个信号处理程序执行的子进程
										//那么就会反复运行
										//需要把这一段放在wait函数的后面,处理完子进程之后再注册

		perror("signal error");

	if((pid=wait(&status))<0)
		perror("wait error");
	printf("pid = %d \n",pid);
}
int main() {
	pid_t pid;

	if(signal(SIGCLD,sig_cld)==SIG_ERR){
		perror("signal error");
	}
	if((pid = fork())<0){
		perror("fork error");
	}else if(pid ==0 ){
		sleep(2);
		_exit(0); 	//P178解释了_exit()和exit的区别
					//_exit是个系统调用 而且不会运行终止处理函数和信号处理函数
	}
	pause();		/*parent*/
	exit(0);

}

程序清单 10-4 sleep的简单而不完整的实现

/**
 * 程序清单 10-4 sleep的简单而不完整的实现 P253
 *
 * zy:
 * 我们在利用alarm 和pause函数来完成sleep函数的功能
 *
 * 试着运行了一下,结果是过了2秒就结束了,没什么问题。
 *
 * 但是这段实现过程3个残缺,具体请看书吧
 *
 */



#include "error.c"
#include "apue.h"
#include "signal.h"
#include "unistd.h"

static void sig_alrm(int signo){
	/*什么都不做,直接返回就可以了*/
}
unsigned int sleep1(unsigned int nsecs){
	if(signal(SIGALRM,sig_alrm)==SIG_ERR){
		return (nsecs);
	}
	alarm(nsecs);//多少秒之后将执行sig_alrm
	pause();	//堵塞至产生一个信号。
	return (alarm(0));
}


int main() {
	sleep1(2);

}

程序清单 10-5 sleep的另一个完善的实现

/**
 * 程序清单 10-5 sleep的另一个完善的实现 P253
 *
 * zy:
 * 主要是防止alram()和pause()函数之间的竞争。
 * 如果再调用了alram()切换了进程,直到alram()超时了,
 * 再执行pause时,此时,SIGALRM的信号已经发过了,所以pause会一直堵塞
 *
 * 下面的代码能够解决这个问题
 * 只要能够执行到sig_alrm就可以跳过pause函数,无论其执行或者是没有执行。
 */



#include "error.c"
#include "apue.h"
#include "signal.h"
#include "unistd.h"
#include "setjmp.h"
static jmp_buf env_alrm;


static void sig_alrm(int signo){
	longjmp(env_alrm,1);
}
unsigned int sleep2(unsigned int nsecs){
	if(signal(SIGALRM,sig_alrm)==SIG_ERR){
		return (nsecs);
	}
	if(setjmp(env_alrm)==0){
		alarm(nsecs);//多少秒之后将执行sig_alrm
		pause();	//堵塞至产生一个信号。
	}
	return (alarm(0));
}


int main() {
	sleep2(2);

}

程序清单 10-6 在捕捉其他信号的程序中调用了sleep2

/**
 * 程序清单 10-6 在捕捉其他信号的程序中调用了sleep2 P254
 *
 * zy:
 * sleep2,就是10-5的函数有个问题,
 * 就是当有一个信号处理程序正在处理的时候,
 * 闹钟超时了,那么执行sig_alrm的代码,再接着执行,就结束了整个程序
 * 信号处理程序的代码就没有被执行完。
 * 下面的代码就是这一个过程。
 *
 * 运行结果:
 *
asd@asd-desktop:~/workspace/test/src$ ./a.out
^C
sig_int starting
sleep2 returned : 0
asd@asd-desktop:~/workspace/test/src$

 * 可以看出没有运行完打断的信号处理程序,整个程序就返回了
 */



#include "error.c"
#include "apue.h"
#include "signal.h"
#include "unistd.h"
#include "setjmp.h"

static jmp_buf env_alrm;


static void sig_alrm(int signo){
	longjmp(env_alrm,1);
}
unsigned int sleep2(unsigned int nsecs){
	if(signal(SIGALRM,sig_alrm)==SIG_ERR){
		return (nsecs);
	}
	if(setjmp(env_alrm)==0){
		alarm(nsecs);//多少秒之后将执行sig_alrm
		pause();	//堵塞至产生一个信号。
	}
	return (alarm(0));
}

static void sig_int(int signo){
	int i,j;
	volatile int k; //K&R 188页,对volatile有介绍
					//不过百度百科介绍的更好一些:作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
					//从内存中直接读值

	printf("\nsig_int starting\n");

	for(i=0;i<300000;i++)		//这一段的作用只是为了运行时间能够超过5秒
		for(j=0;j<4000;j++)
			k+=i*j;

	printf("sig_int finished\n");
}

int main() {
	unsigned int unslept;
	if(signal(SIGINT,sig_int)==SIG_ERR)
		err_sys("signal (SIGINT) error");
	unslept=sleep2(2);

	printf("sleep2 returned : %u\n",unslept);
	exit(0);
}

程序清单 10-7 具有超时限制的read调用

/**
 * 程序清单 10-7 具有超时限制的read调用 P255
 *
 * zy:
 * 可以利用arlarm函数设置一个时间上限,超过了这个时间,某件事还没做完就不做了
 *
 * 比如对一个设备进行读操作,读的很慢,但是我们不能等,如果读了10秒还没读完就算了,
 * 没读完就没读完,但是已经等不起了
 *
 * 但是有些系统调用是会自动重启的,也就是这个read函数会在信号处理程序执行完之后自动重启
 * 所以在这样的情况下,这段代码是无效的。
 */


#include "error.c"
#include "apue.h"

static void sig_alrm(int signo){
	/*什么都不做,仅仅就是为了能够打断现有的操作*/
}


int main() {
	int n;
	char line[MAXLINE];


	if(signal(sig_alrm,sig_alrm)==SIG_ERR)
		err_sys("signal (SIGINT) error");
	alarm(10);//我们最多让这个设备读10秒
	if((n=read(STDIN_FILENO,line,MAXLINE))<0){
		err_sys("read error");
	}
	alarm(0);//取消闹钟,剩余值返回


	write(STDOUT_FILENO,line,n);
	exit(0);
}

程序清单 10-8 使用longjmp,带超时限制调用read

/**
 * 程序清单 10-8 使用longjmp,带超时限制调用read P256
 *
 * zy:
 * 下段代码提供了一种方法,可以让一个慢速系统调用无论是否被自动重启
 * 我们都能打断它
 *
 * 但是与其他信号有着交互问题,在这样的情况下,我们的建议是所用select或者poll函数,以后会讲
 * 运行结果:

asd@asd-desktop:~/workspace/test/src$ ./a.out
read timeout
asd@asd-desktop:~/workspace/test/src$


 */

#include "error.c"
#include "apue.h"
#include <setjmp.h>

static jmp_buf env_alrm;


static void sig_alrm(int signo){
	longjmp(env_alrm,1);
}

int main() {
	int n;
	char line[MAXLINE];

	if(signal(SIGALRM,sig_alrm)==SIG_ERR)
		err_sys("signal (SIGINT) error");
	if(setjmp(env_alrm)!=0){	//返回到此步之后还是可以接着往下
		err_quit("read timeout");
	}

	alarm(5);//我们最多让这个设备读10秒
	if((n=read(STDIN_FILENO,line,MAXLINE))<0){
		err_sys("read error");
	}
	alarm(0);//取消闹钟,剩余值返回

	write(STDOUT_FILENO,line,n);
	exit(0);
}

程序清单 10-9 sigaddset,sigdelset和sigismemer的实现

/**
 * 程序清单 10-9 sigaddset,sigdelset和sigismemer的实现 P257
 *
 * zy:
 * 这代码主要是实际几个函数,具体实际会不会拿来用我还不清楚
 * 因为本身也实现了这样的api的
 * 几个函数的含义可以看书,不过从名字来看,也很容易明白
 * 此外,使用了按位或、按位与等方式,
 * c++ primer书上那个例子应该让你对这个问题理解比较到位了
 * 就不多罗嗦了
 */


#include <signal.h>
#include <errno.h>
#include "error.c"
#include "apue.h"


#define sigemptyset(ptr) (*(ptr)=0)
#define sigfillset(ptr) (*(ptr)=~(sigset_t)0,0)//sigset_t在32位的系统中确实是4个字节,也就是32个bit位
#define SIGBAD(signo) ((signo)<=0||(signo)>=NSIG)


int sigaddset(sigset_t *set,int signo){
	if(SIGBAD(signo)){
		errno=EINVAL;
		return(-1);
	}
	*set |= 1 << (signo-1);//将其打开,减1的问题,不重要,我在书上写清楚了
							//重要的是,要减必须都减,要么都不要减
	return 0;
}


int sigdelset(sigset_t *set,int signo){
	if(SIGBAD(signo)){
		errno=EINVAL;
		return(-1);
	}
	*set &= ~(1 << (signo-1));
	return 0;
}


int sigdelset(sigset_t *set,int signo){
	if(SIGBAD(signo)){
		errno=EINVAL;
		return(-1);
	}
	return ((*set&(1<<(signo-1)))!=0);
}

程序清单 10-10 为进程打印信号屏蔽字段

/**
 * 程序清单 10-10 为进程打印信号屏蔽字段 P259
 *
 * zy:
 * 打印调用进程的信号屏蔽字段中的信号的名称
 * 10-14 和 10-15会调用这个函数
 */

#include <signal.h>
#include <errno.h>

void pr_mask(const char *str){
	sigset_t sigset;
	int errno_save;

	errno_save = errno;// 书上给了句注释,但是我还没有领悟其含义

	if(sigprocmask(0,NULL,&sigset)<0){//看书上,我写的很清楚。主要现在还是觉得多看书才舒服啊
		err_sys("sigprocmask error");
	}

	printf("%s",str);

	if(sigismember(&sigset,SIGINT)) printf("SIGINT ");
	if(sigismember(&sigset,SIGQUIT)) printf("SIGQUIT ");
	if(sigismember(&sigset,SIGUSR1)) printf("SIGUSR1 ");
	if(sigismember(&sigset,SIGALRM)) printf("SIGALRM ");

	//当然我们还可以添加更多的信号在这里

	printf("\n");
	errno=errno_save;
}

程序清单 10-11 信号设置和sigprocmask实例 

/**
 * 程序清单 10-11 信号设置和sigprocmask实例 P260
 *
 * zy:
 * 使用前面几个清单里的函数
 *
 * 注意即使我们多次产生SIGQUIT的信号,但是解除了对该信号的堵塞后
 * 也只会得到一个SIGQUIT的信号
 *
运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
^\^\
SIGQUIT is pendmask
caught SIGQUIT
SIGQUIT unblocked
^\Quit (core dumped)


 */


#include "apue.h"
#include "error.c"
static void sig_quit(int signo){
	printf("caught SIGQUIT\n");
	if(signal(SIGQUIT,SIG_DFL)==SIG_ERR){/*将其信号处理程序设置为默认*/
		err_sys("can't reset SIGQUT\n");
	}
}


int main(void){
	sigset_t newmask,oldmask,pendmask;
	if(signal(SIGQUIT,sig_quit)==SIG_ERR){
		err_sys("can't catch SIGQUIT\n");
	}
	/*本实验我们屏蔽掉SIGQUIT信号并且存储当前的信号状态*/
	sigemptyset(&newmask);
	sigaddset(&newmask,SIGQUIT);


	if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0){
		err_sys("SIGBLOCK error\n");
	}


	sleep(5);


	if(sigpending(&pendmask)<0){
		err_sys("sigpending error\n");
	}
	if(sigismember(&pendmask,SIGQUIT)) {
		printf("SIGQUIT is pendmask\n");
	}


	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){
		err_sys("SIG SETMASK error");
	}
	printf("SIGQUIT unblocked\n");


	sleep(5);


	exit(0);


}

程序清单 10-12 用sigaction实现的signal函数 

/**
 * 程序清单 10-12 用sigaction实现的signal函数 P265
 *
 * zy:
 * 反正呢,仔细看书,所有问题书上都说的很清楚
 * 我也就是敲敲代码
 */

#include "apue.h"
#include "error.c"

Sigfunc *signal(int signo,Sigfunc *func){
	struct sigaction act,oact;
	act.sa_handler=func;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;
	if(signo==SIGALRM){	/*不能让闹钟的信号重启,这是为了之前对io操作设置的时间限定*/
#ifdef SA_INTERRUPT
		act.sa_flags|=SA_INTERRUPT;
#endif
	}else{
#ifdef SA_RESTART
		act.sa_flags|=SA_RESTART;/*系统调用会重新启动*/
#endif
	}
	if(sigaction(signo,&act,&oact)<0){
		return(SIG_ERR);
	}
	return (oact.sa_handler);
}

程序清单 10-13 signal_intr函数 

/**
 * 程序清单 10-13 signal_intr函数 P265
 *
 * zy:
 * 这个版本试图阻止任何被中断的系统调用自己重启动
 */

#include "apue.h"
#include "error.c"

Sigfunc *signal(int signo,Sigfunc *func){
	struct sigaction act,oact;
	act.sa_handler=func;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;
#ifdef SA_INTERRUPT
		act.sa_flags|=SA_INTERRUPT;
#endif
	if(sigaction(signo,&act,&oact)<0){
		return(SIG_ERR);
	}
	return (oact.sa_handler);
}

 程序清单 10-14 信号屏蔽字段、sigsetjmp和siglongjmp实例

/**
 * 程序清单 10-14 信号屏蔽字段、sigsetjmp和siglongjmp实例
 *
 * zy:
 * 主要与sigjmp和longjmp的区别就是:
 * 因为在信号处理函数中,引起这个函数的信号会被屏蔽掉
 * 那么如果在longjmp里面跳出了之后,这个被屏蔽掉的信号就有问题撒
 *
 * 那么sigsetjmp和siglongjmp可以解决问题,然后sigsetjmp可以保存当前屏蔽字段
 * siglongjmp再恢复
 * 运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out &
[1] 2693
asd@asd-desktop:~/workspace/test/src$ starting mian:

kill -USR1 2693
starting sig_usr1:

asd@asd-desktop:~/workspace/test/src$
asd@asd-desktop:~/workspace/test/src$
asd@asd-desktop:~/workspace/test/src$ in sig_alrm:


asd@asd-desktop:~/workspace/test/src$ finishing sig_usr1:

ending mian:


[1]+  Done                    ./a.out
asd@asd-desktop:~/workspace/test/src$
 * 结果似乎很不如意
 * 看了很久,看出为什么打印不出当前线程的屏蔽字段
 */

#include "apue.h"
#include "error.c"
#include <setjmp.h>
#include <time.h>
#include <signal.h>

static void sig_usr1(int),sig_alrm(int);/*居然还可以用逗号分开*/
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;/*sig_atomic_t,对这个访问是原子访问*/

void pr_mask(const char *str){
    sigset_t sigset;
    int errno_save;

    errno_save = errno;// 书上给了句注释,但是我还没有领悟其含义

    if(sigprocmask(0,NULL,&sigset)<0){//看书上,我写的很清楚。主要现在还是觉得多看书才舒服啊
        err_sys("sigprocmask error");
    }

    printf("%s",str);

    if(sigismember(&sigset,SIGINT)) printf("SIGINT ");
    if(sigismember(&sigset,SIGQUIT)) printf("SIGQUIT ");
    if(sigismember(&sigset,SIGUSR1)) printf("SIGUSR1 ");
    if(sigismember(&sigset,SIGALRM)) printf("SIGALRM ");

    //当然我们还可以添加更多的信号在这里

    printf("\n");
    errno=errno_save;
}
int main(void){
	if(signal(SIGUSR1,sig_usr1)==SIG_ERR){
		err_sys("signal(SIGUSR1) error");
	}
	if(signal(SIGALRM,sig_alrm)==SIG_ERR){
		err_sys("signal(sig_alrm) error");
	}
	pr_mask("starting mian: \n");

	if(sigsetjmp(jmpbuf,1)){
		pr_mask("ending mian: \n");
		exit(0);
	}
	canjump = 1;

	while(1)
		pause();
}
static void sig_usr1(int signo){
	time_t starttime;
	if(canjump==0){
		return;				//这是一种手段,防止sigsetjmp还没执行,信号就来了
							//这是在调用sigsetjmp和siglongjmp常用的手段
	}
	pr_mask("starting sig_usr1: \n");

	alarm(3);
	starttime=time(NULL);	//获取当前的系统时间,返回的结果是一个time_t类型,
							//其实就是一个大整数,其值表示从时间1970年1月1日00:00:00到当前时刻的秒数。
							//这是一个C函数标准库的调用

	for( ; ;){				//只是让其运行时间超过5秒
		if(time(NULL)>starttime+5){
			break;
		}
	}

	pr_mask("finishing sig_usr1: \n");

	canjump=0;
	siglongjmp(jmpbuf,1);	//可以看出第二个1将会是sigsetjmp函数的返回值

}

static void sig_alrm(int signo){
	pr_mask("in sig_alrm:  \n");
}

程序清单 10-15 保护临界区不被信号中断

/**
 * 程序清单 10-15 保护临界区不被信号中断 P269
 *
 * zy:
 * 使用一种技术可以完成这个功能:保护临界区不被信号中断
 * 这里使用到了一个函数
 * sigsuspend,他在一个原子操作内完成设置现有屏蔽字段和让进程休眠等待信号的到来
 * 并且能够在返回时,将屏蔽字段复原
 * 运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
program start:
in critical region: SIGINT
^C
 in sig_alrm: SIGUSR1
after return from sigsuspend: SIGINT
program exit:
asd@asd-desktop:~/workspace/test/src$

 * 唯一觉得奇怪的语句就是,in sig_alrm:SIGUSR
 * 这是因为按理在信号处理程序中,引发这个程序的信号也应该被屏蔽
 * 但是看起来在信号处理程序中没有屏蔽这个SIGINT的信号。
 */


#include "apue.h"
#include "error.c"
#include <signal.h>

void pr_mask(const char *str){
    sigset_t sigset;
    int errno_save;

    errno_save = errno;// 书上给了句注释,但是我还没有领悟其含义

    if(sigprocmask(0,NULL,&sigset)<0){//看书上,我写的很清楚。主要现在还是觉得多看书才舒服啊
        err_sys("sigprocmask error");
    }

    printf("%s",str);

    if(sigismember(&sigset,SIGINT)) printf("SIGINT ");
    if(sigismember(&sigset,SIGQUIT)) printf("SIGQUIT ");
    if(sigismember(&sigset,SIGUSR1)) printf("SIGUSR1 ");
    if(sigismember(&sigset,SIGALRM)) printf("SIGALRM ");

    //当然我们还可以添加更多的信号在这里

    printf("\n");
    errno=errno_save;
}


static void sig_int(int signo){
	pr_mask("\n in sig_alrm: ");
}

int main(void){
	sigset_t newmask,oldmask,waitmask;
	pr_mask("program start: ");

	if(signal(SIGINT,sig_int)==SIG_ERR){
		err_sys("signal(SIGINT) error ");
	}
	sigemptyset(&waitmask);
	sigaddset(&waitmask,SIGUSR1);

	sigemptyset(&newmask);
	sigaddset(&newmask,SIGINT);

	/**
	 * 堵塞SIGINT
	 */
	if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0){
		err_sys("SIG_BLOCK error");
	}

	/*
	 * 请在这里写下临界区代码,肯定不会被SIGINT信号打断
	 */

	pr_mask("in critical region: ");

	/*
	 * 现在进程休眠,在一个原子操作内重新设置了屏蔽字段,
	 * 这次只有SINGUSR1的信号,那么就可以处理之前来的SIGINT
	 * 而当该函数返回时,就设置为调用之前的屏蔽字段
	 */
	if(sigsuspend(&waitmask)!=-1){
		err_sys("sigsuspend error");
	}

	pr_mask("after return from sigsuspend: ");


	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){
		err_sys("SIG_SETMASK error");
	}

	pr_mask("program exit:  ");
	exit(0);
}

程序清单 10-16 用SIGSUSPEND等待一个全局变量被设置

/**
 * 程序清单 10-16 用SIGSUSPEND等待一个全局变量被设置 P271
 *
 * zy:
 * 从程序的运行来看确实了完成上述功能,
 * 但是我觉得上述功能的意义不是那么大呀
 * 运行结果:
 *
asd@asd-desktop:~/workspace/test/src$ ./a.out
quitflag:0
^C
interrupt
quitflag:0
^C
asd@asd-desktop:~/workspace/test/src$

 * 不太和书上的一样,不知道为什么在我键入第二个中断键的之后
 * 就退出程序,我觉得代码应该是没有问题的
 */


#include "apue.h"
#include "error.c"

volatile sig_atomic_t quitflag;//这就是那个常用来被设置的全局变量

void pr_mask(const char *str){
    sigset_t sigset;
    int errno_save;

    errno_save = errno;// 书上给了句注释,但是我还没有领悟其含义

    if(sigprocmask(0,NULL,&sigset)<0){//看书上,我写的很清楚。主要现在还是觉得多看书才舒服啊
        err_sys("sigprocmask error");
    }

    printf("%s",str);

    if(sigismember(&sigset,SIGINT)) printf("SIGINT ");
    if(sigismember(&sigset,SIGQUIT)) printf("SIGQUIT ");
    if(sigismember(&sigset,SIGUSR1)) printf("SIGUSR1 ");
    if(sigismember(&sigset,SIGALRM)) printf("SIGALRM ");

    //当然我们还可以添加更多的信号在这里

    printf("\n");
    errno=errno_save;
}


static void sig_int(int signo){
	if(signo == SIGINT){
		printf("\ninterrupt\n");
	}else if(signo==SIGQUIT){
		quitflag=1;
	}
}



int main(void){
	sigset_t newmask,oldmask,zeromask;

	if(signal(SIGINT,sig_int)==SIG_ERR){
		err_sys("signal(SIGINT) error ");
	}
	if(signal(SIGQUIT,sig_int)==SIG_ERR){
		err_sys("signal(SIGINT) error ");
	}
	sigemptyset(&zeromask);
	sigemptyset(&newmask);
	sigaddset(&newmask,SIGQUIT);

	if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0){
		err_sys("SIG_BLOCK error");
	}
	/*
	 * 之前是把SIGQUIT给堵塞了的
	 * 下面那句执行了之后是会接受到SIGQUIT的
	 */
	while(quitflag==0){
		sigsuspend(&zeromask);
	}
	/*
	 * 接收到了SIGQUIT了,并且SIGQUIT现在开始也会堵塞
	 */
	quitflag=0;

	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){
		err_sys("SIG_SETMASK error");
	}

	exit(0);
}

程序清单 10-17 用SIGSUSPEND等待一个全局变量被设置

/**
 * 程序清单 10-17 用SIGSUSPEND等待一个全局变量被设置 P272
 *
 * zy:
 * 从8.9节对下面这个TELL_WAIT的使用机制来看
 * 有个关键点,就是并不会同时调用TELL_PARENT或者是TELL_CHILD,
 * 在一个程序里,只会出现一对:
 * WAIT_PARENT和TELL_CHILD
 *
 * WAIT_CHILD和TELL_PARENT
 *
 */


#include "apue.h"
#include "error.c"


static volatile sig_atomic_t sigflag;
static sigset_t newmask,oldmask,zeromask;

static void sig_usr(int signo){
	sigflag=1;
}
/**
 * 发动这个TELL_WATI体系
 */
void TELL_WAIT(){
	if(signal(SIGUSR1,sig_usr)==SIG_ERR){
		err_sys("signal(SIGINT) error ");
	}
	if(signal(SIGUSR2,sig_usr)==SIG_ERR){
		err_sys("signal(SIGINT) error ");
	}
	sigemptyset(&zeromask);
	sigemptyset(&newmask);
	sigaddset(&newmask,SIGUSR1);
	sigaddset(&newmask,SIGUSR2);

	//现在堵塞好SIGUSR1和SIGUSR2,并且存储现在的状态
	if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0){
		err_sys("SIG_BLOCK error");
	}
}
/**
 * 孩子调用这个代码,告诉父亲,我已经搞定了
 */
void TELL_PARENT(pid_t pid){
	kill(pid,SIGUSR2);
}

/**
 * 父亲调用这个代码,告诉儿子,我已经搞定了
 */
void TELL_CHILD(pid_t pid){
	kill(pid,SIGUSR1);
}

/*
 * 儿子要等父亲干完
 */
void WAIT_PARENT(){
	while(sigflag==0){
		sigsuspend(&zeromask);
	}
	sigflag=0;

	/**
	 * 将阻塞信号复原
	 */
	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){
		err_sys("SIG_SETMASK error");
	}
}
/*
 * 父亲要等儿子干完
 */
void WAIT_CHILD(){
	while(sigflag==0){
		sigsuspend(&zeromask);
	}
	sigflag=0;

	/**
	 * 将阻塞信号复原
	 */
	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){
		err_sys("SIG_SETMASK error");
	}
}

程序清单 10-17 abort函数的POSIX.1实现

/**
 * 程序清单 10-17 abort函数的POSIX.1实现 P276
 *
 * zy:
 * 如题所属,注意这个函数主要是产生的是SIGABRT信号
 * 而且不能阻塞,所以程序很大的程序上都在完成这个功能
 */


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

void abort(){
	sigset_t mask;
	struct sigaction action;
	/*
	 * 进程不能忽略掉这个SIGABRT信号,如果忽略掉了,先把动作恢复至默认动作
	 */
	sigaction(SIGABRT,NULL,&action);
	if(action.sa_handler==SIG_IGN){ //SIG_IGN就是signal Ignore
		action.sa_handler=SIG_DFL;
		sigaction(SIGABRT,&action,NULL);
	}
	if(action.sa_handler==SIG_DFL){
		fflush(NULL);
	}
	/*
	 * 调用进程不能堵塞SIGABRT信号,如果堵塞,就其设置为未堵塞
	 */
	sigfillset(&mask);			/*除了SIGABRT都屏蔽掉*/
	sigdelset(&mask,SIGABRT);
	sigprocmask(SIG_SETMASK,&mask,NULL);
	kill(getpid(),SIGABRT);

	/**
	 * 如果到了这里,说在SIGABRT的信号处理函数没有执行exit之类的函数
	 * 重复一次的原因是为了可能进程产生了更多的输出,所以再一次冲洗所有流
	 */

	fflush(NULL);
	action.sa_handler==SIG_DFL;
	sigaction(SIGABRT,&action,NULL);
	sigprocmask(SIG_SETMASK,&mask,NULL);
	kill(getpid(),SIGABRT);

	exit(1);//书上写着这句永远都不会被执行,我没有搞明白这句的意思。
}

程序清单 10-19 用system调用ed编辑器

/**
 * 程序清单 10-19 用system调用ed编辑器 P276
 *
 * zy:
 * 这里,我们只想证明一件事,那就是调用了system的进程应该忽略掉
 * SIGINT和SIGQUIT信号,堵塞掉SIGCHLD信号。
 * 这是因为SIGINT信号会发送给前台进程组,图10-2说明了
 * 我们执行的./a.out和/bin/sh和/bin/ed都是前台进程组
 * 所以调用了system的进程作为父进程,应该屏蔽掉SIGINT和SIGQUIT信号
 *
 * 这里我们使用之前写的sytem的函数,这个函数没有信号相关的处理
 * 我把其命名为Mysystem函数
 *
 * 第一次运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
a
Here is one line of text
.
1,$p
Here is one line of text
w temp.foo
25
q
asd@asd-desktop:~/workspace/test/src$


 * 书上共运行了两次,我第一次与书上运行不太一样,
 * 关键就是最后我在输入q离开编辑器的时候
 * 没有打印caught SIGCHLD
 *
 * 第二次运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
a
hello,world
.
1,$p
hello,world
w temp.foo
12
^C
?
caught SIGCHLD
^Ccaught SIGCHLD

?
^Ccaught SIGCHLD

?
^Ccaught SIGCHLD

?
q
asd@asd-desktop:~/workspace/test/src$

 * 不过从第二次的运行来看,在我输入中断的情况,
 * 父进程应该是收到了SIGCHLD的信号,
 * 但是却没有收到 SIGINTd的信号。不知道是不是因为linux的版本与书中不符合造成的。
 *
 * 不过尽管如此,我也对调用system的函数应该忽略什么有点粗浅的认识
 */


#include "sys/wait.h"
#include "error.c"
#include "apue.h"
#include "unistd.h"
#include <errno.h>

int mySystem(const char * cmdstring){
    pid_t pid;
    int status;

    if(cmdstring==NULL){
        return(1);
    }
    if((pid=fork())<0){
        status=-1;
    }else if(pid==0){//child
        execl("/bin/sh","sh","-c",cmdstring,(char *)0);
        _exit(127);
    }else{//parent
        while(waitpid(pid,&status,0)<0){
            if(errno!=EINTR){
                status=-1;
                break;
            }
        }
    }
    return status;
}
static void sig_int(int signo){
	printf("caught SIGINT\n");
}
static void sig_chld(int signo){
	printf("caught SIGCHLD\n");
}

int main(){
	if(signal(SIGINT,sig_int)==SIG_ERR){
		err_sys("signal SIGINT error");
	}

	if(signal(SIGINT,sig_chld)==SIG_ERR){
		err_sys("signal SIGCHLD error");
	}
	if(mySystem("/bin/ed")<0){
		err_sys("system() error");
	}
	exit(0);
}

程序清单 10-20 system函数的posi.x正确实现

/**
 * 程序清单 10-20 system函数的posi.x正确实现 P278
 *
 * zy:
 * 代码中已经很明显了,
 * 父进程 也就是调用system的函数
 * 忽略掉了两个信号SIGINT、SIGQUIT和堵塞了一个信号
 *
 *
 */

#include "sys/wait.h"
#include "unistd.h"
#include <errno.h>
#include <signal.h>

int system(const char * cmdstring){
    pid_t pid;
    int status;
    struct sigaction ignore,saveintr,savequit;
    sigset_t chldmask,savemask;

    if(cmdstring == NULL){
    	return 1;
    }
    ignore.__sigaction_handler=SIG_IGN;
    sigemptyset(&ignore.sa_mask);
    ignore.sa_flags=0;

    if(sigaction(SIGINT,&ignore,&saveintr)<0){ //忽略掉了信号SIGINT
    	return(-1);
    }
    if(sigaction(SIGQUIT,&ignore,&savequit)<0){ //忽略掉了信号SIGQUIT
    	return(-1);
    }

    sigemptyset(&chldmask);
    sigaddset(&chldmask,SIGCHLD);

    if(sigprocmask(SIG_BLOCK,&chldmask,&savemask)<0){ //屏蔽掉信号
    	return -1;
    }

    if((pid=fork())<0){
    	status=-1;
    }else if(pid==0){ 	//对于孩子来说,我们不需要屏蔽什么和忽略什么
    					//所以复原
    	sigaction(SIGINT,&saveintr,NULL);
    	sigaction(SIGQUIT,&savequit,NULL);
    	sigprocmask(SIG_SETMASK,&savemask,NULL);
    	execl("/bin/sh","sh","-c",cmdstring,(char *)0);
    	_exit(127);
    }else {
    	while(waitpid(pid,&status)<0){
    		if(errno!=EINTR){	//这里为什么这样没搞清
    			status=-1;
    			break;
    		}
    	}
	}
    /**
     * 恢复之前的信号处理函数和屏蔽字段
     */
    if(sigaction(SIGINT,&saveintr,NULL)<0){
    	return (-1);
    }
    if(sigaction(SIGQUIT,&savequit,NULL)<0){
    	return (-1);
    }
    if(sigprocmask(SIG_SETMASK,&savemask,NULL)<0){
    	return (-1);
    }
    return (status);
}

程序清单 10-21 sleep函数的可靠实现

/**
 * 程序清单 10-21 sleep函数的可靠实现 P281
 *
 * zy:
 * 与10-4和10-5的版本相比,可以正确处理信号和避免了早期的竞争条件
 *
 */

#include "apue.h"
#include <error.c>

static void sig_alrm(int signo){
    /*什么都不做,直接返回就可以了*/
}
unsigned int sleep(unsigned int nsecs){
	struct sigaction newact,oldact;
	sigset_t newmask,oldmask,suspmask;
	unsigned int unslept;

	newact.__sigaction_handler=sig_alrm;
	sigemptyset(&newact.sa_mask);
	newact.sa_flags=0; 	//P262
	sigaction(SIGALRM,&newact,&oldact);

	sigemptyset(&newmask);
	sigaddset(&newmask,SIGALRM);
	sigprocmask(SIG_BLOCK,&newmask,&oldmask);

	alarm(nsecs);

	suspmask=oldmask;
	sigdelset(&suspmask,SIGALRM);	//现在的信号屏蔽字段是susmask
	sigsuspend(&suspmask);			//现在除了SIGALRM信号来了会返回以外,
									//其他的信号不会返回
	/**
	 * 当sigsuspend函数返回后,
	 * 会恢复对SIGALRM信号的堵塞
	 *
	 * 其他信号都能被正常处理
	 */
	unslept=alarm(0);	//0的含义就不解释了,可以自己去unistd.h头文件里去看
	sigaction(SIGALRM,&oldact,NULL);

	sigprocmask(SIG_SETMASK,&oldmask,NULL);
	return(unslept);
}

程序清单 10-22 如何处理SIGTSTP

代码不可运行,我就不抄了。是一个小框架

其它问题总结

ubuntu系统下创建软件桌面快捷方式



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值