1、系统调用
系统调用是受控的内核入口;
【系统调用的运作方式之前,需要关注】:
- 系统调用将处理器从用户态切换到核心态,以便CPU访问受到保护的内核内存;
- 系统调用的组成是固定的,每个系统调用都由一个唯一的数字来标识;
- 每个系统调用可辅之以一套参数,对用户空间(进程的虚拟地址空间)与内核空间之间(相互)传递的信息加以规范;
【执行系统调用步骤】:
- 程序通过调用C语言函数库中的外壳函数,来发起系统调用;
- 外壳函数必须保证所有系统调用参数可用,通过堆栈传入外壳函数;
- 由于所有系统调用进入内核的方式都相同,内核需要区分每个函数调用;
- 外壳函数执行一条中断机器指令(int 0x80),引发处理器从用户态切换到核心态,并执行系统中断;
- 为响应中断,内核会调用system_call例程来处理;
a)在内核栈中保存寄存器值;
b)审核系统调用编号的有效;
c)以系统调用编号对存放所有调用服务例程的列表(内核变量sys_call_table)进行索引发现并调用相应的系统调用服务例程,
若系统调用服务例程带有参数,那么将首先检查参数的有效性;
最后,该服务例程会将结果状态返回给system_call()例程。
d)从内核栈中恢复各寄存器值,并将系统调用返回值置于栈中;
e)返回至外壳函数,同时将处理器切换回用户态;
- 若系统调用有误,外壳函数会使用返回值来设置全局变量errno;
2、库函数
为了提供比底层系统调用更为方便的调用接口;
3、标准C语言函数库,GNU C语言函数库(glibc)
GNU C是Linux上最常用的实现;
- 可直接运行glibc共享库文件来获取glibc版本;
- 针对某个与glibc动态链接的可执行文件,运行ldd;在检查输出的库依赖性列表,便能发现glibc共享库的位置;
glibc定义两个常量:__GLIBC__和__GLIBC_MINOR__;供程序在编译时测试使用;
4、处理来自系统调用和库函数的错误
4.1 处理系统调用错误
系统调用失败时,会将全局整型变量errno设置为一个正值,以标识具体错误;
- 若调用成功,errno不会被重置为0,故该变量值不为0,可能是之前调用失败造成的;
- 在错误检查时,必须坚持首先检查函数的返回值是否表明调用出错,在检查errno确定错误原因;
- 根据errno打印错误消息,提供库函数perror和strerror;
strerror
返回的字符串可以是静态分配;
void test_error() {
FILE *f = fopen("file.txt", "r+");
if(f != NULL){
std::cout << "文件打开成功..." << std::endl;
}else{
perror("error open");
exit(-1);
}
}
4.2 处理来自库函数的错误
- 某些库函数返回错误信息的方式与系统调用完全相同,返回值为-1,并以errno号来表示具体错误;
- 某些库函数在出错时返回-1之外的其他值,仍会设置errno;
- 还有些函数不适用errno;
5、示例程序的注意事项
ename.h
#ifndef LINUX_TEST_ENAME_H
#define LINUX_TEST_ENAME_H
static char *ename[] = {
"",
"EPERM", "ENOENT", "ESRCH", "EIO", "EXIO", "E2BIG",
"ENOEXEC", "EBADF", "ECHILD", "EAGAIN/EWOULDBLOCK", "ENOMEM",
"EACCES", "EFAULT", "ENOTBLK", "EBUSY", "EEXIST", "EXDEV",
"ENODEV", "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE",
"ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", "EROFS",
"EMLINK", "EPIPE", "EDOM", "ERANGE", "EDEADLK/EDEADLOCK",
"ENMAETOOLONG", "ENOLCK", "ENOSYS", "ENOTEMPTY", "ELPP", "",
"ENOMSG", "EIDRM", "ECHRNG", "EL2NSYNC", "EL3HLT", "EL3RST",
"ELNRNG", "EUNATCH", "ENOCSI", "EL2HLT", "EBADE", "EBADR",
"EXFULL", "ENOANO", "EBADRO", "EBADSLT", "", "EBFONT", "ENOSTR",
"ENODATA", "ETIME", "ENOSR", "ENONET", "ENOPKG", "EREMOTE",
"ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO", "EMULITIHOP",
"EDOTDOT", "EBADMSG", "EOVERFLOW", "ENOTUNIQ", "EBADFD",
"EREMCHG", "ELIBACC", "ELIBBAD", "ELIBSCN", "ELIBMAX",
"ELIBEXECC", "EILSEQ", "ERESTART", "ESTRPIPe", "EUSERS",
"ENOTSOCK", "EDESTADDRREQ", "EMSGSIZE", "EPROTOTYPE",
"ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT",
"EOPNOTSUPP/ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
"EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH",
"ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS", "EISCONN",
"ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT",
"ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH", "EALREADY",
"EINPROGRESS", "ESTALE", "EUCLEAN", "ENOTNAM", "ENAVAIL",
"EISNAM", "EREMOTEIO", "EDQUOT", "ENOMEDIUM", "EMEDIUMTYPE",
"ECANCELED", "ENOKEY", "EKEYEXPIRED", "EKEYREVOKED",
"EKEYREJECTED", "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL"
};
#define MAX_ENAME 132
#endif
error_function.h
#ifndef LINUX_TEST_ERROR_FUNCTION_H
#define LINUX_TEST_ERROR_FUNCTION_H
void errMsg(const char *format, ...);
#ifdef __GNUC__
#define NORETURN __attribute__((__noreturn__))
#else
#define NORETURN
#endif
void errExit(const char *format, ...) NORETURN;
void err_exit(const char *format, ...) NORETURN;
void errExitEN(int errnum, const char *format, ...) NORETURN;
void fatal(const char *format, ...) NORETURN;
void usageErr(const char *format, ...) NORETURN;
void cmdLineErr(const char *format, ...) NORETURN;
#endif
get_num.h
#ifndef LINUX_TEST_GEI_NUM_H
#define LINUX_TEST_GEI_NUM_H
#define GN_NONNEG 01
#define GN_GT_O 02
#define GN_ANY_BASE 0100
#define GN_BASE_8 0200
#define GN_BASE_16 0400
long getLong(const char *arg, int flags, const char *name);
int getInt(const char *arg, int flags, const char *name);
#endif
tlpi_hdr.h
#ifndef LINUX_TEST_TLPI_HDR_H
#define LINUX_TEST_TLPI_HDR_H
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <algorithm>
#include "error_function.h"
using namespace std;
#endif
error_function.cpp
#include <stdarg.h>
#include <stdio.h>
#include "error_function.h"
#include "tlpi_hdr.h"
#include "ename.h"
static void terminate(bool useExit3){
char *s;
s = getenv("EF_DUMPCORE");
if(s != NULL && *s != '\0')
abort();
else if(useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
static void outputError(bool useErr, int err, bool flushStdout,
const char *format, va_list ap) {
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if(useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if(flushStdout)
fflush(stdout);
fputs(buf, stderr);
}
void errMsg(const char *format, ...) {
va_list argList;
int savedErrno;
savedErrno = errno;
va_start(argList, format);
outputError(true, errno, true, format, argList);
va_end(argList);
errno = savedErrno;
}
void errExit(const char *format, ...){
va_list argList;
va_start(argList, format);
outputError(true, errno, true, format, argList);
va_end(argList);
terminate(true);
}
void err_exit(const char *format, ...) {
va_list argList;
va_start(argList, format);
outputError(true, errno, false, format, argList);
va_end(argList);
terminate(false);
}
void errExitEN(int errnum, const char *format, ...){
va_list argList;
va_start(argList, format);
outputError(true, errnum, true, format, argList);
va_end(argList);
terminate(true);
}
void fatal(const char *format, ...) {
va_list argList;
va_start(argList, format);
outputError(true, 0, true, format, argList);
va_end(argList);
terminate(true);
}
void usageErr(const char *format, ...) {
va_list argList;
fflush(stdout);
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr);
exit(EXIT_FAILURE);
}
void cmdLineErr(const char *format, ...) {
va_list argList;
fflush(stdout);
fprintf(stderr, "Command-line usage error: ");
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr);
exit(EXIT_FAILURE);
}
get_num.cpp
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "get_num.h"
static void gnFail(const char *fname, const char *msg, const char *arg, const char *name) {
fprintf(stderr, "%s error", fname);
if(name != NULL)
fprintf(stderr, " (in %s)", name);
fprintf(stderr, ": %s\n", msg);
if(arg != NULL && *arg != '\0')
fprintf(stderr, " offending text: %s\n", arg);
exit(EXIT_FAILURE);
}
static long getNum(const char *fname, const char *arg, int flags, const char *name) {
long res;
char *endptr;
int base;
if(arg == NULL || *arg == '\0')
gnFail(fname, "null or empty string", arg, name);
base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8 :
(flags & GN_BASE_16) ? 16 : 10;
errno = 0;
res = strtol(arg, &endptr, base);
if(errno != '\0')
gnFail(fname, "nonnumeric characters", arg, name);
if((flags & GN_GT_O) && res < 0)
gnFail(fname, "negative value not allowed", arg, name);
if((flags & GN_GT_O) && res <= 0)
gnFail(fname, "value must be > 0", arg, name);
return res;
}
long getLong(const char *arg, int flags, const char *name) {
return getNum("getLong", arg, flags, name);
}
int getInt(const char *arg, int flags, const char *name) {
long res;
res = getNum("getInt", arg, flags, name);
if(res > INT_MAX || res < INT_MAX)
gnFail("getInt", "integer out of range", arg, name);
return (int)res;
}
6、系统数据类型
|