APUE學習筆記—— 信號實現系統sleep和system函數,解決進程競爭實例

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


發佈了101 篇原創文章 · 獲贊 26 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章