CSAPP ffiles理解

1. ffliles1.c

#include "csapp.h"

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;
}
  1. open(),该函数将 filename 转换为一个文件描述符,并且返回描述符的数字。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcnt1.h>

int open(char *filename, int flags, mode_t mode);

//返回:若成功则为新文件描述符,若出错为-1
  1. flag参数指明了进程打算如何访问这个文件,可以是一个,也可以是更多位掩码的或,为写提供一些额外的指示。

O_TDONLY:只读。
O_WRONLY:只写。
O_RDWR:可读可写。
O_CREAT:如果文件不存在,就创建它的一个截断的(truncated)(空)文件
O_TRUNC:如果文件已经存在,就截断它。
O_APPEND:在每次写操作前,设置文件位置到文件的结尾处。
例如:
fd = Open(“foo.txt”, O_WRONLY | O_APPEND, 0);

3.mode参数指定了新文件的访问权限

掩码描述
S_IRUSR使用者(拥有者)能够读这个文件
S_IWUSR使用者(拥有者)能够写这个文件
S_IXUSR使用者(拥有者)能够执行这个文件
S_IRGRP拥有者所在组的成员能够读这个文件
S_IWGRP拥有者所在组的成员能够写这个文件
S_IXGRP拥有者所在组的成员能够执行这个文件
S_IROTH其他人(任何人)能够读这个文件
S_IWOTH其他人(任何人)能够写这个文件
S_IXOTH其他人(任何人)能够执行这个文件
  1. dup2()

dup2()用来复制参数oldfd 所指的文件描述词, 并将它拷贝至参数newfd 后一块返回. 若参数newfd为一已打开的文件描述词, 则newfd 所指的文件会先被关闭。 dup2()所复制的文件描述词, 与原来的文件描述词共享各种文件状态。

所以在此代码中 fd1fd2fd3对文件的操作都是只读。同时它们的所获得的文件描述符是不一样的,但最终都指向同一个文件。在它们读的过程中,虽然都是读同一个文件,但是光标所指的位置是未必相同的,这取决于它们自己所读到的位置,即文件的状态不同,相互没有影响。但是当我们使用了dup2(fd2, fd3);,那么就会把fd2的文件描述词赋给fd3,此时它们共享文件状态,所以当fd2读走一个字符时,fd3会接着它读走下一个字符,而不是重头开始读取。
运行结果:

$ gcc ffiles1.c csapp.h csapp.c -lpthread -o ffiles1
$ ./ffiles1 abcde.txt
c1 = a, c2 = a, c3 = b

2. ffiles2.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;
}
  1. int s = getpid() & 0x1;这一句获取了当前进程的进程号后,与0x1相与,相当于是获取当前进程的进程号的最后一位赋值给s,这样得到的结果是随机的,因为进程号的最后一位我们无法确定是什么,0~9都有可能,而这也就导致后面sleep(s);父进程休眠的时间是不确定的。
  2. 子进程与父进程都是用fd1来读取文本,所以它们是同一个文件状态,一个先读取走了一个字符,那么下一个就会接着读取下一字符。

运行结果如下:

$ gcc ffiles2.c csapp.h csapp.c -lpthread -o ffiles2
$ ./ffiles2 abcde.txt
Parent: c1 = a, c2 = b
 Child: c1 = a, c2 = c

在上述的运行结果中,是父进程先运行,所以它就会读取a后面的字符b,而当子进程运行时,就会接着父进程读的位置,读走下一个字符,即字符c

3. ffliles3.c

#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;
}
  1. O_CREAT|O_TRUNC|O_RDWR最终表示的是创建一个空白的文件,可读可写。S_IRUSR|S_IWUSR表示对使用者的权限是可读可写。
  2. fd3 = Open(fname, O_APPEND|O_WRONLY, 0);则表示打开文件后再文件后添加,且只写。

运行结果如下:

/*abcde.txt
pqrswxyznef
*/

这部分代码主要是过程较前面两个会复杂些,那么我们就来一起分析一下吧。

  1. 文件最开始的内容是abcde,但是当fd1打开该文件后,由于flag为O_CREAT|O_TRUNC|O_RDWR,那么打开后就会将里面的内容全部截断,即清空,此时得到的文件就是一个空的文本文件。
  2. Write(fd1, "pqrs", 4);fd1在文件中写了四个字符,那么此时的文件中的内容就为 pqrs,并且fd1的光标指在 s 的后面。
  3. Write(fd3, "jklmn", 5);此时fd3在文件中写入字符,特别注意,在之前的分析中,fd3在文件中的写入是设置文件位置到文件的结尾处。所以此时会接着文件的末尾写,所以最后文件的内容即会更改为 pqrsjklmn
  4. fd2 = dup(fd1);那么此时fd2将会指向fd1的文件描述符,两个共享文件状态

dup用来复制参数oldfd所指的文件描述符。当复制成功是,返回最小的尚未被使用过的文件描述符,若有错误则返回-1.

  1. Write(fd2, "wxyz", 4);所以当执行这一句时,光标的位置会和fd1的文件位置是一样的,即字符s后面,所以字符也将从该位置开始写入。所以此时文件的内容为 pqrswxyzn
  2. Write(fd3, "ef", 2); 此时fd3依旧是往文件的末尾添加,最终文件的内容为 pqrswxyznef
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值