溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

匿名Inode的示例分析

發(fā)布時(shí)間:2021-12-30 10:02:17 來源:億速云 閱讀:136 作者:小新 欄目:系統(tǒng)運(yùn)維

這篇文章將為大家詳細(xì)講解有關(guān)匿名Inode的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

匿名Inode的示例分析

01唯有文件得人心

當(dāng)一個女生讓你替她抓100只螢火蟲,她一定不是為了折磨你,而是因?yàn)樗龕凵狭四恪.?dāng)你們之間經(jīng)歷了無數(shù)的恩恩怨怨和彼此傷害,她再次讓你替她抓100只螢火蟲,那一定是因?yàn)樗€愛著你。

為什么?因?yàn)檫@就是套路,是在下偶爾瞟一眼古裝肥皂劇總結(jié)出來的套路。

Linux里面最大的套路,就是“一切都是文件”。愛一個人,就為她捉螢火蟲;做一件事,就讓它成為一個“文件”。

為什么自古深情留不住,唯有“文件”得人心呢?因?yàn)槲募谟脩魬B(tài)最直觀的形式是隨著一次open,獲得一個fd,有了這個fd,長城內(nèi)外,你基本可以為所欲為:

  • 在本進(jìn)程內(nèi),fd的最直觀操作是open、close、mmap、ioctl、poll這些。mmap讓你具備把fd透射到內(nèi)存的能力,所以你可以通過指針訪問文件的內(nèi)容。再者,這個mmap,如果底層透射的是framebuffer、V4L2、DRM等,則讓我們具備了從用戶態(tài)操作底層顯存、多媒體數(shù)據(jù)等的能力;比如,無論是V4L2還是DRM,都支持把底層的dma_buf導(dǎo)出為fd。poll則提供給用戶阻塞等待某事件發(fā)生的能力。至于ioctl,就更加不用說了,你可以透過ioctl靈活地為fd添加控制命令。

  • 在跨進(jìn)程的情況下,Linux支持fd的跨進(jìn)程socket傳輸,從而可以實(shí)現(xiàn)共享內(nèi)存、dma_buf跨進(jìn)程共享等。比如一個進(jìn)程可以通過send_fd可以把fd發(fā)送出去:

匿名Inode的示例分析

而另外一個進(jìn)程可以通過recv_fd把fd收過來:

匿名Inode的示例分析

這種fd在長城內(nèi)外可以互訪,fd最終可以指向dma_buf同時(shí)可以被mmap,而dma_buf又最終可以被顯卡、顯示控制器、video  decoder/encoder等設(shè)備訪問的能力,讓fd打通了設(shè)備、CPU和跨進(jìn)程的障礙,從此可以橫著走。

匿名Inode的示例分析

02inode源頭file活水

我們把文件想象成一個object,那么inode描述的是本源,和最終的object一一對應(yīng);dentry是inode的一個路徑馬甲,比如我們可以通過"ln"命令為同一個inode創(chuàng)建很多的硬鏈接馬甲;而file則是活水,進(jìn)程對object的一次“open”,獲得一個file,導(dǎo)致用戶態(tài)得到一個"fd"的句柄來操作這個object。

經(jīng)典的inode、dentry、file誰都不缺席的模型是這樣的:

匿名Inode的示例分析

上圖中,我們有一個inode,這個inode有2個dentry,進(jìn)程A、B open的是第一個dentry;而進(jìn)程C、D  open的是第二個dentry。變了的是file和fd,不變的是inode,中間的dentry馬甲沒那么重要。

但是在inode、dentry、file這個經(jīng)典鐵三角中,從來都是可以有一個缺席者的,那就是dentry,因?yàn)?,有時(shí)候用戶態(tài)想獲得長城內(nèi)外行走的便利,但是卻不想這個inode在文件系統(tǒng)里面留下一個路徑的痕跡。簡單來說,我希望有個fd,但是這個fd,你在從"/"往下面搜索的任何一條路徑下,你都找不到它,它根本在根文件系統(tǒng)以下不存在路徑,它是無名氏,它沒有馬甲,它是個傳說。

比如,近期名震江湖的劍客usefaultfd允許我們在用戶空間處理page  fault,我們是通過userfaultfd這個系統(tǒng)調(diào)用先獲得一個fd,之后就可以對它進(jìn)行各種ioctl了:

匿名Inode的示例分析

我們透過userfaultfd系統(tǒng)獲得了一個fd,它在/xxx/yyy/zzz這樣的文件系統(tǒng)下沒有路徑。這種情況下的fd,對應(yīng)著的是一個沒有名字的匿名inode,你顯然沒有辦法像fd  = open ("xxx", ..)那樣來得到匿名inode的fd,因?yàn)?quot;xxx"是一個路徑,而匿名inode沒有xxx,所以你是直接透過syscall  userfaultfd這樣的系統(tǒng)調(diào)用,來獲得anon_inode在你的進(jìn)程里面對應(yīng)的fd的:

匿名Inode的示例分析

人過留名,雁過留聲;殺人者,打虎武松也。但是anon  inode不吃這一套,它是一個絕頂?shù)妮p功高手,它給與的,是透過fd長城內(nèi)外行走的能力,但是,在文件系統(tǒng)里面卻從未來過。這是用戶真實(shí)的需求,如果這種需求一定要透過一個dentry的open才能實(shí)現(xiàn),這未免有點(diǎn)畫蛇添足了。

03匿名inode的內(nèi)核實(shí)例

我們接下來可以隨便打開個anon inode的實(shí)例來看看它是怎么工作的了。首先userfaultd是一個系統(tǒng)調(diào)用:

匿名Inode的示例分析

這個代碼里面比較核心的是就是,它通過:

anon_inode_getfd_secure()

生成一個匿名inode,并獲得一個句柄fd。重點(diǎn)別忘記了,這種“文件”也是可以有file_operations的,比如上面anon_inode_getfd_secure()參數(shù)中的userfaultfd_fops:

匿名Inode的示例分析

這樣,我們就可以在file_operations的ioctl,poll,read等callback里面實(shí)現(xiàn)自己特別的“文件”邏輯,這是我們自由發(fā)揮的舞臺。

說起anon_inode_getfd_secure(),它再往底層走一級是__anon_inode_getfd():

匿名Inode的示例分析

進(jìn)而再走一級是__anon_inode_getfile():

匿名Inode的示例分析

所以本質(zhì)上,是先造一個anon_inode,然后再在這個anon_inode上面造一個pseudo的file,最后通過fd_install(fd,  file),把fd和file纏在一起。再次強(qiáng)調(diào),用戶有了這個fd就可以為所欲為;而內(nèi)核本身,則是通過file_operations的不同實(shí)現(xiàn)來為所欲為的。

anon_inode之上添加一個系統(tǒng)調(diào)用,造一種特殊的fd,讓用戶去poll,去ioctl,把想象空間拉大了。這種實(shí)現(xiàn)方法,如此拉風(fēng)靈活,以至于它本身也成為了一種套路。比如內(nèi)核里面fs目錄下的:

匿名Inode的示例分析

eventfd,eventpoll,fscontext,io_uring,fanotify,inotify,signalfd,timerfd.......

正所謂, 待到秋來九月八,我花開后百花殺。沖天香陣透長安,滿城盡帶黃金甲。文件,哪怕最終是匿名的,都以沖天的香陣,彌漫整個Linux的世界。

04用戶使用匿名inode

到了要說再見的時(shí)刻了,用戶可見的就是fd,通過fd來使用匿名inode。下面我們來制造一個page  fault的例子,讓用戶態(tài)來處理它,這個例子直接簡化自userfaultfd的man  page。我們在主線程中,通過mmap申請一頁內(nèi)存,然后通過userfaultfd的ioctl告訴內(nèi)核這頁的開始地址和長度,以及通過UFFDIO_REGISTER告訴內(nèi)核這頁的page  fault想用戶空間處理:

匿名Inode的示例分析

然后我們在pthread_create()創(chuàng)建的fault_handler_thread線程中,poll  userfaultfd等待事件,之后把一頁全是0x66的內(nèi)容拷貝到page fault發(fā)生的那一頁:

匿名Inode的示例分析

我們運(yùn)行這個程序得到的輸出如下:

匿名Inode的示例分析

我們主線程在執(zhí)行addr[0]=0x5A5A5A5A的時(shí)候,觸發(fā)了page fault。在fault線程里面,page  fault發(fā)生后,poll阻塞返回,之后用戶通過read()讀到了一個uffd_msg的結(jié)構(gòu)體,里面的成員包含了page  fault的地址。之后,我們通過UFFDIO_COPY這個ioctl,把內(nèi)容為0x66的頁面拷貝給page fault的頁面。

所以,最終主線程在執(zhí)行printf打印的時(shí)候,addr[0]里面讀到了5A5A5A5A,剩下的addr[1]里面讀到了66666666??吹絧age  fault由用戶態(tài)靈活這么靈活自如地處理,我的小伙伴們都嚇尿了。

可以看出來:

  • poll()在等什么,完全被定制化了;

  • read()能讀什么,完全被定制化了;

  • ioctl()能控制什么,完全被定制化了。

我們通過“文件”這個不變的“靜”,制造了poll、read、ioctl的靈動自如。

關(guān)于“匿名Inode的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI