转自:https://blog.csdn.net/qq_17019203/article/details/85051627
问题:open(2)函数打开文件是否将文件内容加载到内存空间
首先,文件打开后都会产生一个文件描述符fd,这个文件描述符其实是记录在PCB的文件描述符表中,而这个文件描述符实质上是一个结构体,用来存放跟打开文件相关的信息,基于此前提,我产生了两种假设
1、文件描述符结构体中只存储了文件在硬盘中的相应地址信息,并不将文件内容加载到内存中,这样做的好处是减少内存空间的占用,但大大增加了运行的时间(cpu存取内存数据的速度约ns级别,cpu存取硬盘数据的速度约为60000ns)。
2、文件描述符结构体为文件在内存中分配了地址空间来存放文件内容,这样做的好处是增加了运行速度,不足是当文件太大时严重占用内存空间。
3、如果问题2正确就产生了另一个问题,这块地址空间的大小和分配规则是什么?
接下来是我的探索过程和代码
第一步:我用open(2)函数打开了一个文件,设置连续读取文件,读取的间隔为3秒。
第二步:在读取的过程中我将还没被读取完的文件删除(另外打开一个bash,用rm命令删除)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd;
char buf[128];
int read_size,write_size;
//open a file with read and write
fd = open(argv[1],O_RDWR);
if(fd == -1)
{
perror("open");
return 1;
}
while(1)
{
read_size = read(fd,buf,2);
if(read_size == 0) break;
write(1,buf,read_size);
sleep(3);
}
//close fd
close(fd);
return 0;
}
下面是运行结果:
bash 1
[sun@localhost file_func]$ ./a.out hello
#include <stdio.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int link_flag;
link_flag = link(argv[1],argv[2]);
if(link_flag == -1)
{
perror("link");
return 1;
}
if(link_flag == 0)
printf("creat hard link success...\n");
return 0;
}
bash 2
[sun@localhost file_func]$ rm hello
通过以上的结果我们能看出,在文件还没被读取完时将文件删除不会影响读取程序继续读取文件内容
结论1:用open(2)函数打开文件会将文件的内容加载到内存空间
接下来我们要探索的是,这个分配的地址空间大小上限是多少,文件类型的不同会不会产生不同的结果等问题
补充:
以上实验还存在一种可能性,就是rm 的原理和unlink是一样的(即rm是通过unlink封装的),等待程序运行完才会删除文件(阻塞删除),虽然删除文件操作是在文件执行完以后,但只要执行unlink文件马上就会消失看不到(在系统界面上消失,但在硬盘上还存在)
为了探索rm是不是通过unlink封装,我又进行了以下操作
一、创建虚拟映像使用rm命令
二、通过strace命令获取rm运行时产生的系统调用,命令如下
strace -f -F -o ./log.txt a.out
三、在系统调用里查找是否存在unlink函数
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
//creat child process
pid = fork();
if(pid == -1)
{
perror("fork");
return 1;
}
//child process
if(pid == 0)
{
//load a memory
int ex_flag=execlp("/bin/rm","rm","hello",NULL);
if(ex_flag == -1)
{
perror("execlp");
return 2;
}
return 0;
}
//father process
else
{
wait(NULL);
}
return 0;
}
通过strace产生的结果如下
mmap(0x3f02d8e000, 18600, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3f02d8e000
13697 close(3) = 0
13697 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0665bb5000
13697 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0665bb4000
13697 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0665bb3000
13697 arch_prctl(ARCH_SET_FS, 0x7f0665bb4700) = 0
13697 mprotect(0x3f02d89000, 16384, PROT_READ) = 0
13697 mprotect(0x3f0241f000, 4096, PROT_READ) = 0
13697 munmap(0x7f0665bb6000, 51177) = 0
13697 brk(0) = 0x187b000
13697 brk(0x189c000) = 0x189c000
13697 open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
13697 fstat(3, {st_mode=S_IFREG|0644, st_size=99158576, ...}) = 0
13697 mmap(NULL, 99158576, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f065fd22000
13697 close(3) = 0
13697 ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
13697 newfstatat(AT_FDCWD, "hello", {st_mode=S_IFREG|0666, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
13697 geteuid() = 500
13697 newfstatat(AT_FDCWD, "hello", {st_mode=S_IFREG|0666, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
13697 faccessat(AT_FDCWD, "hello", W_OK) = 0
13697 unlinkat(AT_FDCWD, "hello", 0) = 0
13697 close(0) = 0
13697 close(1) = 0
13697 close(2) = 0
13697 exit_group(0) = ?
13696 <... wait4 resumed> NULL, 0, NULL) = 13697
13696 --- SIGCHLD (Child exited) @ 0 (0) ---
13696 exit_group(0) = ?
我们可以看到倒数第8行出现了一个unlinkat系统调用函数,根据unlinkat函数的定义,第三个参数取0时,unlinkat等价于unlink。
通过以上分析,我有以下结论:
1、open(2)函数在打开文件时,是否将文件内容加载到内存空间目前无法得知,在学习的时候老师的理论是linux在读取文件时候会将文件的地址内容加载到内存,而非文件的内容。
2、第一个实验结果主观上来看,因为在删除掉被读取文件后文件还能继续读取,所以open函数打开文件是将文件内容加载到内存空间的。
但通过客观分析,我们发现我们所认为的“rm删除文件,文件不在系统界面显示就是被删除了”这种想法是不对的,因为rm删除文件是调用了unlinkat系统函数,所以虽然在文件被读取时候我们看不到被读取文件在系统中显示,但此时文件还是存在于硬盘中的,只有当被读取文件被读取完成后才会真正的被删除。
————————————————
版权声明:本文为CSDN博主「虚渊玄」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_17019203/article/details/85051627