专注于用户空间的系统级编程–即内核之上的所有内容。
Linux系统编程的基础
- 系统调用,系统编程始于系统调用,也终于系统调用。syscall是为了从操作系统请求一些服务或资源发起的函数调用。
- C库,是UNIX应用程序的核心。在现代Linux系统中,C库由GNU libc提供,简称glibc。
glibc中除了标准C库之外,还提供了系统调用封装、线程支持和基本应用工具。 - C编译器,标准C编译器是由GNU编译工具集(GNU Compiler Collection,gcc)提供的。
Linux编程的概念
文件和文件系统
文件是Linux系统中最基础最重要的抽象。Linux遵循一切皆文件的理念。因此,很多交互是通过读写文件来完成。
文件必须先打开才能访问。打开方式有只读、只写和读写模式。文件打开之后是通过唯一描述符来引用,
在Linux内核中,文件用一个整数表示(C语言的int类型),称为文件描述符(file descriptor,简称fd)。
文件描述符在用户空间共享,用户程序通过文件描述符可以直接访问文件。
Linux系统编程的大部分工作都会涉及打开、操纵、关闭以及其他文件描述符操作。
- 普通文件(regular files),包含以字节流(线性数据)组织的数据。
- 目录和链接(diectory and link),目录是可读名称(文件名)到索引编号之间的映射。名称和索引节点之间的配对称为链接。
- 硬链接(hard links),不同名称的多个链接映射到同一个索引节点时,称该链接为硬链接
- 符号链接(symbolic links),类似于普通文件,有自己的索引节点和数据块,包含要链接文件的绝对路径。
- 特殊文件(special file),指以文件来表示的内核对象。Linux只支持以下四种设备:
- 块设备文件
- 字符设备文件
- 命名管道(named pipes),通常称为FIFO,是以文件描述符作为通信信道的进程间通信(IPC)机制
- UNIX域套接字(socket),进程间通信的高级形式
- 文件系统和命名空间,Linux提供全局统一的文件和命名空间
进程
进程是执行时的目标代码:活动的、正在运行的程序。但不仅仅包含目标代码,它还包含数据、资源、状态和虚拟计算机。
进程的生命周期从可执行代码开始,在Linux下,最常见的格式称为“可执行和可链接格式(Executable and Linkable Format,ELF)”。
包含多个元数据、多个代码段和数据段。代码段是线性目标代码块,可以加载到线性内存块中。
最重要和通用的段是文本段、数据段和bss段。
- 文本段,包含可执行代码和只读数据如常量
- 数据段,包含初始化的数据
- bss段,包含未初始化的全局数据。因为C标准规定C变量的默认值为0,因此没有必要在磁盘上将0保存到目标代码中。
相反的,根据目标代码可以很容易列举出bss段中未初始化的变量,内核在加载到内存时可以映射bss段中的全0页面,
bss段的设计完全出于性能优化。bss是block started by symbol的简称。
线程
每个进程包含一个或多个执行线程(通常简称线程threads)。线程是进程内的活动单元。
线程包括栈、处理器状态、目标代码的当前位置(pc)。进程的其他部分由所有线程共享,最主要的是进程地址空间。
在用户空间,Linux依据POSIX 1003.1c实现线程模型(称为Pthreads)。目前Linux线程实现称为POSIX Threading Library(NPTL)。
进程层次结构
每个进程都有唯一的正整数标识,称为进程ID(pid)。在Linux中,进程有严格的层次结构,即进程树。
进程树的根是第一个进程,称为init进程,通常是init程序。新的进程通过调用fork()创建,原进程称为父进程,
fork()创建的新进程为子进程。当进程结束时,并不会立即从系统中删除。相反地,内核将在内存中保存该进程的部分内容,
允许父进程查询其状态,这被称为等待终止进程。一旦父进程确认某个子进程已经终止,该子进程就会完全被删除。
信号
信号是一种单向异步通知机制。信号可能从内核发送到进程,也可能从进程发送到内核,或者进程发送给自己。
信号一般用于通知进程发生了某些事件,如段错误或按下ctrl-C。
进程间通信
允许进程间交换信息并通知彼此所发生的事件是操作系统最重要的工作之一。Linux支持的进程间通信机制包括:
- 管道
- 命名管道
- 信号量
- 消息队列
- 共享内存
- 快速用户空间互斥(futex)
头文件
内核本身和glibc提供大量用于系统编程的头文件,包括标准C库(<string.h>)以及一些UNIX(<unistd.h>)
错误处理
在系统编程中,错误是通过函数返回值和errno描述。glibc为库函数和系统调用提供透明errno支持。
变量errno在<errno.h>中定义如下:
extern int errno;