以下内容引述<Unix/Linux系统编程手册>
系统调用
系统调用是受控的内核入口,进程可以请求内核以自己的名义去执行具体动作。
- 系统调用将处理器从用户态切换为核心态,以便CPU访问受保护的内核内存;
- 系统调用的组成是固定的,每个系统调用都是由一个唯一的数字来标志;
- 每个系统调用可辅以一套参数,对用户空间(进程虚拟地址空间)与内核空间之间(相互)传递的信息加以规范
系统调用步骤
- 应用程序调用C语言函数库中的外壳函数,发起系统调用;
- 系统调用中断处理例程,外壳函数必须保证所有的系统调用参数可用。通过堆栈,这些参数传入外壳函数,但内核却希望将这些参数置于特定寄存器;
- 由于所有系统调用进入内核的方式相同,内核需要设法区分每个系统调用。为此,外壳函数会将系统调用编号复制到一个特殊的CPU寄存器(%eax)中;
- 外壳函数执行一条中断机器指令(int 0x80),引发处理器从用户态切换到内核态;并执行系统中断0x80的中断矢量所指向的代码;
- 为响应中断0x80,内核会调用system_call()例程来处理这次中断
- 如果系统调用服务例程的返回值表明调用有误,外壳函数会使用该值来设置全局变量errno,然后,外壳函数会返回到调用程序,并同时返回一个整形值,以表明系统调用是否成功。
GNU C语言函数库
标准C语言函数库的实现随Unix的实现而异,GNU C语言函数库是Linux上最常用的实现
确定某个与glibc动态链接的可执行文件,运行ldd(列出动态依赖性)程序,检查依赖列表
$ ldd myprog | grep libc
libc.so.6 => /lib/tls/libc.so.6 (0x4004b000)
应用程序可以调用函数来确定系统的glibc版本
__GLIBC__
__GLIBC_MINOR__
如果glibc 2.12版本的系统上,两个值分别是2和12。
对于运行时版本,可以调用gnu_get_libc_version(),来确定运行时的glibc版本。
#include <gnu/libc-version.h>
const char* gnu_get_libc_version(void);
// returns pointers to null-terminated, staticaly allocated string containing GNU C library version number.
处理系统调用的错误
系统调用失败时,会给全局整形变量errno设置为一个正值,以标志具体的错误,程序应该包含**<errno.h>**头文件。
如果系统调用和库函数成功,errno值绝对不会被重置为0。
系统调用失败,使用函数perror()和strerror()打印
#include <stdio.h>
void perror(const char* msg);
#include <string.h>
char* strerror(int errnum);