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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

VFS

內(nèi)核既管內(nèi)存,又管磁盤IO。
作為LINUX內(nèi)核來說,它在內(nèi)存中構(gòu)建了一個(gè)虛擬文件系統(tǒng)VFS,不同于windows上的物理文件系統(tǒng)結(jié)構(gòu),C盤代表的就是物理的C盤分區(qū),D盤就是D盤的物理分區(qū),
VFS本質(zhì)就是一顆目錄樹,每個(gè)目錄可以映射代表不同的物理設(shè)備。

為什么要有VFS?

因?yàn)閂FS相當(dāng)于一個(gè)中間的解耦層,下層的存儲(chǔ)源的存儲(chǔ)形式可能是各不相同的,可能是來自不同的硬件設(shè)備,但需要將這些包成一個(gè)統(tǒng)一的對外接口暴露給上層應(yīng)用使用。

pagecache

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

dirty

pagecache作為文件的緩存,當(dāng)被進(jìn)行修改了的時(shí)候,實(shí)際是對這個(gè)緩存內(nèi)容的修改,此時(shí)就會(huì)標(biāo)記成dirty

flush

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

FD(文件描述符)

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

通過df -h可以看到當(dāng)前VFS的整個(gè)結(jié)構(gòu)

這個(gè)結(jié)構(gòu)就是首先掛載了來自/dev/sda3的一個(gè)/的根目錄,然后發(fā)現(xiàn)還有一個(gè)單獨(dú)從/dev/sda1掛載的/boot目錄,而實(shí)際上/下面也應(yīng)該有一個(gè)/boot目錄,但這個(gè)掛載的/boot目錄會(huì)覆蓋原本/中的/boot目錄。

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

那如果把這個(gè)掛載的/boot去掉呢?

發(fā)現(xiàn)掛載結(jié)構(gòu)少了/boot,確實(shí)卸載掉了/boot:

此時(shí)再去/下查看,發(fā)現(xiàn)/boot還在,這個(gè)/boot就是/原本的/boot,只不過里邊是空的:

此時(shí)重新再掛載上剛才卸載的/boot,發(fā)現(xiàn)再次查看/boot,內(nèi)容又回來了:

但這個(gè)過程我們可以發(fā)現(xiàn),對于程序而言,這個(gè)文件目錄樹結(jié)構(gòu)非常的穩(wěn)定,不會(huì)隨著掛載的變動(dòng),目錄發(fā)生什么變化

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

LINUX中的一切皆文件

在馮諾依曼體系結(jié)構(gòu)下,計(jì)算器,控制器就相當(dāng)于我們的CPU,主存儲(chǔ)器就相當(dāng)于我們的內(nèi)存, 而輸入輸出設(shè)備也就是一切的IO設(shè)備。而這一切,在linux之中,全都是用文件進(jìn)行表示。

文件類型

-:普通文件

可執(zhí)行,圖片,文本

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

d:目錄

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

l:鏈接

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

b:塊設(shè)備

讀取的內(nèi)容位置可以來回漂移。
例如:硬盤

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

c:字符設(shè)備

只能向后讀取,不能自由讀取前后偏移量的數(shù)據(jù),可能會(huì)有一些編解碼約束,不能被切割的字符數(shù)據(jù)
鍵盤,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(底層類型,不能直接看到)

上面代碼管道符左右都會(huì)各自啟動(dòng)一個(gè)子進(jìn)程去執(zhí)行花括號里的內(nèi)容,而我們知道管道符的作用是將管道符左邊的輸出作為右邊的輸入,那它是如何實(shí)現(xiàn)的呢?

生成的子進(jìn)程號分別是4512和4513,此時(shí)看下它們的文件描述符:

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

通過lsof查看:

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

[eventpoll]:

內(nèi)核提供給epoll的內(nèi)存區(qū)域。
因?yàn)閞edis就是基于epoll實(shí)現(xiàn)連接的,啟動(dòng)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]

有意思的實(shí)驗(yàn)-生成掛載鏡像

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

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

losetup /dev/loop0 mydisk.img

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

mke2fs /dev/loop0

格式化成ext2的文件格式。

到現(xiàn)在為止,我們已經(jīng)成功掛載到了一塊虛擬環(huán)衛(wèi)設(shè)備,那能不能也類似的讓linux中的某個(gè)虛擬文件路徑映射到這個(gè)虛擬設(shè)備上? 就類似于上面/boot的效果。

我們先生成一個(gè)虛擬路徑:

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

此時(shí)可以看到,文件路徑映射已經(jīng)形成了這兩者的映射。
此時(shí)移動(dòng)到~/io_test/mnt/xxoo/,發(fā)現(xiàn)內(nèi)容不是空的, 而是已經(jīng)是掛載的設(shè)備的內(nèi)容了:

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

仿docker的思想雛形方向體現(xiàn)

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

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

我們在虛擬路徑里照樣創(chuàng)建一個(gè)bin的目錄,并將系統(tǒng)的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/

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

先看一下當(dāng)前的bash進(jìn)程號:

[root@dream01 xxoo]# echo $$ 1264

把根目錄切換到當(dāng)前目錄,并啟動(dòng)當(dāng)前目錄的bash:

[root@dream01 xxoo]# chroot ./

然后發(fā)現(xiàn)確實(shí)啟動(dòng)了一個(gè)新的bash解釋程序,我們打印一下當(dāng)前的進(jìn)程號:

bash-4.1# echo $$ 39999

發(fā)現(xiàn)是39999

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

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

然后退出bash,發(fā)現(xiàn)內(nèi)容被輸出到了我們剛剛指定的新的根目錄,也就是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

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。