Contents
  1. 1. 前言
  2. 2. 返回值
  3. 3. 直接调用
  4. 4. 相关函数
  5. 5. 错误恢复

前言

Linux system callkernel入口,通常不会被直接调用,而是由对应的C函数库包装来完成一些必要的步骤(trapping to kernel mode),同时也使用调用方式与普通库没有什么区别。

一般C包装函数功能如下:

  • 将参数与system call代号copy到kernel指定的寄存器
  • trapping to kernel mode(开始执行)
  • 返回user mode时,如果返回为error number,则设置errno

C函数库也可以会为参数作预处理和返回值的后期处理。

man 2 syscalls可以查看所有的系统调用。

返回值

当出现错误时,大部分system call都会返回一个负值(如 error(3)中定义的三个负值之一),C包装库函数会向调用者隐藏此细节:返回-1并将errno的绝对值copy保存。

成功的system call一般返回0,但也有返回非0值,具体查看对应的manual page.

某些情况下,开发者必须定义一个feature test macro来从头文件(在manual page的SYNOPSYS段中指定)中获取system call的声明,该定义必须在include任何头文件之前。此种情况下,macro在manual中描述。更多关于feature test macro的说明,可以参见 man 7 feature_test_macro

关于一些衍生UNIX术语与及相关缩写的说明,可以参阅man 7 standards

直接调用

一般情况下无需直接调用system call,但是有时并无C函数库对其进行妥善的封装,此时可以使用syscall(2)来直接调用,历史上,也可以使用宏_syscall(2)

相关函数

errno定义在<errno.h>文件中,对支持多线程的系统,应为每个线程保存一个errno的copy,以避免相互干涉,因此在linux中,其定义是这样的。

1
2
extern int *__errno_location(void);
#define errno (*__errno_location())

errno定义成为函数的返回值(int型指针)所指向的值。

需要注意的是errno在没有新的错误产生时不会清零,所以请只在系统调用返回错误时检查它。errno也不会被设置为0, 0也没有对应的错误定义。

1
2
#include <string.h>
char *strerror(int errnum);

输入errno返回对应的说明。

1
2
#include <stdio.h>
void perror(const char *msg);

直接打印输入”msg: “+errno对应的说明,比如msg可以是当前命令。

例程如下:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc , char* argv[]){
fprintf(stderr,"EACESS: %s\n",strerror(EACCES));
errno = ENOENT;
perror(argv[0]);
exit(0);
}

输出如下:

1
2
3
$ ./a.out
EACESS: Permission denied
./a.out: No such file or directory

错误恢复

error分为fatalnonfatal, 对于fatal error,能做的就是打印错误并退出程序;而非fatal的错误,多半是与资源有关的 ,如EAGAIN,ENFILE,ENOBUFS,ENOLCK等,可以实施一些等待+重试操作来提升可靠性,避免不必要的退出。比如说检测到网络连接断开时,可以设定为等待一段时间后再次连接。有的程序使用指数算法,重试次数越多,等待时间显著增长。

Contents
  1. 1. 前言
  2. 2. 返回值
  3. 3. 直接调用
  4. 4. 相关函数
  5. 5. 错误恢复