ffiles的详解
1.ffiles1
运行结果如下:
/*
$ gcc ffiles1.c csapp.h csapp.c -lpthread -o ffiles1
$ ./ffiles1 abcde.txt
c1 = a, c2 = a, c3 = b
*/
实际代码如下:
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char c1, c2, c3;
char *fname = argv[1];
fd1 = Open(fname, O_RDONLY, 0);
fd2 = Open(fname, O_RDONLY, 0);
fd3 = Open(fname, O_RDONLY, 0);
dup2(fd2, fd3);
Read(fd1, &c1, 1);
Read(fd2, &c2, 1);
Read(fd3, &c3, 1);
printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);
Close(fd1);
Close(fd2);
Close(fd3);
return 0;
}
main函数的两个参数,分别为int类型的argc(记录输入的参数个数),字符串数组的argv(存放传进的字符串)。举个例子,在运行这段代码时,我们输入了这个命令 ./ffiles1 abcde.txt,表示argc的值为2,而argv[0]为./ffiles1 argv[1]为abcde.txt。大概就是这个意思,接着继续看一下代码,定义了三个文件描述符(文件描述符是一个非负整数,每一个文件描述符都唯一对应了一个打开的文件,通过文件描述符可以准确打开文件。但是有三个描述符不可使用,分别是fd0:标准输入,fd1:标准输出,fd2:标准出错),以及将argv[1]的值赋给fname,即abcde.txt.然后均以只读的形式读取文件。(
o_rdonly read only 只读
o_wronly write only 只写
o_rdwr read write 可读可写
o_trunc 若文件存在则长度被截为0(属性不变)
o_creat:若文件不存在,则创建它的一个截断的(空)文件
o_append:在每次写操作前,设置文件位置到文件的结尾处,即追加。
)
dup2(fd2, fd3);
dup2函数复制描述符表表项oldfd到描述符表表项newfd,覆盖描述符表表项newfd之前的内容,如果newfd已经打开了,dup2会在复制oldfd之前关闭newfd.其标准形式为int dup2(int oldfd,int newfd)
因此在这里fd3将与fd2指向相同的文件。
Read(fd1, &c1, 1);
Read(fd2, &c2, 1);
Read(fd3, &c3, 1);
读取fd1中的一个字符,易得为a,而fd2中的字符也是a,但fd3中读出的字符是b,因为a已经被fd2读出来了。(若未指向同一文件,而是最后共同指向v-node表,那么文件都是互不干涉,相当于重新打开。而指向了同一文件,相当于把这个文件打开两次,记录第一次执行的操作)
简图如下:好丑
最后就是关闭所有的文件了
2.ffiles2
运行结果如下:(注:Parent与Child输出顺序不唯一)
/*
$ gcc ffiles2.c csapp.h csapp.c -lpthread -o ffiles2
$ ./ffiles2 abcde.txt
Parent: c1 = a, c2 = b
Child: c1 = a, c2 = c
*/
实际代码如下:
#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;
}
int s = getpid() & 0x1;
if (fork()) {
/* Parent */
sleep(s);
} else {
/* Child */
sleep(1-s);
获取pid并与0x1做与运算,得出结果为0或1,若为1,则表示父亲要休眠一秒钟,反之孩子要休眠一秒钟。
新创建的子进程几乎但不完全与父进程相同。
子进程可以得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本
包括代码,数据段,堆,共享库,以及任何打开文件描述符相同的副本
在ffiles2中,父进程与子进程是共享文件描述符的,所以相当于二者指向同一个文件,并且在fork之前要打开文件,不然没有文件描述符加以共享。因此,c1所读取字符为a,而之后只能读取在a之后的字符。
3.ffiles3
运行结果如下:
/*abcde.txt
pqrswxyznef
*/
实际代码如下:
#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;
}
S_IRUSR:使用者(拥有者)能够读取这个文件
S_IWUSR:使用者(拥有者)能够写这个文件
S_IXUSR:使用者(拥有者)能够执行这个文件
S_IRGPR:使用者(拥有者)所在组的成员能够读这个文件
S_IWGPR:使用者(拥有者)所在组的成员能够写这个文件
S_IXGPR:使用者(拥有者)所在组的成员能够执行这个文件
S_IROTH:其他人(任何人)能够读这个文件
S_IWOTH:其他人(任何人)能够写这个文件
S_IXOTH:其他人(任何人)能够执行这个文件
fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
Write(fd1, "pqrs", 4);
打开一个文件,如果不存在就创建它,如果存在就截断它,以可读可写的形式打开,返回一个文件描述符,向fd1里面写入pqrs。
fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
Write(fd3, "jklmn", 5);
注:fd2(fd1):二者指向同一个文件,但是文件描述符不一样
同样打开fname文件,但是以只写或者追加的方式打开。因此向fd3中写入jklmn.因为fd1与fd3打开的是相同的文件,所以写入fd3,也是在fd1中写入.
fd2 = dup(fd1); /* Allocates new descriptor */
Write(fd2, "wxyz", 4);
Write(fd3, "ef", 2);
将fd1复制至fd2,此时,fd2与fd1指向相同的文件,向fd2(fd1)中写入wxyz,因为上一次fd1写入的最后为s,所以会从s后面开始写,那么将会把jklm给覆盖掉。最后往fd3中写入ef,fd3上一次写入的最后字符为n,故此时会在n的后面继续往下写。所以最终的结果为pqrswxyznef.
简图如下:过于简单,看懂就行