APUE学习笔记00——UNIX基础
Xilong Yang

APUE这本书实在是干而松散,不自己消化消化确实看不下去。这篇文章简单总结一下前两章,Unix基础与标准。

基础介绍

操作系统在狭义上单指一种控制硬件单元,为程序提供运行环境的软件,称为内核(Kernel)。内核的接口称为系统调用(System call)。

广义上,操作系统包括内核以及一些软件,这些软件包括了公用函数库、shell和系统实用程序(system utility)以及一些应用程序。

其中,公用函数库就是建立在系统调用的基础上的一些函数。应用程序既可以使用系统调用也可以使用公用函数库。shell是一个特殊的应用程序,为运行其它程序提供了一个接口。

登录

用户键入用户名与口令,系统在口令文件中读取相关信息并进行相应操作。口令文件通常是/etc/passwd,其中每行记录一个登录项。登录项由7个以冒号分隔的字段组成,依次是:

1
登录名:加密口令:UID:GID:注释:起始目录:shell程序

目前所有系统都将加密口令移到另一个文件中,但这里先略过。

登录后,通常进入到shell程序,shell是一个命令行解释器,它读取用户输入然后执行命令。

文件和目录

文件系统

UNIX文件系统是目录和文件的一种层次结构,所有东西的起点是根(root)目录,其名称是/

我们总听说UNIX中一切皆文件,对于目录也是适用的。实际上目录是一个包含目录项的文件,逻辑上可以认为每个目录项都包含一个文件名与文件属性信息。

文件名

目录中的各项拥有一个文件名(filename)。只有斜线/和空字符不能出现在文件名中。因为斜线用来分隔路径名和文件名而空字符表示路径结束。

但习惯上最好还是使用字母、数字、.-_作为文件名,这可以避免很多麻烦。

新的目录会创建两个文件名....指向当前目录,..指向上级目录。在根目录中都指向根目录。

路径名

/分隔的一个或多个文件名组成的序列构成一个路径名,以/开头的称为绝对路径,以文件名开头称为相对路径。相对路径名指向相对于当前目录的文件。

工作目录

每个进程有一个工作目录,所有相对路径名从工作目录开始解释,进程可以使用chdir来更改工作目录。

起始目录

登录时的工作目录为起始目录,记录在口令文件中。

输入与输出

文件描述符

文件描述符通常是一个小的非负整数,内核用以标识一个特定的进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,它都返回一个文件描述符。在读写文件时,可以使用这个文件描述符。

标准输入、标准输出和标准错误

惯例上,每当运行一个新程序时,所有shell都为其打开3个文件描述符,分别为标准输入、标准输出、标准错误。如果不做特殊处理,它们都链接终端。

大多数shell提供一种方法,使其中任何一个都能重定向至某个文件。

缓冲

不带缓冲的I/O直接向文件中写入,而带缓冲的I/O在缓冲区满后才写入文件。

程序和进程

程序是存储在磁盘上的可执行文件。内核使用exec函数将程序读入内存,并执行程序。

程序的执行实例被称为进程。UNIX系统确保每个进程都有一个唯一的数字标识符,称为PID,PID总是一个非负整数。

系统主要使用三个函数控制进程:forkexecwaitpid

通常一个进程只有一个控制线程——某一时刻执行的一组机器指令。但某些情况下可以拥有多个线程。一个进程内的所有线程共享同一地址空间、文件描述符、栈以及进程相关属性。因为它们能访问同一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性。

线程也用ID标识,称为TID。TID只在进程内起作用,对其它进程没有意义。

出错处理

当UNIX系统函数出错时通常返回一个负值,且整型变量errno通常被设置为具有特定信息的值。文件<errno.h>中定义了errno及可以赋予它的常量。这些常量都以字母E开头。对于errno有两条规则:

  1. 只有出错时才改变它的值。
  2. 任何程序都不会将它的值设为0。

两个函数可以用于打印出错消息,分别是:

1
2
3
4
5
#include <string.h>
char *strerror(int errnum); // errnum通常是errno值,函数将其映射为一个出错消息的字符串。

#include <stdio.h>
void perror(const char *msg); // 先输出msg,然后接一个冒号,一个空格,errno对应的消息,一个换行符。

用户标识

UID

UID是一个数值,向系统标识各个不同的用户。系统管理员在确定一个用户的登录名的同时,确定其UID。用户不能更改其UID。

UID为0的用户为root用户。root用户拥有系统的自由支配权。

GID

GID是一个标识用户所在组的ID,也是由系统管理员在创建用户时分配的。组文件将组名映射为数值的GID,通常是/etc/group

SGID

SGID是附属组ID,UNIX允许一个用户同时属于多至16个其它的组。这些信息也存在组文件中。

信号

信号用于通知进程发生了某种情况。进程有3种处理信号的方式:

  1. 忽略信号。
  2. 按系统默认方式处理。
  3. 提供一个函数,信号发生时调用该函数,称为捕捉信号。

时间值

日历时间:从1970年1月1日00:00:00以来经过的秒数时间值,使用time_t保存。

进程时间:被称为CPU时间,用以度量进程使用的中央处理器资源。进程时间以时钟tick计算。当一个进程创建时,系统维护3个进程时间值:

  1. 时钟时间,进程消耗的总时间。
  2. 用户CPU时间,进程执行用户指令所用的时间。
  3. 系统CPU时间,进程执行内核程序所用的时间。

其中用户CPU时间与系统CPU时间之和称为CPU时间。

系统调用与库函数

系统调用是内核提供的良好定义、数量有限、直接进入内核的入口点。从应用角度考虑,可以将系统调用看做C函数。通用库函数通常调用一个或多个系统调用,其中一个关键区别在于,我们可以在需要的时候替换库函数,但无法替换系统调用。另一方面,系统调用通常提供简单而单一的功能,而库函数则更加复杂。