exit.c

764阅读 0评论2009-08-04 jhluroom
分类:LINUX

/*
 * linux/kernel/exit.c
 *
 * (C) 1991 Linus Torvalds
 */


#include <errno.h>
#include <signal.h>
#include <sys/wait.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>

int sys_pause(void);//将进程置为睡眠状态,直到收到信号

int sys_close(int fd);//关闭指挥文件的系统调用


void release(struct task_struct * p)//释放指定进程占用的任务槽以及任务结构占用的内存页面。

{
    int i;

    if (!p)
        return;
    for (i=1 ; i<NR_TASKS ; i++)//NR_TASKS=64,扫描任务数组,找到指定任务

        if (task[i]==p) {
            task[i]=NULL;//本任务置空

            free_page((long)p);//清内存

            schedule();//重新调度,在这里可能没有必要重新调度,不知作者为何要重新调度

            return;
        }
    panic("trying to release non-existent task");
}

static inline int send_sig(long sig,struct task_struct * p,int priv)//向指定任务*p发送信号sig,置权限为priv,当priv为1时为强制发送    

{
    if (!p || sig<1 || sig>32)
        return -EINVAL;
    if (priv || (current->euid==p->euid) || suser())//强制发送标志位||当前进程 与指定进程相同也就是自己||为超级用户current->euid=0,(当前进程 的有效用户ID为0时,表示为超级用户)

        p->signal |= (1<<(sig-1));
    else
        return -EPERM;
    return 0;
}

static void kill_session(void)//终止会话

{
    struct task_struct **p = NR_TASKS + task;//将p指向任务的末端

    
    while (--p > &FIRST_TASK) {
        if (*p && (*p)->session == current->session)//除任务0外,其会话ID等于当前任务的会话ID时,

            (*p)->signal |= 1<<(SIGHUP-1);//发送挂断进程信号,同时注意一下发送信号的方法

    }
}

/*
 * XXX need to check permissions needed to send signals to process
 * groups, etc. etc. kill() permissions semantics are tricky!
 */

int sys_kill(int pid,int sig)//向指定进程或进程组发送任何信号,并非杀死进程,(除进程号0外)

{
    struct task_struct **p = NR_TASKS + task;//将p指向任务数组的末端,最多64个任务

    int err, retval = 0;

    if (!pid) while (--p > &FIRST_TASK) {//pid=0时,信号发送于以当前进程为领头的进程 组中的所有进程 。

        if (*p && (*p)->pgrp == current->pid) //其进程组号==当前进程 的进程号,(进程组号一定等于这个进程组中的某一个进程的进程号,面这个进程就是这个进程组的领头进程 ,这个进程的进程号就是这个进程组的组号)

            if (err=send_sig(sig,*p,1))
                retval = err;
    } else if (pid>0) while (--p > &FIRST_TASK) {//pid>0时,信号发送于pid的进程

        if (*p && (*p)->pid == pid)
            if (err=send_sig(sig,*p,0))
                retval = err;
    } else if (pid == -1) while (--p > &FIRST_TASK)//pid=-1,信号发送于除第一个进程(初始进程init)外的所有进程

        if (err = send_sig(sig,*p,0))
            retval = err;
    else while (--p > &FIRST_TASK)//pid<-1,信号发送于进程组为pid的所有进程

        if (*p && (*p)->pgrp == -pid)
            if (err = send_sig(sig,*p,0))
                retval = err;
    return retval;
}

static void tell_father(int pid)//通知父进程,向进程pid 发送信号SIGCHLD,默认情况下子进程停止或终止

{
    int i;

    if (pid)
        for (i=0;i<NR_TASKS;i++) {//扫描整个任务数组,64个任务

            if (!task[i])
                continue;
            if (task[i]->pid != pid)
                continue;
            task[i]->signal |= (1<<(SIGCHLD-1));
            return;
        }
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
    printk("BAD BAD - no father found\n\r");
    release(current);//如果没有找到父进程,则释放自己,这样做并不好,应该有进程1来处理

}

int do_exit(long code)//程序退出处理函数

{
    int i;

    free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));//释放当前进程代码段的内存页,current->ldt[1]代码段,current->ldt[0]没用

    free_page_tables(get_base(current->ldt[2]),get_limit(0x17));//释放当前进程数据和堆栈段的内存页,current->ldt[2]数据堆栈段,current->ldt[0]没用

    for (i=0 ; i<NR_TASKS ; i++)
        if (task[i] && task[i]->father == current->pid) {//如果当前进程有子进程,则将该子进程的父进程置为1

            task[i]->father = 1;
            if (task[i]->state == TASK_ZOMBIE)//如果该子进程处于ZOMBIE状态,则向task[1](init进程)发送SIGCHLD信号

                /* assumption task[1] is always init */
                (void) send_sig(SIGCHLD, task[1], 1);
        }
    for (i=0 ; i<NR_OPEN ; i++)//NR_OPEN=20,关闭当前进程打开的所有文件

        if (current->filp[i])
            sys_close(i);
    iput(current->pwd);//对当前进程的pwd root executable进行同步操作,放回并分别置空

    current->pwd=NULL;
    iput(current->root);
    current->root=NULL;
    iput(current->executable);
    current->executable=NULL;
    if (current->leader && current->tty >= 0)//当前进程为会话领头,且其有控制终,则释放终端

        tty_table[current->tty].pgrp = 0;
    if (last_task_used_math == current)//last_task_used_math协处理器指针,当前进程上次使用过协处理器,则将last_task_used_math置空

        last_task_used_math = NULL;
    if (current->leader)//当有进程为会话领头时

        kill_session();//终止会话

    current->state = TASK_ZOMBIE;//将当前进程状态置为TASK_ZOMBIE,表明当前里程已释放了资源

    current->exit_code = code;// 任务退出执行程序的退出码,保存有父进程提取的退出码

    tell_father(current->father);//通知父进程

    schedule();//进程调度

    return (-1);    /* just to suppress warnings */
}

int sys_exit(int error_code)//系统调用exit,终止进程

{
    return do_exit((error_code&0xff)<<8);
}

int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)//挂起当前进程,直到pid指定的子进程退出终止或者收到要求终止该进程 的信号 ,或者需要调用 一个信号处理函数

{
    int flag, code;
    struct task_struct ** p;

    verify_area(stat_addr,4);//验证区域大小是否可用

repeat:
    flag=0;
    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
        if (!*p || *p == current)//空项,本进程项跳过

            continue;
        if ((*p)->father != current->pid)//不是当前进程的子进程跳过,

            continue;
        //以下的语名定为当前进程 的子进程

        if (pid>0) {//pid>0,等待指定的进程pid

            if ((*p)->pid != pid)//不是当前要找的子进程,跳过

                continue;
        } else if (!pid) {//pid==0    等待进程组号等于当前进程组号的任何子进程

            if ((*p)->pgrp != current->pgrp)//扫描 的进程组号与当前的进程组号不相等,跳过

                continue;
        } else if (pid != -1) {// pid<-1等待指定进程pid 绝对值的进程

            if ((*p)->pgrp != -pid)//不是当前要找的子进程,跳过

                continue;
        }
        //以下的语名定为要找到的进程

        switch ((*p)->state) {
            case TASK_STOPPED://子进程非正常结束

                if (!(options & WUNTRACED))//参数 option 可以为 0 或WNOHANG(如果没有任何已经结束的子进程则马上返回, 不予以等待)或 WUNTRACED (如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会),如果WUNTRACED没有置位,无需产即返回,可继续扫描其他进程

                    continue;
                put_fs_long(0x7f,stat_addr);//如果WUNTRACED置位,则写入状态信息,返回此子进程 号

                return (*p)->pid;
            case TASK_ZOMBIE://子进程非正常结束

                current->cutime += (*p)->utime;//用户态运行时间累计到当前进程(父进程中)

                current->cstime += (*p)->stime;//系统态运行时间累计到当前进程(父进程中)

                flag = (*p)->pid;    //取子进程号

                code = (*p)->exit_code;//取子进程的退出码

                release(*p);//释放该子进程

                put_fs_long(code,stat_addr);//置状态信息为退出码值

                return flag;
            default://找到一个符合要求的子进程,它处于运行态或睡眠态,flag=1

                flag=1;
                continue;//这个地方是否可以换成break 语名???

        }
    }
    if (flag) {
        if (options & WNOHANG)//如果options==WNOHANG(如果没有任何已经结束的子进程则马上返回, 不予以等待)时,则立即返回

            return 0;
        current->state=TASK_INTERRUPTIBLE;//置当前进程为可中断等待状态

        schedule();//重新进程调度,重新调度后,当又轮到本程序进行时,则从下面的语句开始进行

        if (!(current->signal &= ~(1<<(SIGCHLD-1))))//如果本程序还没有收到子进程发过来的SIGCHLD信号,则重新开始查找

            goto repeat;
        else
            return -EINTR;
    }
    return -ECHILD;
}
//说明:LINE 190行的重新任务调度,是很有必要的,这样一任务调度,父进程就

//有可能暂时不运行,转让于子进程加以运行,这样就有可能子进程运行结束,

//当轮到父进程运行时,正好执行if (!(current->signal &= ~(1<<(SIGCHLD-1))))语句,

//也就是说能够收到子进程传过来的SIGCHLD信号,从而返回-EINTR(4),

//也就是等待到了子进程的结束


上一篇:signal.c
下一篇:int sys_waitpid(pid_t pid,unsigned long * stat_add