APUE学习笔记—— 信号实现系统sleep和system函数,解决进程竞争实例

来源:互联网 发布:美国人聊天软件 编辑:程序博客网 时间:2024/06/06 12:32

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;}
这里可以不使用信号的机制来逐个字母输出试试。


原创粉丝点击