系统级I/O和网络
I/O(输入/输出)是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。输入操作是从I/O设备拷贝数据到主存,而输出操作是从主存拷贝数据到I/O设备。所有语言的运行时系统都提供执行I/O的较高级别的工具。
一个Unix文件就是一个个字节的序列:。所有的I/O设备,如网络、磁盘和终端,都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行:
- 打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中表示这个文件。内核会记录有关这个打开文件的所有信息,应用程序只需记住这个描述符。
Unix外壳创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2)。头文件<unistd.h>定义了常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,它们可用来代替显式的描述符值。
-
改变当前的文件位置。对于每个打开的文件,内核都保持着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显示地设置文件的当前位置k。
-
读写文件。一个读操作就是从文件拷贝n>0个字节到存储器,从当前文件位置k开始,然后将k增加到k+n。类似地,写操作就是从存储器拷贝n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
给定一个大小为m字节的文件,当k>=m时执行读操作会触发一个称为end-of-file(EOF)的条件,应用程序能检测到这个条件。在文件结尾处并没有明确的“EOF符号”。
- 关闭文件。当应用程序完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放他们的存储器资源。
套接字接口(socket interface)是一组函数,它们和Unix I/O函数结合起来,用以创建网络应用。
套接字接口概述从Unix内核的角度来看,一个套接字就是通信的一个端点。从Unix程序的角度来看,套接字就是一个有相应描述符的打开文件。
struct sockaddr {
unsigned short sa_family; // protocal family
char sa_data[14]; // address data
};
struct sockaddr_in {
unsigned short sin_family; // address family (always AF_INET)
unsigned short sin_port; // port number in network byte order
struct in_addr sin_addr; // IP address in network byte order
unsigned char sin_zero[8]; // pad to sizeof(struct sockaddr)
};
因特网的套接字地址存放在类型为sockaddr_in的16字节结构中。对于英特网应用,sin_family成员是AF_INET,sin_port成员是一个16位的端口号,而sin_addr成员就是一个32位的IP地址。IP地址和端口号总是以网络字节顺序(大端法)存放的。
_in 后缀是互联网(internet)的缩写,而不是输入(input)的缩写。