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;
}
這裏可以不使用信號的機制來逐個字母輸出試試。