本章对一些术语进行简要说明并给出实例。

文件和目录

stat和fstat函数返回包含所有文件属性的一个信息结构。第四章将详细说明文件的各种属性。
目录项的逻辑视图与实际上的磁盘存放方式是不同的。UNIX文件系统大多数实现并不在目录项中存放属性,这是权衡于硬连接带来的一些问题。第四章将讨论。

文件名

只有斜线和空字符不能出现在文件名中。斜线用来分割路径名,空字符终止一个路径名。
opendir函数与readdir函数,包含于dirent.h中,并且包含对dirent结构的定义。
opendir函数返回只想DIR结构的指针。将指针传送给readdir函数,使用循环读取每个目录项,返回指向dirent结构的指针,如果没有了则返回null指针。此目录项顺序非首字母排序。

工作目录

working directory,或current working directory。进程可以使用chdir函数更改工作目录。
登陆时cwd设置为起始目录home directory。该目录从口令文件(通常是/etc/passwd)中相应用户的登录项中取得。

输入输出

文件描述符

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

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

运行一个新程序时shell都将打开3个文件描述符,即以上三者。如果没有特殊处理,则他们都链接向终端。shell可以提供一种方法将它们重新定向到某个文件,例如:

1
ls > file.list

这个就是将标准输出重新定向到file.list文件。

不带缓冲IO

open、read、write、lseek和close函数提供了不带缓冲的IO,这些函数都使用文件描述符。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <unistd.h>
#include <stdio.h>

#define BUFFSIZE 4096

int main() {
int n;
char buf[BUFFSIZE];

while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) {
if (write(STDOUT_FILENO, buf, n) != 0) {
printf("write err.\n");
}
}

if (n < 0) {
printf("read err.\n");
}

return 0;
}

1676294805711-18b6895d-659b-428c-a56a-568e1c2b4583
1676294883035-be1ef918-e451-45e9-b26b-8d6f9656583a
1676294923547-b5592e83-131c-435a-a794-af4098b20d26
1676294940071-dc7483b0-a9de-49d4-a41a-698f4a054ee9
1676294945525-8bcd2209-8566-41e9-84c0-7b774b3c4605

标准IO

1676295056455-b4f5c42d-04b5-4bce-8cae-8309fa251384
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main() {
int c;

while ((c = getc(stdin)) != EOF) {
if (putc(c, stdout) == EOF) {
printf("output error\n");
}
}

if (ferror(stdin)) {
printf("input error\n");
}

return 0;
}

1676295440921-484bf32a-de19-42b8-9b25-a8c38ac8273c

程序和进程

程序

程序是个文件,内核使用7个exec函数中的一个将其读入内存并执行。

进程和进程ID

程序运行实例被称为进程,不多说了。
UNIX系统确保每个进程都有一个唯一的数字成为process ID,总是一个非负整数。
使用getpid函数返回一个pid_t类型数据,按照标准它被存在一个long中。

程序控制

fork、exec(包含7个变体)和waitpid函数是用于进程控制的三个主要函数。
例子:
1676296112483-1547bd25-449c-496c-9ee5-c68a51c129cc
1676296121439-f0f073bd-e90b-4bc6-8a26-8f5972f3fd1e
1676296137706-85427fce-1713-49c7-bb9a-b67e7f7805dd

线程和线程ID

线程是对某个特定进程而言的,因此同一进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。
线程也用ID标识,只是只在所属的进程内起作用。

出错处理

UNIX系统函数出错时通常返回一个负值,而且整型变量errno通常被设置为具有特定信息的值。有的函数例如大多数返回指向对象指针的函数在出错时会返回null指针。
errno.h中定义了errno以及各种常量,这些常量都以E开头。
每个线程有属于自己的局部errno以避免相互干扰。
1676297077078-1390b5c6-bdbb-4938-9562-9e697eba20ec
注意:

  1. 没有出错,其值不会被清除,因此进党函数返回出错才检验其值。
  2. 任何函数不会将errno设置为0,并且errno.h中的所有常量都非0。

1676297179780-0f652028-d836-422a-95ba-c8414d82c0c1
例子:
1676297370850-f057c217-a40e-4b24-a78c-4c9bd66e09a2
1676297419675-7c068aa1-35ef-4c1d-926a-147a6c2ac848

出错恢复

可以将errno.h中的出错分成两类:

  1. 致命性:无法执行恢复,最多只能输出一条出错,然后quit。
  2. 非致命性:可以较妥善地处理。

1676297582892-3c29dfa6-afb4-4caf-9522-5be851c74394

用户标识

用户ID

user ID是一个数值,用于向系统标识各个不同的用户。用户不能更改ID。
0为root。管理员在确定一个用户登陆名的同时,user ID也就确定了。

组ID

group ID也是在指定用户登录名时分配的。
组文件将组名映射为数值的组ID。通常是/etc/group
现今UNIX系统使用32位整型表示user ID和group ID。
例子:
1676298050565-f4ed29d5-5524-44b8-aadc-0fa6b563f68e

附属组ID

1676298109216-34c15428-2e10-4172-a63f-fb6bd3c5d129

信号

signal(信号)用于通知进程。例如除数为0时进程将收到浮点异常SIGFPE
三种处理方式:

  1. 忽略信号:不推荐使用。
  2. 按系统默认方式处理:除数为0默认处理是终止该进程。
  3. 提供一个函数,信号发生时调用该函数,这被称为捕捉。

终端键盘有两种产生信号的方法:interrupt key也就是中断键通常是Delete^Cquit key也就是退出键通常是^\,用于中断当前运行的进程。
调用kill函数可以向另一个进程发送一个信号。前提是我们必须是那个进程的所有者或者是超级用户。
例子:
1676298732109-d397bcaf-ec77-468c-80db-94686465958c
1676298739243-7e2c7d7b-90e6-4f46-bddf-c2881fcbfbe2

时间值

  1. 日历时间:从协调世界时(Coordinated Universal Time, UTC)1970年1月1日00:00:00 开始到现在的秒数。系统基本数据类型time_t用于保存这种时间值。
  2. 进程时间:也被称为CPU时间,用以度量进程使用的CPU资源。其以时钟滴答计算。每秒钟曾经取为50、60或100个时钟滴答。使用clock_t保存这种时间值。

UNIX系统为每个进程维护了3个时间值:

  • 时钟时间
  • 用户CPU时间
  • 系统CPU时间

时钟时间(wall clock time),是进程运行时间总量。
用户CPU时间是执行用户指令所用的时间量。同理系统CPU时间就是执行内核程序的时间。两者之和被称为CPU时间。
使用time命令获取要执行的命令的三个时间。

系统调用和库函数

Linux 3.2.0 提供了380个系统调用,FreeBSD 8.0 提供的系统调用超过 450 个。
UNIX为每个系统调用在标准C库中设置一个具有同样名字的函数。从应用角度考虑,可将系统调用视为 C 函数(我怎么感觉说反了)。
多的不讲,一张图:
1676300144839-3d3b1d91-0b12-46ec-a2d2-78f2962bbbf9

⬆︎TOP