CSAPP之系统IO
1.UNIX I/O
定义:所有的I/O设备,如网络、磁盘和终端,都被模型化为文件,而所有的输入和输出都被当做相应文件的读和写。
这种将设备映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行:
-
1.打开文件,内核返回一个非负整数的文件描述符,通过对此文件描述符对文件进行所有操作。 2.改变当前的文件位置,文件开始位置为文件偏移量,应用程序通过seek操作,可设置文件的当前位置为k。 3.读写文件,读操作:从文件复制n个字节到内存,从当前文件位置k开始,然后将k增加到k+n;写操作:从内存复制n个字节到文件,当前文件位置为k,然后更新k 4.关闭文件。当应用完成对文件的访问后,通知内核关闭这个文件。内核会释放文件打开时创建的数据结构,将描述符恢复到描述符池中
2.打开和关闭文件
1.文件:
普通文件包含任意数据,应用程序常区分文本文件和二进制文件,文本文件是只含ASCII或Unicode字符的普通文件,二进制文件是所有其他的文件。对内核而言,文本文件和二进制文件没区别
目录是包含一组链接的文件。其中每个链接都将一个文件名映射到一个文件,这个文件也可能是一个另一个目录。每个目录至少含有两个条目:“.”是到该目录自身的链接,“…”是到父目录的链接
打开文件
-
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int open(char *filename,int flags,mode_t mode);//返回值:成功返回文件描述符,出错-1
flags参数:
- O_RDONLY: 只读
- O_WRONLY: 只写
- O_RDWR: 可读可写
- O_CREAT: 若文件不存在,则创建
- O_TRUNC: 若文件已存在,就截断它.
- O_APPEND: 追加
例:fd = open(“foo.txt”,O_RDONLY,0);//以读的方式打开一个已存在的文件
mode参数 ——— 指定新文件的访问权限位
关闭文件
-
#include <unistd.h> int close(int fd); 返回:若成功则为0,若出错为-1
3.读写文件
-
#include <unistd.h> ssize_t read(int fd, void *buf, size_t n); ssize_t write(int fd, const void *buf, size_t n);
read函数:
从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf,返回值-1表示错误,返回值0表示EOF, 否则,返回实际传送的字节数量.
write函数:
从存储器位置buf拷贝至多n个字节到描述符fd的当前文件位置.
read和write传送的字节数少于要求的字节数情况
- 读时遇到EOF(end-of-file),如该文件从当前位置开始只有20个字节,而我们要读50个字节
- 从终端读文本行,如果打开文件是与终端相关联的(如键盘和显示器),那么每个read函数将一次传送一个文本行,返回的不足值等于文本行的大小
- 读和写网络套接字(socket)。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回值不足值,对Linux管道(pipe)调用read和write时,也可能出现不足值。
4.用RIO包
RIO提供两类不同函数
-
无缓冲的输入输出函数。这些函数直接在内存和文件之间传送数据,没有应用级缓冲。它们对将二进制数据读写到网络和从网络中读写二进制数据较有用。
-
带缓冲的输入函数。这些函数允许你高效地从文件中读取文本行和二进制数据这些文件的内容缓存在应用级缓冲区内,类似于为printf这样的标准I/O函数提供的缓冲区。
5.读取文件元数据
应用程序通过调用stat和fstat函数,检索到关于文件的信息(有时也称为文件的元数据)
-
#include<unistd.h> #include<sys/stat.h> int stat(const char *filename,struct stat *buf); int fstat(int fd,struct stat *buf);//返回值:成功返回0,出错-1
6.读取目录内容
1.读取目录
#include<sys/types.h>
#include<dirent.h>
DIR *opendir(const char *name);//返回值:若成功,则为处理的指针,若出错,则为NULL
函数opendir以路径名为参数,返回指向目录流的指针。
#include<dirent.h>
struct dirent *readdir(DIR *dirp);//返回:若成功,则为指向下一个目录项的指针;若没有更多的目录项或出错,则为NULL
每次对readdir调用返回的都是指向流dirp中下一个目录项的指针。每个目录项结构如下:
struct dirent{
ino_t d_ino;//文件位置
char d_name[256];//文件名
}
若出错,readdir返回NULL,并设置errno,检查自调用readdir以来errno是否被修改过可区分错误和流结束
2.关闭目录
#include<dirent.h>
int closedir(DIR *dirp);//返回:成功0,错误-1
7.共享目录
可以用许多不同的方式来共享Linux文件。内核用三个相关的数据结构来表示打开的文件:
-
描述符表。每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表项指向文件表中的一个表项。
-
文件表。打开文件的集合是由一张文件表来表示的,所有进程共享这张表。每个文件表的表项组成包括当前的文件位置、引用计数,以及一个指向v-node表中对应表项的指针。
-
v-node表,同文件表一样,所有的进程共享这张v-node表。每个表项包含stat结构中的大多数信息,包括st_mode和st_size
多个描述符通过不同的文件表项来引用同一个文件
8.重定向
Unix Shell提供了I/0重定向操作符,允许用户将磁盘文件和标准输入输出联系起来
Unix I/O提供dup2函数支持重定向
-
#include <unistd.h> int dup2(int oldfd, int newfd); 返回: 若成功则为非负的描述符,出差则为-1
dup2拷贝描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。若newfd已经打开,dup2会在拷贝oldfd之前关闭newfd.
总结
系统io这一块内容更为繁杂,需要系统规范的浏览整个章节,才能对它有一定的了解。任重而道远!!!