shell lab

把书中提到的,shell lab中要使用的相关函数专门拿出来记录一下。

 

pid_t waitpid(pid_t pid, int *status, int options);

返回:如果成功,返回子进程的PID,如果WNOHANG,则为0,如果其他错误,则返回-1;

options = 0(默认情况下)时,进程吊起直到等待列表中的一个子进程终止。

如果pid > 0,那么等待集合就是一个单独的子进程,子进程进程ID为pid。

如果pid = -1,那么等待集合就是由父进程所有的子进程组成的。

 

修改默认情况:
设置options为常量

WNOHANG:如果等待集合中的任何子进程还没有终止就立即返回0,以便在等待子进程终止的过程中做其他工作。

WUNTRACED:返回已终止或者被停止的子进程,默认行为是只返回已终止的子进程的PID。

WNOHANG | WUNTRACED:立即返回,如果等待集合中没有任何子进程已终止或者被停止,则返回0,否则返回相应PID。

头文件:errno.h

 

检查已回收子线程的退出状态:

 

如果调用进程没有子进程,那么waitpid会返回-1,并且设置errno为ECHILD。如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR。

头文件:errno.h

 

pid_t setpgid(pid_t pid, pid_t pgid)

将进程pid的进程组改为pgid,如果pid=0,那么这个进程就是当前进程,如果pgid=0,那么就使用pid参数为进程的id。

当进程15213是调用进程时,setpgid(0, 0)创建一个新的进程,其进程组ID是15213,并且把进程15213加入这个新的进程组中。

 

int kill(pid_t pid, int sig)

进程通过调用kill函数发送信号给其他进程(包括它们自己)

如果pid大于0,那么kill函数发送信号sig给进程pid,如果pid小于零,那么kill发送信号给进程组abs(pid)中的每个进程。

 

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

signal函数可以修改和信号相关的默认行为,唯一的例外是SIGSTOP和SIGKILL,它们的默认行为是不能被修改的。

1.如果handler是SIG_IGN,那么忽略类型为signum的信号

2.如果handler是SIG_DFL,那么类型为signum的信号恢复默认行为。

3.否则,handler就是用户定义的函数的地址,传入一个信号处理程序(signal handler),只要进程接受到一个类型为signum的信号,就会调用这个程序处理信号,

以此达到修改默认行为的目的。

当一个类型为k的信号被捕获,一个整型参数被设置为k,通过这个参数,信号处理程序可以处理不同的信号。

void handler(int sig)
{
    printf("caught SIGINT\n");
    exit(0);
}

int main()
{
    if (signal(SIGINT, handler) == SIG_ERR)
        unix_error("signal error");
    pause();
    
    exit(0);
}
sigint1

 

int sigaction(int signum, struct sigaction *act, struct sigaction *oldact);

不同系统之间,信号处理语义的差异(比如一个慢速系统调用被中断后是重启还是放弃)是Unix信号处理的一个缺陷,为了解决这个问题,Posix标准定义了sigaction函数。

sigaction函数运用并不广泛,它要求用户设置多个结构条目,一个更简洁的方案是定义一个包装函数Signal

 

 

竞争:
假设一个父进程在一个记录列表中保存着它的子进程列表,当父进程创建一个新的子进程时,它就把这个子进程添加到列表中,当父进程在SIGCHILD处理程序

中回收一个终止的子进程时,它就从列表中删除这个子进程。

那么如果父进程创建了一个子进程,子进程在父进程继续执行之前发出SIGCHILD信号,处理程序回收终止的程序并调用deletejob,这个函数什么也不做,因为

父进程还未将该子进程添加。在处理程序运行完毕之后父进程继续运行,将一个已经不存在的子进程添加进列表中。

解决方法:

在调用fork生成子进程之前就阻塞SIGCHILD信号,在调用了addjob之后再取消阻塞这个信号,这样就确保了子进程的SIGCHILD处理程序在子进程添加后才执行。

注意,由于子进程继承了父进程的被阻塞集合,所以我们要在子进程内调用的程序执行前取消阻塞SIGCHILD。

void handler(int sig)
{
    pid_t pid;
    while((pid = waitpid(-1, NULL, 0)) > 0)
        deletejob(pid);
    if(errno != ECHILD)
        unix_error("waitpid error");
}

int main(int argc, char **argv)
{
    int pid;
    sigset_t mask;
    
    Signal(SIGCHILD, handler);
    initjobs();
    
    while(1) {
        Sigemptyset(&mask);
        Sigaddset(&mask, SIGCHILD);
        Sigprocmask(SIG_BLOCK, &mask, NULL);
        
        if ((pid = Fork()) == 0) {
            Sigprocmask(SIG_UNBLOCK, &mask, NULL);
            Execve("/bin/date", argv, NULL);
        }
        
        addjob(pid);
        Sigprocmask(SIG_UNBLOCK, &mask, NULL);
    }
    exit(0);
}
procmask2

 

posted @ 2016-09-23 19:58  autoria  阅读(749)  评论(0)    收藏  举报