AUPE 輸出致標準錯誤的出錯函式分析與實現 err_sys, err_quit, err_doit etc.
阿新 • • 發佈:2021-06-11
1. 出錯函式彙總
AUPE 輸出至標準錯誤的出錯函式, 分為以下幾個:
函式名 | 何時呼叫 | 功能 |
---|---|---|
err_ret | 系統呼叫相關的非致命錯誤 | 列印訊息並且返回 |
err_sys | 系統呼叫相關的非致命錯誤 | 列印訊息並且終止程式 |
err_cont | 不與系統呼叫相關的非致命錯誤 | 錯誤碼通過顯式引數error傳遞, 列印訊息並返回 |
err_exit | 不與系統呼叫相關的致命錯誤 | 錯誤碼通過顯式引數error傳遞, 列印訊息並返回 |
err_dump | 系統呼叫相關的致命錯誤 | 列印訊息, 核心轉儲, 並且終止程式 |
err_msg | 不與系統呼叫相關的非致命錯誤 | 列印訊息, 並且返回 |
err_quit | 不與系統呼叫相關的致命錯誤 | 列印訊息, 並且終止程式 |
2. 簡要分析
-
詳細錯誤開關
通過一個標誌errnorflag, 控制錯誤詳細資訊的列印. -
錯誤號
如果要列印除傳入錯誤資訊外, 更加詳細錯誤資訊:
除了要開啟errnorflag, 另外,
① 對於系統呼叫相關錯誤, 可以不用呼叫者傳遞錯誤號(引數error), 可直接利用全域性變數errno, 檢查物件要求調用出錯時會設定errno;
② 對於非系統呼叫相關錯誤, 如果要列印錯誤號對應的詳細資訊, 就需要呼叫者傳遞錯誤號. 常見多執行緒環境, 或者其他不會設定errno, 但會返回錯誤號的呼叫, i.g. pthread_create; -
是否終止程式
只有致命錯誤, 不論系統呼叫, 還是非系統呼叫, 才會終止程式. 非必要情況, 不用終止程式. 終止程式用exit(). -
核心轉儲(core dump)
呼叫abort異常終止當前程序, 觸發核心轉儲. 輸出core檔案的前提是, Core file size > 0, 可以用ulimit -c命令設定, ulimit -a命令檢視, 單位如下圖.
$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 30702 max locked memory (kbytes, -l) 65536 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 30702 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited $ ulimit -c 0 $ ulimit -c 1024 $ ulimit -c 1024
- 可變引數及如何實現列印
對於可變引數"...", 可以將其轉換為va_list, 然後利用vsnprintf, 將要列印的內容寫至緩衝區中. 最後將列印內容輸出到stderr.
3. 實現原始碼
先來看公共的, 可變引數、錯誤號及提示資訊列印函式err_doit
errnoflag : 列印錯誤詳細資訊開關;
error: 錯誤號, 通過strerror將其轉化為字串;
fmt: 格式化字串, 可由使用者傳入, 包含了使用者錯誤提示資訊;
ap: 可變引數列表, 由使用者跟fmt一起傳入;
err_doit的主要任務是:
- 根據引數設定, 先將使用者設定錯誤提示資訊fmt & 引數列表ap轉存到緩衝區buf中;
- 判斷是否要列印錯誤號error對應的錯誤訊息, 如果要列印, 就用strerror將error轉化成字串資訊, 並追加填入緩衝區;
- 緩衝區字串末尾追加\n, 因為呼叫庫IO函式時, 會自動沖刷庫快取;
- 沖刷IO庫快取, 然後將緩衝區字串, 通過fputs輸出至IO庫快取, 這樣就列印了錯誤資訊;
/**
* 列印訊息, 並且返回呼叫者
* 呼叫者指定引數errnoflag
* @param errnoflag 錯誤標識, 值非0時, 才打印錯誤號轉化成的錯誤詳細資訊. 值為0時, 不列印錯誤號對應錯誤詳細資訊.
* @param error 錯誤號. 系統呼叫相關錯誤, 不用傳錯誤號, 直接用errno作為錯誤號; 非系統呼叫錯誤(不設定errno的), 手動傳入錯誤號
* @param fmt 格式化字串
* @param ap 變參列表, fmt中有多少個轉義字元, ap就需要提供同樣個數引數值
*/
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
int errno_save;
char buf[MAXLINE];
errno_save = error; /* value caller might want printed */
vsprintf(buf, fmt, ap); /* format string fmt write out to buf */
if (errnoflag)
sprintf(buf+strlen(buf), ": %s", strerror(errno_save)); /* append strerror(error) to buf */
strcat(buf, "\n"); /* append "\n" to buf */
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr); /* write out buf to stderr */
fflush(stderr); /* SunOS 4.1.* doesn't grok NULL argument */
}
出錯函式的使用者介面實現:
主要需要注意區分 是否為系統呼叫相關, 是否需要使用者輸入錯誤號, 是否列印詳細錯誤資訊, 是否終止程序, 是否需要core file
#include <errno.h> /* for definition of errno */
#include <stdarg.h> /* ANSI C header file */
#include <stdlib.h>
#include "ourhdr.h"
static void err_doit(int, int, const char *, va_list);
/*
* 系統呼叫相關的非致命錯誤
* 列印訊息並且返回
*/
void err_ret(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, errno, fmt, ap);
va_end(ap);
return;
}
/*
* 系統呼叫相關的非致命錯誤
* 列印訊息並且終止程式
*/
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, errno, fmt, ap);
va_end(ap);
exit(1);
}
/**
* 不與系統呼叫相關的非致命錯誤
* 錯誤碼通過顯式引數error傳遞
* 列印訊息並返回
*/
void err_cont(int error, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, error, fmt, ap);
va_end(ap);
}
/**
* 不與系統呼叫相關的致命錯誤
* 錯誤碼通過顯式引數error傳遞
* 列印訊息並終止程式
*/
void err_exit(int error, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, error, fmt, ap);
va_end(ap);
exit(1);
}
/*
* 系統呼叫相關的致命錯誤
* 列印訊息, 核心轉儲, 並且終止程式
*/
void err_dump(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, errno, fmt, ap);
va_end(ap);
abort(); /* dump core and terminate */
exit(1); /* shouldn't get here */
}
/*
* 不與系統呼叫相關的非致命錯誤
* 列印訊息, 並且返回
*/
void err_msg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
}
/*
* 不與系統呼叫相關的致命錯誤
* 列印訊息, 並且終止程式
*/
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
exit(1);
}