学习意义
输入/输出(I/O)是在主存和外部设备之间复制数据的过程。输入操作是从I/O设备复制数据到主存,而输出操作相反。
所有语言的运行时系统都提供执行I/O的较高级别的工具。
- 了解Unix I/O将帮助你理解其他的系统概念
Unix I/O
将设备映射为文件,允许linux内核引出一个简单低级的应用接口。
文件
绝对路径名:从根节点开始的路径。
相对路径名:以文件名开始,从当前工作目录开始的路径。
打开和关闭文件
open函数返回新文件描述符。
flags参数可为一位或更多位掩码的或,为写提供额外指示。
close函数:关闭一个打开的文件。
读和写文件
read函数从描述符fd的当前文件位置复制最多n个字节到内存位置buf。
write函数从内存位置buf复制最多n个字节到描述符fd的当前文件位置。
读取文件元数据
应用程序调取stat和fstat函数检索关于文件的信息(即文件的元数据)
共享文件
Linux内核用三个数据结构来表示打开的文件。
- 描述符表(descriptionor table)
每个进程都拥有一个独立的描述符表, 表项是由该进程打开的描述符来索引的,每个打开的描述符表项指向文件表中的一个表项。 - 文件表(file table)
打开文件的集合由该表来维护,所有进程共享该表,文件表有个表项,它是由三个部分组成,分别是:该文件的位置,描述符表的应用计数,和v-node表的表项指针。 - v-node表
所有进程共享该表。每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员。
多个描述符通过不同的文件表表项引用同一个文件。
父子进程共享文件,调用fork后,子进程有一个父进程描述符表的副本。
示例:
I/O重定向
①dup2函数
dup2函数复制描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在复制oldfd之前关闭newfd。
示例:
②相关程序示例:
⑴
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1;
int s = getpid() & 0x1;
char c1, c2;
char *fname = argv[1];
fd1 = Open(fname, O_RDONLY, 0);
Read(fd1, &c1, 1);
if (fork()) {
/* Parent */
sleep(s);
Read(fd1, &c2, 1);
printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
} else {
/* Child */
sleep(1-s);
Read(fd1, &c2, 1);
printf("Child: c1 = %c, c2 = %c\n", c1, c2);
}
return 0;
}
输入:
运行结果:
在fork之前,进程打开文件abcde.txt返回fd1,然后用fd1读取到了文件abcde.txt的第一个字符a并赋值给c1。
fork之后父进程调用sleep等待子进程结束。子进程继承了父进程打开的文件,所以子进程描述符表中的fd1和父进程的fd1表项内容相同。因此子进程再使用fd1读取文件abcde.txt时读到是第2个字符。
父进程在子进程结束后使用fd1读取文件abcde.txt读取第三个字符。
⑵
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char *fname = argv[1];
fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
Write(fd1, "pqrs", 4);
fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
Write(fd3, "jklmn", 5);
fd2 = dup(fd1); /* Allocates new descriptor */
Write(fd2, "wxyz", 4);
Write(fd3, "ef", 2);
Close(fd1);
Close(fd2);
Close(fd3);
return 0;
}
输入结果:
运行结果:pqrswxyznef
fd1写入“pqrs”,fd3在打开文件时用到了O_APPEND,所以fd3写入时不会覆盖原有的内容,而是在已有内容后附加新的内容,这样就写入了“jklmn"。文件内容变成了“pqrsjklmn”。
然后对于fd2,fd2 = dup(fd1),所以fd2使用fd1的光标,写入“wxyz”,文件内容变为“pqeswxyzn”。
再使用fd3附加在原有内容之后写入了”ef",文件内容变成了“pqrswxyznef"。