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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章