观海听涛
Waiting for you

UNIX高级编程与网络编程读书笔记(一)

30 Mar 2014

最近在看UNIX环境高级编程UNIX网络编程 卷一。每翻开新的一页,就会学到很多,读起来非常的刺激。

在看这两本书的时候,我有一点感悟。 1. 仅仅实现功能,不是一个完整的系统,功能只是系统中的一个组成部分。一个完整的系统必须考虑到所有可能会出现的情况,一个具有较高鲁棒性的系统才配称作系统。 2. 仅仅知道一个函数能完成什么功能是远远不够的,必须知道为了完成这个功能,函数做了什么。 3. 网络编程中对TCP状态的把握很重要。

下面是一些具体的笔记。

文件IO和标准IO库

文件IO是针对文件描述符的。文件IO是不带缓存的,也就是指每个read和write都调用内核中的一个系统调用。很显然不带缓存将会频繁的调用内核中的系统调用,造成效率比较低。而标准IO库提供缓存机制,以减少使用read和write调用内核系统调用的数量。

对于标准IO库来说,它们的操作是围绕着流进行的。文件IO中使用open函数打开一个文件,在标准IO库中则使用fopen函数打开一个文件。使用它打开一个文件的同时,一个流就和一个文件相结合了。fopen函数返回一个指向FILE结构体的指针。这个结构体包括IO库为管理该流所需的所有信息,比如实际IO的文件描述符,指向流缓存的指针,缓存的长度,当前缓存中的字符数,出错标志等。对于每一个进程来说,系统都为他们预定义了三个流,分别是标准输入,标准输出,标准出错,可通过预定义文件指针stdin,stdout,stderr使用它们。

标准IO库中的流是提供缓存的,而且系统自动提供缓存管理。有三种类型的缓存:

  1. 全缓存。即当填满标准IO缓存后才进行实际IO操作。
  2. 行缓存。当在输入和输出中遇到新行符时,标准IO库执行实际的IO操作。需要注意的是,因为标准IO库用来收集每一行缓存的长度是固定的,所以只要填满了该缓存,即使还没有写入一个新行符,也将进行实际的IO操作。
  3. 不带缓存。典型的应用是在标准出错流上,因为出错信息需要尽快的显示出来。

以下三个函数可用于一次读一个字符:

int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);

前两个函数的区别是getc是被实现为宏的,而fgets不是宏,也就是说,调用fgets所需的时间一般会长于调用getc。而函数getchar则相当于getc(stdin),即从标准输入读入一个字符。

下面两个函数提供每次输入一行的功能:

char *fgets(char *buf,int n,FILE *fp);
char *gets(char *buf);

gets从标准输入读,而fgets则从指定的流读。gets是一个不被推荐使用的函数,因为由于它不能指定缓存的长度,这样很可能会造成缓存越界。推荐总是使用fgets和fputs,即从指定流输入或输出一行。

从书中给的数据来看,使用流缓存的程序比不使用缓存的程序要快得多,虽然两者的函数调用次数相同,但是使用流缓存的程序引起的系统调用次数远远低于函数调用次数,而系统调用与普通的函数调用相比是很花费时间的。