AUPE 输出致标准错误的出错函数分析与实现 err_sys, err_quit, err_doit etc.

1. 出错函数汇总

AUPE 输出至标准错误的出错函数, 分为以下几个:

函数名 何时调用 功能
err_ret 系统调用相关的非致命错误 打印消息并且返回
err_sys 系统调用相关的非致命错误 打印消息并且终止程序
err_cont 不与系统调用相关的非致命错误 错误码通过显式参数error传递, 打印消息并返回
err_exit 不与系统调用相关的致命错误 错误码通过显式参数error传递, 打印消息并返回
err_dump 系统调用相关的致命错误 打印消息, 核心转储, 并且终止程序
err_msg 不与系统调用相关的非致命错误 打印消息, 并且返回
err_quit 不与系统调用相关的致命错误 打印消息, 并且终止程序

2. 简要分析

  1. 详细错误开关
    通过一个标志errnorflag, 控制错误详细信息的打印.

  2. 错误号
    如果要打印除传入错误信息外, 更加详细错误信息:
    除了要打开errnorflag, 另外,
    ① 对于系统调用相关错误, 可以不用调用者传递错误号(参数error), 可直接利用全局变量errno, 检查对象要求调用出错时会设置errno;
    ② 对于非系统调用相关错误, 如果要打印错误号对应的详细信息, 就需要调用者传递错误号. 常见多线程环境, 或者其他不会设置errno, 但会返回错误号的调用, i.g. pthread_create;

  3. 是否终止程序
    只有致命错误, 不论系统调用, 还是非系统调用, 才会终止程序. 非必要情况, 不用终止程序. 终止程序用exit().

  4. 核心转储(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
  1. 可变参数及如何实现打印
    对于可变参数"...", 可以将其转换为va_list, 然后利用vsnprintf, 将要打印的内容写至缓冲区中. 最后将打印内容输出到stderr.

3. 实现源码

先来看公共的, 可变参数、错误号及提示信息打印函数err_doit
errnoflag : 打印错误详细信息开关;
error: 错误号, 通过strerror将其转化为字符串;
fmt: 格式化字符串, 可由用户传入, 包含了用户错误提示信息;
ap: 可变参数列表, 由用户跟fmt一起传入;

err_doit的主要任务是:

  1. 根据参数设置, 先将用户设置错误提示信息fmt & 参数列表ap转存到缓冲区buf中;
  2. 判断是否要打印错误号error对应的错误消息, 如果要打印, 就用strerror将error转化成字符串信息, 并追加填入缓冲区;
  3. 缓冲区字符串末尾追加\n, 因为调用库IO函数时, 会自动冲刷库缓存;
  4. 冲刷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);
}
posted @ 2021-06-11 16:41  明明1109  阅读(311)  评论(0编辑  收藏  举报