天天看點

回收程序使用者空間資源 exit()函數 _exit()函數 atexit()函數 on_exit()函數

摘要:本文主要講述程序的終止方式,以及如何使用exit()函數來終止程序,回收程序使用者空間資源;分析了exit()函數與_exit()函數,return關鍵字的差異.同時詳細解讀了如何使用atexit()和on_exit()函數來注冊終止處理程式.

程序終止、回收資源

1.程序終止方式

    在核心中,程式執行的唯一方法是調用一個exec函數.而程序自願終止的唯一方法是顯示或隐式地調用_exit()或_Exit().

    程序有5種正常終止方式:

(1)常見的一種是,在main函數中執行return語句,這點是等效于調用exit().

(2)調用exit()函數,其操作包括調用終止處理程式(由atexit()或on_exit()函數注冊,下文再細細說來),然後關閉所有IO流等.

(3)調用_exit或_Exit函數.

(4)程序的最後一個線程在其啟動例程中執行傳回語句.但是,該線程的傳回值不會用作程序的傳回值.當最後一個線程從其啟動例程傳回時,該程序以終止狀态0傳回.

(5)程序的最後一個線程調用pthread_exit函數.

     3種異常終止方式:(在這裡隻做了解,不細細讨論).

(6)調用abort.它産生SIGABRT信号,這個終止方式依靠資訊傳遞機制.

(7)當程序接收到某些信号時,信号可由程序自身、其他程序或核心産生.

(8)最後一個線程對“取消”請求作出響應.

    不管程序是如何終止,最後都會執行核心中的同一段代碼.關閉所有檔案描述符,釋放他所有的存儲器等等.如:程序正常退出前需要執行注冊的退出處理函數(終止處理程式),重新整理流緩沖區等操作,然後釋放程序使用者空間.而程序控制塊PCB并不在這時釋放.僅調用退出函數的程序屬于一個僵死程序.

    僵死程序:在UNIX系統中,一個已經終止,但是其父程序尚未對其進行善後處理(擷取終止子程序的資訊,釋放所占資源)的程序稱為僵死程序.

2.exit()與return的差別

    函數exit()用于退出程序.在正式釋放資源前,将以反序的方式執行由on_exit()函數和atexit()函數注冊的清理函數(終止處理程式),同時重新整理流緩沖區.C語言關鍵字return與exit()在main函數(注意:隻是在main函數,在其他地方,是不相同的)中完成同樣的操作,但兩者有本質的差別:

(1)return退出目前函數,exit()函數退出目前程序;是以,在main函數裡,return(0)和exit(0),完成一樣的功能.

(2)return僅從子函數中傳回,并不退出程序.調用exit()時要調用一段終止處理函數,然後關閉所有的IO流.下面的例子1是專門講述這點差異的.

3.exit()函數

頭檔案:#include <stdlib>

定義函數:void exit(int status);

函數說明:

    exit()函數用來正常終止目前程序的執行,并把參數status(稱之為終止狀态)傳回給父程序,而程序所有的緩沖區資料自動寫回并關閉所有IO流.

例子1:在main函數使用死循環的方式調用子函數fun().在這個例子可以看到exit和return的差異.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int fun()
{
        printf("fun\n");
        sleep(1);

        //讀者切換調用,體會一下
        //return 0;
        exit(0);
}

int main()
{
        int i;
        i++;
        printf("i = %d \n",i);
        while(1)
                fun();
        return 0;
}
           

    在上面這個例子中,我們看到如果在子函數中使用exit(),則循環隻執行一次;如果在子函數中使用return關鍵字,則死循環将一直執行下去.

4._exit()函數

頭檔案:#include <unistd.h>

定義函數:void _exit(int status);

函數說明:

    _exit()等價于_Exit().

    _exit()函數用來立即結束程式的執行,并把參數傳回給父程序,不調用任何終止處理程式而直接退出.此函數調用後不會傳回,并且會傳遞SIGCHLD信号給父程序,父程序可以由wait()函數取得子程序結束的狀态.注意:_exit()不會處理标準IO緩沖區,如果更新緩沖區請使用exit().

例子2:檢視exit與_exit函數的差別.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
        printf("output\n");
        printf("content int buffer"); //不帶\n

        //_exit(0);             //隻輸出output,沒有清理緩沖區(沒重新整理)
        exit(0);                //改為此句,将輸出content int buffer
        //return 0;
}
           

    由例子2可以看出,_exit()調用退出時,沒有清理重新整理緩沖區.終止一個程式時,_exit()立即進入核心,而exit()則先要執行一些清理處理程式.

5.atexit()和on_exit()函數

頭檔案:#include<stdlib.h>

定義函數:

int atexit(void (*function)(void));

int on_exit(void (*function)(int , void *), void *arg);

傳回值:如成功傳回0,若出錯傳回非0值.

函數說明:

    函數atexit()和on_exit()用來注冊執行exit()函數前執行的操作函數,其實作使用了回調函數的方法.按ISO C規定,一個程序可以注冊多達32個函數,這些函數被稱之為終止處理程式.檢視實際可以注冊多少個終止處理程式,可以通過調用sysconf()函數獲得.exit()調用這些函數的順序與他們登記時候的順序相反.同一個函數如若登記多次,則也會被調用多次.函數atexit()和on_exit()兩者的差異僅僅是在函數的參數上.

    其中atexit()函數的參數是一個函數的位址,類型為void (*function)(void);當調用此函數時無需向它傳遞任何參數,也不期望它傳回一個值.

    而on_exit()函數的參數是一個帶參數的函數,類型為void (*function)(int , void *),在這個函數void (*function)(int , void *)中,第一個參數為退出的狀态,在執行exit()函數時傳遞此參數值為exit()函數的參數.第二個參數為使用者輸入資訊,一個無類型的指針,使用者可以指定一段代碼位置或輸出資訊.(這裡有點拗口,結合下面的例子3看看.)

例子3:說明如何使用atexit()函數.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
static void my_exit1();
static void my_exit2();

int main()
{
        int i=0;
        if(atexit(my_exit2)!=0)
        {
                printf("can't register my_exit2");
        }
        for(i=0; i<3; i++)
        {
        if(atexit(my_exit1)!=0)
                {
                        printf("can't register my_exit1");
                }
        }

        printf("main exiting....\n");
        return 0;
        //exit(0);//同return 0;
        //_exit(0);//這個就不同了
}

static void my_exit1()
{
        printf("first exit handler...\n");
}
static void my_exit2()
{
        printf("second exit handler...\n");
}
           

輸出:

:main exiting....

:first exit handler...

:first exit handler...

:first exit handler...

:second exit handler...

    終止處理程式每注冊一次,就會被調用一次.在上述例子中,第一個終止處理程式被注冊了3次,是以也會被調用3次.且調用順序是和注冊時的順序是相反的,這讓我們聯想到棧的原理,先進後出,後進先出.

    注意:這裡在main函數是調用return 0(和exit()一樣),但是如果調用_exit(),輸出結果就不一樣了.程式隻會輸出main exiting.....因為_exit()不會去調用清理工作的函數.

例子4:說明如何使用on_exit()函數.

#include <stdio.h>
#include <stdlib.h>

static void test_exit(int status,void *arg);

int main()
{
        char *str = "How to use on_exit function...\n";
        on_exit(test_exit,(void *)str);
        exit(1314);
        //return 520;
}
static void test_exit(int status,void *arg)
{
        printf("before exit()!\n");
        printf("exit:%d\n",status);
        printf("arg=%s\n",(char *)arg);
}
           

輸出:

:before exit()!

:exit:1314

:arg=How to use on_exit function...

    可以看出,on_exit()和exit()還有終止處理程式test_exit()之間的關系.函數test_exit()的兩個參數都是從“别人”哪裡得來的.注意了,這裡如果是用return 520;效果也會一樣哦,讀者可以試試.

例子5:在終止處理程式中調用_exit()函數,這個有點奇葩啊,讀者猜想一下會是神馬結果呢.改良例子4,得到以下程式:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
static void my_exit1();
static void my_exit2();
static void my_exit3();

int main()
{
        int i=0;
        if(atexit(my_exit2)!=0)
        {
                printf("can't register my_exit2\n");
        }
		 //新增加的部分
        if(atexit(my_exit3)!=0)
        {
                printf("can't register my_exit3\n");
        }
        for(i=0; i<3; i++)
        {
        if(atexit(my_exit1)!=0)
                {
                        printf("can't register my_exit1\n");
                }
        }

        printf("main exiting....\n");
        //return 0;
        exit(0);
        //_exit(0);
}

static void my_exit1()
{
        printf("first exit handler...\n");
}
static void my_exit2()
{
        printf("second exit handler...\n");
}
static void my_exit3()
{
        printf("three exit handler...\n");
        _exit(0);//注意這裡了.加了_exit(0);
}
           

輸出:

: main exiting....

:first exit handler...

:first exit handler...

:first exit handler...

:three exit handler...

    發現沒有,“second exit handler...”這個沒有輸出,為什麼?如果在其中一個終止處理程式中(atexit() or on_exit())調用了_exit()函數;那剩餘的終止處理程式将不會得到調用,同時由exit()函數調用的其他終止程序步驟也将不會執行.

6.綜述

    exit()退出會處理緩沖區,_exit()不會.資源不能浪費,配置設定出去的資源,要記得回收回來,給更需要的程序使用.不要站着茅坑不拉屎.

    atexit()和on_exit()注冊終止處理程式,如果使用者在結束程序前,想幹一下别的事,可以用這兩個函數注冊.

    exit()和return等價,僅在main函數中.

    _exit()和_Exit()等價,任何時候.

參考閱讀:

[1] exit()函數使用說明.http://blog.csdn.net/u010006102/article/details/39737155.

[2] _exit()函數使用說明.http://blog.csdn.net/u010006102/article/details/39740101.

[3] atexit()函數使用說明.http://blog.csdn.net/u010006102/article/details/39740071.

[4] on_exit()函數使用說明.http://blog.csdn.net/u010006102/article/details/39740021.

筆者:個人能力有限,隻是學習參考...讀者若發現文中錯誤,敬請提出.

-- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙築高台,靜下心來,慢慢地沉澱---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------