《UNIX 环境高级编程》学习笔记——UNIX 基础知识

UNIX环境高级编程——UNIX 基础知识

  • 引言
  • UNIX 体系结构
  • 登录
  • 文件和目录
  • 输入和输出
  • 程序和进程
  • 出错处理
  • 用户标识
  • 信号
  • 时间值
  • 系统调用和库函数

引言

所有操作系统都为它们所允许的程序提供服务。

典型的服务包括:执行新程序、打开文件、读文件、分配存储区以及获得当前时间等。

UNIX 体系结构

可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。
通常将这种软件称为内核,因为它相对较小,而且位于环境的核心。
《UNIX 环境高级编程》学习笔记——UNIX 基础知识_第1张图片
内核的接口被称为系统调用
公用函数库构建在系统调用接口之上,应用程序既可使用公用函数库,也可使用系统调用。

shell是一个特殊的应用程序,为运行其他应用程序提供了一个接口。

广义上,操作系统包括了内核嗯哼哼一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特性。
其他软件包括系统实用程序、应用程序、shell以及公用函数库等。

登录

1. 登录名
用户在登录UNIX系统时,先键入登录名,然后键入口令。

系统在其口令文件(通常是/etc/passwd文件)中查看登录名。

口令文件中的登录项由7个以冒号分隔的字段组成,依次是:登录名、加密口令、数字用户ID(205)、数字组ID(105)、注释字段、起始目录(/home/sar)以及shell程序(/bin/ksh)。

sar:x:205:105:Stephen Rago: /home/sar:/bin/ksh

目前,所有的系统已将加密口令移到另一个文件中。

2. shell

用户登录后,系统通常先显示一些系统信息,然后用户就可以向 shell 程序键入命令。(当用户登录时,某些系统启动一个视窗管理程序,但最终总会有一个shell程序运行在一个视窗中)。

shell是一个命令行解释器,它读取用户输入,然后执行命令。

shell的用户输入通常来自于终端(交互式shell),有时则来自于文件(称为shell脚本)。

《UNIX 环境高级编程》学习笔记——UNIX 基础知识_第2张图片
系统从口令文件中相应用户登录项的最后一个字段中了解到应该为该登录用户执行哪一个shell。

文件和目录

1. 文件系统

UNIX 文件系统是目录和文件的一种层次结构,所有东西的起点是称为的目录,这个目录的名称是一个字符“ / ”。

目录是一个包含目录项的文件。
在逻辑上,可以认为每个目录项都包含一个文件名,同时还包含说明该文件属性的信息。

文件属性是指文件类型(是普通文件还是目录等)、文件大小、文件所有者、文件权限(其他用户能否访问该文件)以及文件最后的修改时间等。

stat 和 fstat 函数返回包含所有文件属性的一个信息结构。

2. 文件名

目录中的各个名字称为文件名

只有斜线( / )和空字符这两个字符不能出现在文件名中。
斜线用来分隔构成路径的个文件名,空字符则用来终止一个路径名。

好的习惯还是只使用常用印刷字符的一个子集作为文件名字符。

为了移植性,POSIX.1 推荐将文件名限制在以下字符集之内: 字母(a ~ z、A ~ Z)、数字(0~9)、句点( . )、短横线( - )和下划线( _ )。

创建新目录时会自动创建了两个文件名: . (称为点)和 . . (称为点点)。
点指向当前目录,点点指向父目录。
在最高层次的根目录中,点点与点相同。

3. 路径名

由斜线分隔的一个或多个文件名组成的序列(也可以斜线开头)构成路径名,以斜线开头的路径名称为绝对路径名,否则称为相对路径名

相对路径名指向相对于当前目录的文件。

文件系统根的名字( / )是一个特殊的绝对路径名,它不包含文件名。

ls(1) 这种表示方法是 UNIX 系统的惯用方法,用以引用 UNIX 系统手册中的一个特定项。

ls(1) 引用第一部分中的 ls 项。
个部分通常用数字 1~8 编号,在每个部分中的各项则按字母顺序排列。

可用下面的命令查看 ls 命令手册页:

main l ls

main -sl ls

4. 工作目录

每个进程都有一个工作目录,有时称其为当前工作目录

所有相对路径名都从工作目录开始解释。
进程可以用 chdir 函数更改其工作目录。

例如,
相对路径名 doc/memo/joe 指的是当前工作目录中的 doc 目录中的 memo 目录中的文件(或目录) joe 。
从该路径名可以看出, doc 和 memo 都应当是目录,但是却不能分辨 joe 是文件还是目录。
路径名 /urs/lib/lint 是一个绝对路径名,它指的是目录中的 urs 目录中的 lib 目录中的文件(或目录)lint 。

5. 起始目录
登录时,工作目录设置为起始目录,该起始目录从口令文件中相应用户的登录项中取得。

输入和输出

1. 文件描述符

文件描述符通常是一个小的非负整数,内核用以标识一个特定进程正在访问的文件。

当内核打开一个现有文件或创建一个新文件时,它都返回一个文件描述符。
在读、写文件时,可以使用这个文件描述符。

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

每当运行一个新程序时,所有的 shell 都为其打开3个文件描述符,即标准输入标准输出以及标准错误

如果不做特殊处理,则这3个描述符都链接向终端。

大多数 shell 都提供一种方法,使其中任何一个或所有这3个描述符都能重定向到某个文件。

ls > file.list

执行 ls 命令,其标准输出重定向到名为 file.list 的文件。

3. 不带缓冲的 I/O

函数 open、read、write、lseek 以及 close 提供了不带缓冲的 I/O。这些函数都使用文件描述符。

4. 标准 I/O

标准 I/O 函数为那些不带缓冲的 I/O函数提供了一个带缓冲的接口。

使用标准 I/O函数无需担心如何选取最佳的缓冲区大小。
使用标准 I/O 函数还简化了对输入行的处理(常常发生在 UNIX 的应用程序中)。

标准 I/O函数库提供了使我们能够控制该库所使用的缓冲风格的函数。

最熟悉的标准 I/O函数是 printf 。
在调用 printf 的程序中,总是包含 ,该头文件包括了所有标准 I/O函数的原型。

程序和进程

1. 程序

程序是一个存储在磁盘上某个目录中的可执行文件。

内核使用 exec 函数(7个 exec函数之一),将程序读入内存,并执行程序。

2. 进程和进程 ID

程序的执行实例被称为进程

某些操作系统用任务表示正在被执行的程序。

UNIX 系统确保每个进程都有一个唯一的数字标识符,称为进程 ID(process ID)。
进程 ID 总是一个非负整数。

3. 进程控制

有3个用于进程控制的主要函数: fork、exec 和 waitpid 。(exec 函数有7种变体,但经常把它们统称为 exec 函数。)

4. 线程和线程 ID

通常,一个进程只有一个控制线程——某一时刻执行的一组机器指令。
多个控制线程也可以充分利用多处理其系统的并行能力。

一个进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。
因为它们能访问同一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性。

与进程相同,线程也用ID标识。
线程ID只在它所属的进程内起作用。
一个进程中的线程ID在另一个进程中没有意义。
当在一进程中对某个特定线程进行处理时,可以使用该线程的ID引用它。

出错处理

当UNIX系统函数出错时,通常会返回一个负值,而且整型变量 errno 通常被设置为具有特定信息的值。

文件中定了 errno 以及可以赋与它的各种常量。
这些常量都以字符 E 开头。

UNIX中,intro(2) 列出了所有出错常量。

POSIX 和 ISO C 将 errno 定义为一个符号,它扩展称为一个可修改的整型左值(lvalue)。它可以是一个包含出错编号的整数,也可以是一个返回出错编号指针的函数。

在支持线程的环境中,多个线程共享进程地址空间,每个线程都有属于它自己的局部 errno 以避免一个线程干扰另一个线程。

对于 errno 应当注意两条规则。

  • 第一条规则是:如果没有出错,其值不会被例程清除。因此,仅当函数的返回值指明出错时,才检验其值。
  • 地二条规则是:任何函数都不会将 errno 值设置为0,而且在 中定义的所有常量都不为0。

C标准定义了两个函数,打印出错信息:

  • strerror 函数将 reenum (通常就是 errno 值)映射为一个出错消息字符串,并且返回此字符串的指针。
  • perror 函数基于 errno 的当前值,在标准错误上产生一条出错消息,然后返回。
    它首先输出由 msg 指向的字符串,然后是一个冒号,一个空格,接着是对应于 errno 值的出错消息,最后是一个换行符。

出错恢复

可将在中定义的各种出错分成两类:致命性和非致命性的。

  • 对于致命性的错误,无法执行恢复动作。最多能做的是在用户屏幕上打印出一条出错消息或者将一条出错消息写入日志文件中,然后退回。
  • 对于非致命性的出错,有时可以较妥善地进行处理。大多数非致命性出错是暂时的(如资源短缺),当系统中的活动较少时,这种出错很可能不会发生。

与资源相关的非致命性出错包括: EAGAIN、ENFILE、ENOLCK、ENOSPC、EWOULDBLOCK,有时 ENOMEM 也是非致命性出错。
当 EBUSY 指明共享资源正在使用时,也可将它作为非致命性出错处理。
当 EINTR 中断一个慢速系统调用时,可将它作为非致命性出错处理。

对于资源相关的非致命性出错的典型恢复操作是延迟一段时间,然后重试。

用户标识

1. 用户 ID

口令文件登录项中的用户 ID 是一个数字,它向系统标识各个不同的用户。

系统管理员在确定一个用户的登录名的同时,确定其用户ID。
用户不能更改其用户 ID。
通常每个用户有一个唯一的用户 ID。

用户 ID 为0的用户为根用户超级用户
在口令文件中,通常有一个登录项,其登录名为 root ,称这种用户的特权为超级用户特权。

如果一个进程具有超级用户特权,则大多数文件权限检查都不再进行。
某些操作系统功能只向超级用户提供,超级用户对系统有自由的支配权。

2. 组 ID

口令文件登录项也包括用户的组ID(group ID),它是一个数值。
组ID也是由系统管理员在指定用户登录名时分配的。

组文件将组名映射为数值的组ID。组文件通常是 /etc/group 。

对于用户而言,使用名字比使用数值方便,所以口令文件包括了登录名和用户ID之间的映射关系,而组文件则包含了组名和组ID之间的映射关系。

程序调用 getuid 和 getgid 以返回用户ID和组ID。

3. 附属组 ID

大多数UNIX系统版本还允许一个用户属于另外一些组。
这一功能是从 4.3BSD 开始的,它允许一个用户属于多至16个其他的组。
登录时,读文件 /etc/group,寻找列有该用户作为其成员的前16个记录像就可以得到该用户的附属组ID

信号

信号(signal)用于通知进程发生了某种情况。

进程有以下3中处理信号的方式。

  • (1)忽略信号。
  • (2)按系统默认方式处理。对于除数为0,系统默认方式是终止该进程。
  • (3)提供一个函数,信号发生时调用该函数,这被称为捕捉该信号。

终端键盘上有两种产生信号的方法,分别称为中断键(interrupt key,通常是 Delete 键或 ctrl+C)和退出键(quit key , 通常是 Ctrl+\),它们被用于中断当前运行的进程。

另一种产生信号的方法是调用 kill 函数。

在一个进程中调用此函数就可向另一个进程发送一个信号。
限制:当向一个进程发送信号时,我们必须是那个进程的所有者或者是超级用户。

时间值

日历时间:
自1970.1.1 00:00:00这个特定时间以来所经过的秒数的累计值。
系统用基本数据类型 time_t 保存这种时间值。

进程时间:
也称为CPU时间,用于度量进程使用的中央处理器资源。
进程时间以时钟滴答计算。每秒曾经取为50/60/100个时钟滴答。
系统用基本数据类型 clock_t 保存这种时间值。

当度量一个进程的执行时间时,UNIX系统为一个进程维护了3个 进程时间值:

  • 时钟时间,又称墙上时钟时间,是进程运行的时间总量。
  • 用户CPU时间,执行用户指令所用的时间量。
  • 系统CPU时间是为该进程执行内核程序所经历的时间。

系统调用和库函数

系统调用和库函数都以C函数形式出现,都为应用程序提供服务,但其性质不同。
系统调用处于更底层,一般库函数会借助系统调用实现功能,反之,则不会。
系统调用通常提供一种最小接口,而库函数通常提供比较复杂的功能。

《UNIX 环境高级编程》学习笔记——UNIX 基础知识_第3张图片

学习参考资料:

《UNIX 环境高级编程》第3版

你可能感兴趣的:(UNIX,环境高级编程,unix,服务器,linux)