Linux 文档访问原语

2008-02-23 05:06:32来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

经授权自 1999 年春季号 Linux 杂志重印。Linux 杂志版权任何,由 Infostrata Communications 出版。
POSIX API 最重要的一个抽象概念就是文档。尽管几乎任何的操作系统都将文档用于永久性存储器,但任何 Unix 版本通过文档抽象概念提供对大多数系统资源的访问。

更具体地说,这意味着 Linux 使用相同的一组系统调用来提供对设备(例如软盘和磁带设备)、网络资源(最常见的是 TCP/IP 连接)、系统终端,甚至内核状态信息的访问。感谢无所不在的系统调用,娴熟地使用和文档相关的调用对于每个 Linux 程式员来说都很重要。让我们仔细查看一下文档 API 背后的一些基本概念,并描述最重要的文档相关系统调用。

Linux 提供许多不同种类的文档。最常见的类型就简称为常规文档,他存储大量用于以后访问的信息。您所使用的绝大部分文档 -- 例如可执行文档(如 /bin/vi)、数据文档(如 /etc/passwd)和系统二进制文档(如 /lib/libc.so.6)-- 都是常规文档。他们通常驻留在磁盘上的某处,但我们稍后会发现,并不一定都是这种情况。

另一种文档类型是目录,他包含了一个其他文档及其位置的列表。使用 ls 命令列出目录中的文档时,他打开该目录的文档,并打印出他所包含的任何文档的信息。

其他文档类型包括块设备(表示文档系统高速缓存的设备,例如硬盘驱动器)、字符设备(表示非高速缓存的设备,例如磁带驱动器、鼠标和系统终端)、管道和套接字(允许进程相互之间对话),连同符号链接(允许文档在目录层次结构中有多个名称)。

大多数文档都有一个或多个引用他们的符号名。这些符号名是一组由 / 字符定界的字符串,并向内核标识文档。他们是 Linux 用户所熟悉的路径名;例如,路径名 /home/ewt/article 引用的是我手提电脑中包含这篇文章文本的文档。没有两个文档能够共享相同的名称(但单一文档能够有多个名称),因此路径名唯一地标识单一文档。

进程能够访问的每个文档都由一个小的非负整数标识,称为“文档描述符”。文档描述符由打开文档的系统调用创建,并由从当前进程创建的新子进程继承。就是说,当进程启动了一个新程式时,原始进程的打开文档通常是由新程式继承的。

按照约定,大多数程式保留前三个文档描述符(0、1 和 2)用于特别目的 -- 访问所谓的标准输出、标准输出和标准错误流。文档描述符 0 是标准输入,这里许多程式都将从外部世界接收输入。文档描述符 1 是标准输出。大多数程式在这里显示正常的输出。对于和错误情况相关的输出,使用文档描述符 2(标准错误)。

任何习惯使用 Linux shell 的人都曾看到过标准输入、输出和错误文档描述符的使用。通常,shell 运行命令时带文档描述符 0、1 和 2,都是指 shell 的终端。当使用 > 字符指示 shell 将一个程式的输出发送给另一个程式时,shell 在调用新程式之前打开该文档作为文档描述符 1。这将导致程式将他的输出发送给指定的文档而不是用户终端;其妙处是,对于程式本身,这是透明的!

和之类似,"<" 字符指示 shell 使用特定的文档作为文档描述符 0。这样就强迫程式从该文档中读取他的输入;这两种情况下,任何来自程式的错误仍将出现在终端上,如同他们在文档描述符 2 的情况下发送给标准错误相同。(在 "bash" shell 中,能够使用 2> 而不是 > 将标准错误重定向)。这种类型的文档重定向是 Linux 命令行最强大的特性之一。

使用任何和文档相关的系统调用之前,程式应该包括 ;他们为最普遍的文档例程提供了函数原型和常数。在下面的示例代码中,我们假设每个程式开始处都有



#include
#include


首先,让我们了解如何读写文档。凭直觉就能够知道,read() 和 write() 系统调用是执行这些操作的最常用方法。这两种系统调用将有三个自变量:要访问的文档描述符、指向要读写的信息的指针连同应该读写的字符数。返回成功读写的字符数。清单 1 说明了一个简单的程式,他从标准输入(文档描述符 0)中读取一行,并将他写入标准输出(文档描述符 1):


清单 1:

void main(void) {
char buf[100];
int num;

num = read(0, buf, sizeof(buf));
write(1, "I got: ", 7); /* Length of "I got: " is 7! */
write(1, buf, num);
}



关于这个处理有两个值得注意的问题。首先,我们需要 read() 返回 100 个字符,但假如我们运行这个程式,只有在用户按下了 "enter" 键以后才能获得输入。许多文档操作都根据最好效果工作:他们尝试返回程式需要的任何信息,但只有部分能够成功。缺省情况下,终端配置成一旦存在 "\n" 或新行符(通过按 "enter" 键产生)时,就从 read() 调用返回。这实际上很方便,因为大多数用户都希望程式无论如何都是面向行的。但常规数据文档并非如此,假如依靠他就可能产生不可预料的结果。

另一个要注意的问题是我们不必在显示输出后写一个 \n。read() 调用给了我们来自用户的 \n,只将那个 \n 通过 write() 写回标准输出。假如您希望在没有新行符的情况下看到发生的事件,尝试将最后一行改为



write(1, buf, num - 1);


有关这个简单示例的最后一点:buf 绝对不包含实际的 C 字符串。C 字符串由标记字符串结束的单一 \0 字符终止。因为 read() 不将 \0 添加到缓冲区的结尾,在 read() 上使用 strlen()(或任何其他 C 字符串函数)将可能铸成大错!这种行为能够让 read() 和 write() 对包括 \0 字符的数据处理,而这对于一般字符串函数来说是不可能的。

read() 和 write() 系统调用能够对绝大多数文档起作用。但他们不对目录起作用,目录应该通过特别函数(例如 readdir())来访问。另外,read() 和 write() 对于某些类型的套接字也不起作用。

某些文档,例如常规文档和块设备文档,使用文档指针的概念。他指定在文档中,下一个 read() 调用从哪里读取,下一个 write() 调用从哪里写入。read() 或 write() 后,文档指针随着已处理的字符数(在内部,通过内核)增加。这样,使用单一循环就能够方便地读取文档中的任何数据。清单 2 就是示例:


清单 2:

char buffer[1024];

while ((num = read(0, buffer, 1024))) {

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: Linux 的编程常识

下一篇: Linux Data Structures(Linux数据结构)