轉自: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