【TLPI读书笔记】 三、系统编程概念
系统调用
- 系统调用是受控的内核入口,借助于这一机制,进程可以请求内核以自己的名义去执行某些动作。
- 以应用程序编程接口(API)的形式,内核提供有一系列服务供程序访问。
关于系统调用需关注一下几点:
- 系统调用将处理器从用户态切换到核心态,以便CPU访问受到保护的内核内存
- 系统调用的组成是固定的,每个系统调用都由一个唯一的数字来标识
- 每个系统调用可辅之以一套参数,对用户空间与内核空间之间传递的信息加以规范。
- 从C语言编程的角度来看,调用C语言函数库的外壳函数等同于调用相应的系统调用服务例程。
库函数
- 一个库函数是构成C语言函数库的众多库函数之一。
- 许多库函数不会使用任何系统调用;另一方面,还有些库函数构建与系统调用层之上。
- 往往,设计库函数是为了提供比底层系统调用更为方便的调用接口。
标准C语言函数库;GNU C语言函数库(glibc
)
标准C语言函数库的实现随UNIX的实现而异。GNU C语言函数库是Linux上最常用的实现。
确定系统的glibc
版本
在shell
中可直接运行glibc
共享库文件————将其视为可执行文件,来获取glibc
版本
$ /lib/libc.so.6
而要确定该库的位置:可以针对某个与glibc
动态链接的可执行文件运行ldd
程序。
处理来自系统调用和库函数的错误
几乎每个系统调用和库函数都会返回某类状态值,用以表明调用成功与否。要了解调用是否成功,必须坚持对状态值进行检查。若调用失败必须采取相应行动,至少应该显示错误消息。
少数几个系统调用在调用时从不失败。例如,
gitpid()
总能成功返回进程ID,而_exit()
总能终止进程。无需对此类系统调用的返回值进行检查。
处理系统调用错误
- 通常,返回值为 -1 表示出错;而且会将全局整型变量
errno
设置为一个正值,以标识具体错误。<errno.h>
头文件提供了对errno
的声明,以及一组针对各种错误编号而定义的常量。所有这些错误都以字母E
打头。 - 如果系统调用和库函数成功,
errno
绝不会被重置为0,故此,该变量不为0可能是之前调用失败造成的。因此,在进程错误检查时,必须坚持首先检查函数的返回值是否表明调用出错,然后再检查errno
确定错误原因。 - 少数系统调用在调用成功后也会返回 -1。要判断此类系统调用是否出错,应在调用前将 errno 置为0,并在调用后对其检查。
- 系统调用失败后,常见的做法是根据
errno
打印错误消息。提供库函数perror()
和strerror()
就是出于这一目的。
处理来自库函数的错误
不同的库函数在调用发生错误时,返回的数据类型和值也各不相同。从错误处理的角度来说,可将库函数划分为以下几类:
- 某些库函数返回错误的方式与系统调用完全相同————返回值为 -1,伴之以
errno
号来表示具体错误 - 某些库函数在出错时会返回 -1 之外的其他值,但仍会设置
errno
来表明具体的出错情况 - 还有些函数根本不使用
errno
。
总结
本章重点介绍了系统调用和库函数,并说明其关系;然后说明了函数库所需的动态库文件;最后介绍了错误处理。