【ch5. 函式庫】
UNIX 提供許多C語言函式庫(標準輸出輸入、數學、字串等函式庫),這些函式庫與 ANSI C 和 POSIX 版本相容。
再本章會教導兩重系統呼叫,錯誤處理和信號。
錯誤處理
透過 errno.h 提供的 errno 變數檢查錯誤,當系統呼叫失敗時,他會儲存一個錯誤碼到 errno 變數,你的程式變可以檢查這個變數來判斷錯誤的原因。
perror 函式提供一種不需檢查錯誤馬的方式,他會回傳一段描述錯誤訊息到標準輸出入流(standard I/O stream)。
#include
#include
#include
FILE *fd;
int fopen_errno;
main (int argc, char **argv)
{
if ((fd=fopen (argv[1], "r")) == NULL)
{
fopen_errno = errno; // 儲存錯誤碼
if (fopen_errno == ENOENT) // ENOENT 表示無此檔案或目錄
{
printf("no such file");
}
else
{
perror("Invalid input file");
exit(1);
}
}
exit(0);
}
信號(Signal)
系統換傳送信號來指出(1)程式的錯誤、(2)使用者要求的中斷/interrupt 及(3)其他狀況。
信號是簡單的 IPC (Interprocess communication 行程間通訊),類似 POSIX.4 定義外部信號的功能。
例如當按下 CTRL-C,就是發出 SIGINT 信號給程式,告知程式必須結束。當成是試圖存取非法的記憶體位址,系統會發出 SIGSEGV 信號給程式。
當信號到達時,程式通常會
* Ignore:忽略並繼續執行
* Terminate:結束,可能造成記憶體傾印 (core dump)
* Stop:暫時停時執行,但也可能繼續執行
* Continue:目前停止的程式繼續執行
* Execute handler:預先準備好的信號處理常式
UNIX 系統通常會設定一組 32 種信號,底下為最重要的 6 種(其他系統也會有的信號) * SIGABRT:預設動作-結束,程式呼叫 abort 時送出
* SIGFPE:預設動作-結束,浮點運算錯誤
* SIGILL:預設動作-結束,不合法指令
* SIGINT:預設動作-結束,中斷發生;當使用者按下 CTRL-C
* SIGEGV:預設動作-結束,記憶體片斷重複;不合法記憶體使用
* SIGTERM:預設動作-結束,結束的要求(由軟體送出)
SIGKILL 也是值的討論,幾乎所有系統都俱備。但它的行為較特殊,是以"極端傷害的方式來結束",要求行程立刻停止而結束。
另外許多系統提供 SIGUSR1、SIGUSR2,讓使用者使用自訂的信號。
有兩個函式可以處理信號,(1)raise-程式用它來送信號給自己;(2)signal-程式使用他來改變預設的動作。
* raisee 使用,若成功將回傳0,失敗為1 (此例子因導致結束,raise 將不會 return)
#include
#include
#include
main()
{
int ret;
printf("test raise 1\n");
sleep(5);
ret = raise(SIGINT);
printf("test raise 2, ret = %d\n",ret);
}
* signal 使用,signal(signal, whattodo),whattodo通常有三種 (1)SIG_ING 忽略、(2)SIG_DEF 執行預設的動作、(3)handler
此範例當使用者按下 CTRL-C,inthandler會印出訊息
#include
#include
#include
void inthandler(int signal)
{
printf("received signal %d\n",signal);
abort;
}
main()
{
signal(SIGINT, inthandler);
sleep(60);
}
系統介面的問題
免費的軟體函式庫不提供函式庫與系統的介面,(例如 read 及 write 函式)。這些函式庫可以再作業系統的C函式庫裡找到。Kernel的必須支援這些函式呼叫。
跨平台編譯可能需要一些特殊技巧:
* 編譯的目的擋可以執行所有函式,但你卻沒有系統中可用函式庫的權限 (此系統是你編譯的環境) -> 解法:找出你需要的函式庫放進編譯系統,保證 gcc 和 gld 可以找到,可以避免從系統上cpoy的問題。
*編譯的目標程式可能無法再系統呼叫達成 (若你正在開發某種內嵌入式的軟體,你的目標程式可能一點"作業系統"的感覺也沒有,他不和 POSIX 相容) -> 解法:(1) 開發一個經簡的函式庫,使你的程式可以編譯和執行最基本的功能,一旦呼叫精減函式庫無法提供的系統呼叫,變會傳回錯誤訊息。(2)開發一個原全與 POSIX 相容的函式庫,提供所有你需要的功能。
跨平台編譯環境的 POSIX 函式
_exit:跳出但不清除
Close:關閉檔案
Execve:啟動新的行程
Fork:建立新程序
Fstat:取的檔案狀態
Getpid:取得程序識別碼
Isatty:I/O 敘述子是終端機嘛
Kill:送出信號
Link:建立新檔案
Lseek:設定檔案內部指標
Read:讀取檔案
Sbrk:延伸資料段
Stat:取得檔案狀態
Times:取得時間統計
Unlink:刪除目錄項目
Wait:檔帶子程序
Write:寫入檔案
gcc 假設有一個啟動模組稱為 crt0.o,用來設定記憶體,進行起始工作、轉移控制權到 main 常式,並再 main 返回時進行清除工作。也可自行開發自己的 ctr0.o。
crt0.o 的動作有:
* 定義 start,第1個指令開始執行的位置
* 設定 stack pointer
* 設定程式 bss (未初始化資料) 段的記憶體為 0
* 進行硬體及軟體依些初始化動作
* 呼叫 main