Linux內(nèi)核學(xué)習(xí)011——進(jìn)程管理(七)

Linux內(nèi)核學(xué)習(xí)011——進(jìn)程管理(七)

進(jìn)程終結(jié)

當(dāng)一個(gè)進(jìn)程終結(jié)時(shí)淌山,內(nèi)核必須釋放其所占有的資源,并通知其父進(jìn)程顾瞻。

一般而言泼疑,進(jìn)程的結(jié)束是由自身引起的。進(jìn)程中介發(fā)生在進(jìn)程調(diào)用exit()系統(tǒng)調(diào)用時(shí)荷荤,無論是顯式調(diào)用或者是隱式地從某個(gè)程序的主函數(shù)返回退渗。當(dāng)進(jìn)程終結(jié)時(shí)移稳,主要時(shí)依靠do_exit()完成,該函數(shù)定義于Linux2.6.34//kernel/exit.c#L900会油。其主要完成以下工作:

  1. 將task_struct中的標(biāo)志成員設(shè)置為PF_EXITING秒裕,以表示進(jìn)程正在被刪除
  2. 調(diào)用del_timer_sync()刪除任一內(nèi)核定時(shí)器,根據(jù)返回的結(jié)果钞啸,它確保沒有定時(shí)器在排隊(duì)几蜻,也沒有定時(shí)器處理程序在運(yùn)行
  3. 若BSD的進(jìn)程記賬功能開啟,則調(diào)用acct_update_integrals()來輸出記賬信息
  4. 調(diào)用exit_mm()釋放進(jìn)程占用的mm_struct体斩,若沒有別的進(jìn)程還在使用梭稚,則徹底釋放
  5. 接著調(diào)用exit_sem()函數(shù),若進(jìn)程在排隊(duì)等待IPC信號(hào)絮吵,則離開隊(duì)列
  6. 調(diào)用exit_files()和exit_fs()弧烤,以分別遞減文件描述符、文件系統(tǒng)數(shù)據(jù)的應(yīng)用計(jì)數(shù)蹬敲。若其中的某個(gè)計(jì)數(shù)降為0暇昂,則可以釋放該資源、
  7. 接著將task_struct中的exit_code設(shè)置為exit()提供的退出值伴嗡,或者去完成任何其他由內(nèi)惡化機(jī)制規(guī)定的退出動(dòng)作急波,退出代碼存放在此處供父進(jìn)程檢索
  8. 調(diào)用exit_notify()向父進(jìn)程發(fā)送信號(hào),為本進(jìn)程的子進(jìn)程(若存在)尋找新的父進(jìn)程瘪校,要么是線程組中的其他線程或者為init進(jìn)程澄暮,并把進(jìn)程狀態(tài)設(shè)置為EXIT_ZOMBIE
  9. 調(diào)用schedule()切換到新的進(jìn)程。因?yàn)樘幱贓XIT_ZOMBIE狀態(tài)的進(jìn)程不會(huì)再被調(diào)度阱扬,所以這是進(jìn)程執(zhí)行的最后一段代碼

do_exit()函數(shù)如下:


NORET_TYPE void do_exit(long code)
{
    struct task_struct *tsk = current;
    int group_dead;

    profile_task_exit(tsk);

    WARN_ON(atomic_read(&tsk->fs_excl));

    if (unlikely(in_interrupt()))
        panic("Aiee, killing interrupt handler!");
    if (unlikely(!tsk->pid))
        panic("Attempted to kill the idle task!");

    tracehook_report_exit(&code);

    validate_creds_for_do_exit(tsk);

    /*
     * We're taking recursive faults here in do_exit. Safest is to just
     * leave this task alone and wait for reboot.
     */
    if (unlikely(tsk->flags & PF_EXITING)) {
        printk(KERN_ALERT
            "Fixing recursive fault but reboot is needed!\n");
        /*
         * We can do this unlocked here. The futex code uses
         * this flag just to verify whether the pi state
         * cleanup has been done or not. In the worst case it
         * loops once more. We pretend that the cleanup was
         * done as there is no way to return. Either the
         * OWNER_DIED bit is set by now or we push the blocked
         * task into the wait for ever nirwana as well.
         */
        tsk->flags |= PF_EXITPIDONE;
        set_current_state(TASK_UNINTERRUPTIBLE);
        schedule();
    }

    exit_irq_thread();

    exit_signals(tsk);  /* sets PF_EXITING */
    /*
     * tsk->flags are checked in the futex code to protect against
     * an exiting task cleaning up the robust pi futexes.
     */
    smp_mb();
    raw_spin_unlock_wait(&tsk->pi_lock);

    if (unlikely(in_atomic()))
        printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
                current->comm, task_pid_nr(current),
                preempt_count());

    acct_update_integrals(tsk);
    /* sync mm's RSS info before statistics gathering */
    if (tsk->mm)
        sync_mm_rss(tsk, tsk->mm);
    group_dead = atomic_dec_and_test(&tsk->signal->live);
    if (group_dead) {
        hrtimer_cancel(&tsk->signal->real_timer);
        exit_itimers(tsk->signal);
        if (tsk->mm)
            setmax_mm_hiwater_rss(&tsk->signal->maxrss, tsk->mm);
    }
    acct_collect(code, group_dead);
    if (group_dead)
        tty_audit_exit();
    if (unlikely(tsk->audit_context))
        audit_free(tsk);

    tsk->exit_code = code;
    taskstats_exit(tsk, group_dead);

    exit_mm(tsk);

    if (group_dead)
        acct_process();
    trace_sched_process_exit(tsk);

    exit_sem(tsk);
    exit_files(tsk);
    exit_fs(tsk);
    check_stack_usage();
    exit_thread();
    cgroup_exit(tsk, 1);

    if (group_dead)
        disassociate_ctty(1);

    module_put(task_thread_info(tsk)->exec_domain->module);

    proc_exit_connector(tsk);

    /*
     * FIXME: do that only when needed, using sched_exit tracepoint
     */
    flush_ptrace_hw_breakpoint(tsk);
    /*
     * Flush inherited counters to the parent - before the parent
     * gets woken up by child-exit notifications.
     */
    perf_event_exit_task(tsk);

    exit_notify(tsk, group_dead);
#ifdef CONFIG_NUMA
    mpol_put(tsk->mempolicy);
    tsk->mempolicy = NULL;
#endif
#ifdef CONFIG_FUTEX
    if (unlikely(current->pi_state_cache))
        kfree(current->pi_state_cache);
#endif
    /*
     * Make sure we are holding no locks:
     */
    debug_check_no_locks_held(tsk);
    /*
     * We can do this unlocked here. The futex code uses this flag
     * just to verify whether the pi state cleanup has been done
     * or not. In the worst case it loops once more.
     */
    tsk->flags |= PF_EXITPIDONE;

    if (tsk->io_context)
        exit_io_context(tsk);

    if (tsk->splice_pipe)
        __free_pipe_info(tsk->splice_pipe);

    validate_creds_for_do_exit(tsk);

    preempt_disable();
    exit_rcu();
    /* causes final put_task_struct in finish_task_switch(). */
    tsk->state = TASK_DEAD;
    schedule();
    BUG();
    /* Avoid "noreturn function does return".  */
    for (;;)
        cpu_relax();    /* For when BUG is null */
}

至此泣懊,與進(jìn)程相關(guān)聯(lián)的所有資源都被釋放掉了,此時(shí)進(jìn)程不可運(yùn)行麻惶,并處于EXIT_ZOMBIE退出狀態(tài)馍刮。它此時(shí)僅占有的內(nèi)存為內(nèi)核棧、thread_info結(jié)構(gòu)體和task_struct結(jié)構(gòu)體窃蹋。此時(shí)的進(jìn)程是為了向父進(jìn)程提供信息卡啰。當(dāng)父進(jìn)程檢索到信息或者通知內(nèi)核那是無關(guān)信息后,由進(jìn)程所持有的剩余內(nèi)存被釋放脐彩。

刪除進(jìn)程描述符

在調(diào)用do_exit()之后碎乃,進(jìn)程已經(jīng)僵死不能運(yùn)行了,但是系統(tǒng)還保留了它的進(jìn)程描述符惠奸。因此梅誓,進(jìn)程終結(jié)時(shí)所需的清理工作和進(jìn)程描述符的刪除是被分開執(zhí)行的。

wait一族函數(shù)都是通過唯一的一個(gè)系統(tǒng)調(diào)用wait4()來實(shí)現(xiàn)的。wait4()會(huì)掛起調(diào)用它的進(jìn)程梗掰,直到其中的一個(gè)子進(jìn)程退出嵌言,此時(shí)函數(shù)會(huì)返回該子進(jìn)程的PID。此外及穗,調(diào)用該函數(shù)時(shí)提供的指針包含子函數(shù)退出時(shí)的退出代碼摧茴。

當(dāng)最終需要釋放進(jìn)程描述符時(shí),會(huì)調(diào)用release_task()函數(shù)埂陆,該函數(shù)定義在Linux2.6.34/kernel/exit.c#L168完成以下工作:

  1. 調(diào)用__exit_signal()函數(shù)苛白,該函數(shù)會(huì)調(diào)用_unhash_process(),后者會(huì)調(diào)用detach_pid()從pidhash上撒謊才能胡該進(jìn)程焚虱,同時(shí)也會(huì)在任務(wù)列表中刪除該進(jìn)程购裙。
  2. _exit_signal()會(huì)釋放目前僵死進(jìn)程所使用的所有剩余資源,并進(jìn)行最終統(tǒng)計(jì)和記錄
  3. 若該進(jìn)程是線程組的最后一個(gè)進(jìn)程鹃栽,且領(lǐng)頭進(jìn)程已經(jīng)死掉躏率,那么release_task()需要通知僵死的領(lǐng)頭進(jìn)程的父進(jìn)程
  4. release_task()調(diào)用put_task_struct()釋放進(jìn)程內(nèi)核棧和thread_info結(jié)構(gòu)所占的頁,并釋放task_struct所占的高速緩存

通過上述操作民鼓,完成了釋放進(jìn)程描述符和所有進(jìn)程獨(dú)享的資源

具體代碼如下:

void release_task(struct task_struct * p)
{
    struct task_struct *leader;
    int zap_leader;
repeat:
    tracehook_prepare_release_task(p);
    /* don't need to get the RCU readlock here - the process is dead and
     * can't be modifying its own credentials. But shut RCU-lockdep up */
    rcu_read_lock();
    atomic_dec(&__task_cred(p)->user->processes);
    rcu_read_unlock();

    proc_flush_task(p);

    write_lock_irq(&tasklist_lock);
    tracehook_finish_release_task(p);
    __exit_signal(p);

    /*
     * If we are the last non-leader member of the thread
     * group, and the leader is zombie, then notify the
     * group leader's parent process. (if it wants notification.)
     */
    zap_leader = 0;
    leader = p->group_leader;
    if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) {
        BUG_ON(task_detached(leader));
        do_notify_parent(leader, leader->exit_signal);
        /*
         * If we were the last child thread and the leader has
         * exited already, and the leader's parent ignores SIGCHLD,
         * then we are the one who should release the leader.
         *
         * do_notify_parent() will have marked it self-reaping in
         * that case.
         */
        zap_leader = task_detached(leader);

        /*
         * This maintains the invariant that release_task()
         * only runs on a task in EXIT_DEAD, just for sanity.
         */
        if (zap_leader)
            leader->exit_state = EXIT_DEAD;
    }

    write_unlock_irq(&tasklist_lock);
    release_thread(p);
    call_rcu(&p->rcu, delayed_put_task_struct);

    p = leader;
    if (unlikely(zap_leader))
        goto repeat;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末薇芝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丰嘉,更是在濱河造成了極大的恐慌夯到,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件供嚎,死亡現(xiàn)場離奇詭異黄娘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)克滴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來优床,“玉大人劝赔,你說我怎么就攤上這事〉ǔǎ” “怎么了着帽?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長移层。 經(jīng)常有香客問我仍翰,道長,這世上最難降的妖魔是什么观话? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任予借,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灵迫。我一直安慰自己秦叛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布瀑粥。 她就那樣靜靜地躺著挣跋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狞换。 梳的紋絲不亂的頭發(fā)上避咆,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音修噪,去河邊找鬼查库。 笑死,一個(gè)胖子當(dāng)著我的面吹牛割按,可吹牛的內(nèi)容都是我干的膨报。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼适荣,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼现柠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弛矛,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤够吩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后丈氓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體周循,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年万俗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了湾笛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闰歪,死狀恐怖嚎研,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情库倘,我是刑警寧澤临扮,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站教翩,受9級(jí)特大地震影響杆勇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饱亿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一蚜退、第九天 我趴在偏房一處隱蔽的房頂上張望闰靴。 院中可真熱鬧,春花似錦关霸、人聲如沸传黄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膘掰。三九已至,卻和暖如春佳遣,著一層夾襖步出監(jiān)牢的瞬間识埋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工零渐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窒舟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓诵盼,卻偏偏與公主長得像惠豺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子风宁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容