日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

IO那些事01-IO总述和文件描述符

發布時間:2024/3/12 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IO那些事01-IO总述和文件描述符 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

VFS

內核既管內存,又管磁盤IO。
作為LINUX內核來說,它在內存中構建了一個虛擬文件系統VFS,不同于windows上的物理文件系統結構,C盤代表的就是物理的C盤分區,D盤就是D盤的物理分區,
VFS本質就是一顆目錄樹,每個目錄可以映射代表不同的物理設備。

為什么要有VFS?

因為VFS相當于一個中間的解耦層,下層的存儲源的存儲形式可能是各不相同的,可能是來自不同的硬件設備,但需要將這些包成一個統一的對外接口暴露給上層應用使用。

pagecache

在VFS中,每一個文件都有一個inode id作為唯一標識,一個文件首先要被內核讀到內存中,然后開啟一個pagecache(默認4K)作為這個文件在內存中的緩存,這樣的話如果多個應用都需要這同一個文件,只需要來內存中命中這一份文件,而不需要多次讀取,后續對文件的操作都將是基于內存中緩存的操作,將會變得非常快

dirty

pagecache作為文件的緩存,當被進行修改了的時候,實際是對這個緩存內容的修改,此時就會標記成dirty

flush

被標記為dirty,意味著這個緩存的文件是發生了變化的,也就是需要將變化同步更新到磁盤的真實文件中,所以會有一個flush的操作,但flush的觸發時機也是分為很多種,每一種的成本也不同:
1、例如可以修改一次緩存文件,就通過內核刷寫更新一次內容到磁盤文件中(響應快,但效率最差)
2、考慮到dirty的文件并不是只針對某一個文件的, 而是內核中會存在許多dirty的文件,因此可以當內核中的dirty達到一定比例,內核進行刷寫(存在中間數據丟失風險,效率較高)
3、可以周期性的進行刷洗內核中的dirty的文件。(存在中間數據丟失風險,效率較高)

FD(文件描述符)

文件的內存中的pagecache,就意味著多個應用有可能都共享使用一份緩存內容,那么各自的例如對文件的不同位置的讀取,就涉及到一個seek偏移量可能是各不相同的,而這些包起來對外來看就統一用FD來表示。

通過df -h可以看到當前VFS的整個結構

這個結構就是首先掛載了來自/dev/sda3的一個/的根目錄,然后發現還有一個單獨從/dev/sda1掛載的/boot目錄,而實際上/下面也應該有一個/boot目錄,但這個掛載的/boot目錄會覆蓋原本/中的/boot目錄。

這里看到的實際上就是來自 /dev/sda1的/boot

那如果把這個掛載的/boot去掉呢?

發現掛載結構少了/boot,確實卸載掉了/boot:

此時再去/下查看,發現/boot還在,這個/boot就是/原本的/boot,只不過里邊是空的:

此時重新再掛載上剛才卸載的/boot,發現再次查看/boot,內容又回來了:

但這個過程我們可以發現,對于程序而言,這個文件目錄樹結構非常的穩定,不會隨著掛載的變動,目錄發生什么變化

并且這之中其實是有一個映射的過程,通過這個過程,我們可以想到,日后如果某個文件夾例如/abc的大小不滿足需要,完全可以接入一塊新的比較大的外接設備,然后把舊數據遷移過去,然后將外接設備掛載到/abc,也就是覆蓋掉原來的/abc,這樣就完成了一個無結構修改的靜默擴容操作了。

LINUX中的一切皆文件

在馮諾依曼體系結構下,計算器,控制器就相當于我們的CPU,主存儲器就相當于我們的內存, 而輸入輸出設備也就是一切的IO設備。而這一切,在linux之中,全都是用文件進行表示。

文件類型

-:普通文件

可執行,圖片,文本

-rwxr-xr-x. 1 root root 764088 45 2012 vi

d:目錄

dr-xr-xr-x. 2 root root 4096 612 2019 bin

l:鏈接

軟鏈接,硬鏈接。
一個文件的硬鏈接下的各個文件與源文件共享inodeid,可以通過stat xx文件查看,并且會在文件本身標識著引用數,當刪除源文件時,對其他的硬鏈接文件沒有影響。
一個文件的軟鏈接下的各個文件inodeid是不同的。當刪除源文件時,軟鏈接的文件也會無法使用

b:塊設備

讀取的內容位置可以來回漂移。
例如:硬盤

brw-rw---- 1 root disk 8, 1 723 22:37 sda1

c:字符設備

只能向后讀取,不能自由讀取前后偏移量的數據,可能會有一些編解碼約束,不能被切割的字符數據
鍵盤,socket

crw--w---- 1 root tty 4, 32 723 22:37 tty32 crw--w---- 1 root tty 4, 33 723 22:37 tty33 crw--w---- 1 root tty 4, 34 723 22:37 tty34 crw--w---- 1 root tty 4, 35 723 22:37 tty35 crw--w---- 1 root tty 4, 36 723 22:37 tty36 crw--w---- 1 root tty 4, 37 723 22:37 tty37 crw--w---- 1 root tty 4, 38 723 22:37 tty38 crw--w---- 1 root tty 4, 39 723 22:37 tty39

s:socket(底層類型,不能直接看到)

[root@dream01 fd]# exec 8<> /dev/tcp/www.baidu.com/80 [root@dream01 fd]# cd /proc/$$/fd [root@dream01 fd]# ll 總用量 0 lrwx------ 1 root root 64 712 01:30 0 -> /dev/pts/0 lrwx------ 1 root root 64 712 01:31 1 -> /dev/pts/0 lrwx------ 1 root root 64 712 01:31 2 -> /dev/pts/0 lrwx------ 1 root root 64 712 01:31 255 -> /dev/pts/0 lr-x------ 1 root root 64 712 01:31 8 -> socket:[24388] [root@dream01 fd]# lsof -op $$ COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME bash 9310 root cwd DIR 0,3 24173 /proc/9310/fd bash 9310 root rtd DIR 8,3 2 / bash 9310 root txt REG 8,3 2621442 /bin/bash bash 9310 root mem REG 8,3 3932199 /lib64/libresolv-2.12.so bash 9310 root mem REG 8,3 3932187 /lib64/libnss_dns-2.12.so bash 9310 root mem REG 8,3 3932189 /lib64/libnss_files-2.12.so bash 9310 root mem REG 8,3 1971152 /usr/lib/locale/locale-archive bash 9310 root mem REG 8,3 3932173 /lib64/libc-2.12.so bash 9310 root mem REG 8,3 3932179 /lib64/libdl-2.12.so bash 9310 root mem REG 8,3 3932216 /lib64/libtinfo.so.5.7 bash 9310 root mem REG 8,3 3932163 /lib64/ld-2.12.so bash 9310 root mem REG 8,3 2230781 /usr/share/locale/zh_CN/LC_MESSAGES/libc.mo bash 9310 root mem REG 8,3 1967051 /usr/lib64/gconv/gconv-modules.cache bash 9310 root 0u CHR 136,0 0t0 3 /dev/pts/0 bash 9310 root 1u CHR 136,0 0t0 3 /dev/pts/0 bash 9310 root 2u CHR 136,0 0t0 3 /dev/pts/0 bash 9310 root 8r IPv4 24388 0t0 TCP dream01:59731->39.156.66.14:http (ESTABLISHED) ##這就是socket類型 bash 9310 root 255u CHR 136,0 0t0 3 /dev/pts/0

p:pipeline(底層類型,不能直接看到)

上面代碼管道符左右都會各自啟動一個子進程去執行花括號里的內容,而我們知道管道符的作用是將管道符左邊的輸出作為右邊的輸入,那它是如何實現的呢?

生成的子進程號分別是4512和4513,此時看下它們的文件描述符:

可以看到左側子進程(4512)的輸出到pipe管道文件描述符上,而右側子進程(4513)的輸入也為同一個pipe管道文件描述符上,這樣就完成了管道符的功能。

通過lsof查看:

兩者指向的都是一個inodeid 39968的pipe,并且一個是寫,一個是讀。

[eventpoll]:

內核提供給epoll的內存區域。
因為redis就是基于epoll實現連接的,啟動redis,然后看redis的文件描述符可以看到:

lrwx------ 1 root root 64 723 22:37 0 -> /dev/null lrwx------ 1 root root 64 723 22:37 1 -> /dev/null lrwx------ 1 root root 64 723 22:37 2 -> /dev/null lr-x------ 1 root root 64 723 22:37 3 -> pipe:[9002] l-wx------ 1 root root 64 723 22:37 4 -> pipe:[9002] lrwx------ 1 root root 64 723 22:37 5 -> [eventpoll] lrwx------ 1 root root 64 723 22:37 6 -> socket:[9006]

有意思的實驗-生成掛載鏡像

dd if=/dev/zero of=mydisk.img bs=1048576 count=100

輸入是/dev/zero(空),輸出是mydisk.img,一個塊的大小是1048576(1M),一共有100個塊組成,最后生成的就是100M的被0填充的文件

losetup /dev/loop0 mydisk.img

讓環衛接口設備/dev/loop0掛載剛剛生成的文件mydisk.img 也就是/dev/loop0不再指向一個物理地址,而是指向了新生成的這個文件。

mke2fs /dev/loop0

格式化成ext2的文件格式。

到現在為止,我們已經成功掛載到了一塊虛擬環衛設備,那能不能也類似的讓linux中的某個虛擬文件路徑映射到這個虛擬設備上? 就類似于上面/boot的效果。

我們先生成一個虛擬路徑:

mkdir -p ~/io_test/mnt/xxoo/ mount -t ext2 /dev/loop0 ~/io_test/mnt/xxoo/

指定掛載的文件格式為ext2,把它(/dev/loop0)掛載到~/io_test/mnt/xxoo/的虛擬路徑上

[root@dream01 io_test]# df -lh Filesystem Size Used Avail Use% Mounted on /dev/sda3 97G 9.4G 83G 11% / tmpfs 931M 0 931M 0% /dev/shm /dev/sda1 194M 27M 158M 15% /boot /dev/loop0 97M 1.6M 91M 2% /root/io_test/mnt/xxoo

此時可以看到,文件路徑映射已經形成了這兩者的映射。
此時移動到~/io_test/mnt/xxoo/,發現內容不是空的, 而是已經是掛載的設備的內容了:

[root@dream01 xxoo]# ll 總用量 12 drwx------ 2 root root 12288 719 22:23 lost+found

仿docker的思想雛形方向體現

先看一下我們的bash程序在哪:

[root@dream01 xxoo]# whereis bash bash: /bin/bash /usr/share/man/man1/bash.1.gz

我們在虛擬路徑里照樣創建一個bin的目錄,并將系統的bin拷貝過來:

[root@dream01 xxoo]# mkdir bin [root@dream01 xxoo]# cp /bin/bash bin/

然后查看一下bash需要的第三方依賴是什么:

[root@dream01 bin]# ldd bash linux-vdso.so.1 => (0x00007fffd1bff000)libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f9f2ca1f000)libdl.so.2 => /lib64/libdl.so.2 (0x00007f9f2c81b000)libc.so.6 => /lib64/libc.so.6 (0x00007f9f2c486000)/lib64/ld-linux-x86-64.so.2 (0x00007f9f2cc49000)

然后把這些依賴也都拷貝過來:

[root@dream01 xxoo]# mkdir lib64 [root@dream01 xxoo]# cp /lib64/{libtinfo.so.5,libdl.so.2,libc.so.6,ld-linux-x86-64.so.2} lib64/

接下來,如果我們能讓當前的虛擬掛載點作為根目錄,是不是就可以也可以啟動我們剛剛拷貝過來的bash了呢?

先看一下當前的bash進程號:

[root@dream01 xxoo]# echo $$ 1264

把根目錄切換到當前目錄,并啟動當前目錄的bash:

[root@dream01 xxoo]# chroot ./

然后發現確實啟動了一個新的bash解釋程序,我們打印一下當前的進程號:

bash-4.1# echo $$ 39999

發現是39999

此時如果打印這么一句生成文件的指令:

bash-4.1# echo "hello" > /hello.txt

然后退出bash,發現內容被輸出到了我們剛剛指定的新的根目錄,也就是xxoo目錄下:

[root@dream01 xxoo]# ll 總用量 15 drwxr-xr-x 2 root root 1024 719 22:35 bin -rw-r--r-- 1 root root 6 719 22:47 hello.txt drwxr-xr-x 2 root root 1024 719 22:41 lib64 drwx------ 2 root root 12288 719 22:23 lost+found

是不是略有一點docker的味道呢?但實際docker肯定不是這么簡單的,要復雜的多,但我們做的好像已經有點這種味道了,這樣讓相當于一個操作系統內出現多個子操作系統,并且每個人都有每個人的根目錄。docker也是基于虛擬文件系統的支撐才得以實現的。
而我們操作的其實都是這塊掛載設備,卸載之后,發送給他人,他人依舊可以掛載使用,對里面的內容進行修改。 其實回想docker,最開始也是要生成一個img的鏡像文件,然后對其里面進行服務注入。

總結

以上是生活随笔為你收集整理的IO那些事01-IO总述和文件描述符的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。