1、实现系统sleep函数
此函数使调用进程被挂起,直到满足下列条件之一:(1)已经经过seconds所指定的墙上时钟时间(2)调用进程捕捉到一个信号并从信号处理程序返回。
以下的可靠实现并没有考虑到两个alarm交互作用的情况
可靠实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_alarm(){}
int sleep_(int seconds){
unsigned int unslept;
struct sigaction act,oact;
act.sa_handler = sig_alarm;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigset_t newmask,suspendmask,oldmask;
sigaction(SIGALRM,&act,&oact);
sigemptyset(&newmask);
sigaddset(&newmask,SIGALRM);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
alarm(seconds);
suspendmask = oldmask;
sigdelset(&suspendmask,SIGALRM);
sigsuspend(&suspendmask);//挂起,等待alarm信号
unslept = alarm(0);
sigprocmask(SIG_SETMASK,&oldmask,NULL);
return unslept;
}
int main(){
int i;
for(i = 0 ; i < 7 ; i ++){
printf("The %dth second.\n",i+1);
printf("sleep seconds %d\n",sleep_(2));
}
return 0;
}
2、实现系统system函数
POSIX.1要求system函数忽略SIGINT和SIGQUIT信号,阻塞SIGCHLD。至于原因书本上写的很清楚。下面是个人理解:
system函数的实现是运用fork and exec,来执行shell指令的,所以父进程在子进程完成之前就不能执行SIGINT和SIGQUIT信号,否则父子进程直接中断了,SIGCHLD信号是子进程完成时告诉父进程的信号,如果在子进程没有完成,父进程是不能捕捉SIGCHLD
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
int system_(const char *cmdstring){
pid_t pid;
int status;
struct sigaction ignoreact,saveintact,savequitact;
ignoreact.sa_handler = SIG_IGN;
sigemptyset(&ignoreact.sa_mask);
ignoreact.sa_flags = 0;
sigaction(SIGINT,&ignoreact,&saveintact);//saveintact是保存以前的配置,可以参见signal的返回值,所以这里也能用signal函数实现
sigaction(SIGQUIT,&ignoreact,&savequitact);
sigset_t chldmask,savemask;
sigemptyset(&chldmask);
sigaddset(&chldmask,SIGCHLD);
sigprocmask(SIG_BLOCK,&chldmask,&savemask);
if((pid = fork()) == -1){
status = -1;
}else if(pid == 0){
sigaction(SIGINT,&saveintact,NULL);//子程序中可以有SIGINT和SIGQUIT信号,所以这里重新
sigaction(SIGQUIT,&savequitact,NULL);
execl("/bin/bash","sh","-c",cmdstring,(char*)0);//下面两句在课本上顺序正好相反,这里我没想明白,我认为是应该在子进程execl完成之后才能释放SIGCHLD信号的捕获
sigprocmask(SIG_SETMASK,&savemask,NULL);
_exit(127);
}else{
while(waitpid(pid,&status,0) < 0) {//让子进程先执行
if(errno != EINTR){
status = -1;
break;
}
}
}
sigaction(SIGINT,&saveintact,NULL);//以下是父进程恢复这些信号的捕获(此时子进程已经执行完成)
sigaction(SIGQUIT,&savequitact,NULL);
sigprocmask(SIG_SETMASK,&savemask,NULL);
return status;
}
int main(){
system_("date");
system_("w");
return 0;
}
3、解决进程竞争实力
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
static volatile sig_atomic_t sigflag;
sigset_t newmask,oldmask,susmask;
void print(const char *str,pid_t pid){
printf("pid: %d, ",pid);
while(*str != '\0')
putc(*str++,stdout);
printf("\n");
}
void sig_usr(int signo){
sigflag = 1;
}
void tell_child(pid_t pid){
kill(pid,SIGUSR1);
}
void wait_parent(){
susmask = oldmask;//书本上没有添加这两行代码,个人觉得加上更加严谨点,sleep的实现就用了类似的方法
sigdelset(&susmask,SIGUSR1);//使SIGUSR1信号能使其终止挂起状态
while(sigflag == 0)
sigsuspend(&susmask);
sigflag = 0;
sigprocmask(SIG_SETMASK,&oldmask,NULL);
}
int main(){
pid_t pid;
int status;
struct sigaction act,oact;
act.sa_handler = sig_usr;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGUSR1,&act,&oact);
sigemptyset(&newmask);
sigaddset(&newmask,SIGUSR1);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
printf("%d, %d\n",newmask,oldmask);//输出newmask和oldmask,我输出的结果是512,0
if((pid = fork()) == -1){
printf("fork error");
exit (-1);
}else if(pid == 0){
wait_parent();//等待父进程先输出
print("charactor",getpid());
}else{
print("charactor",getpid());
tell_child(pid);
}
return 0;
}
这里可以不使用信号的机制来逐个字母输出试试。