0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

關(guān)于Linux文件系統(tǒng)的幾點(diǎn)注意事項(xiàng)

Q4MP_gh_c472c21 ? 來(lái)源:未知 ? 作者:李建兵 ? 2018-03-17 11:14 ? 次閱讀

做內(nèi)核開(kāi)發(fā)的朋友,可能對(duì)下面的代碼都很眼熟。

1.staticconststructfile_operationsxxx_fops={

2..owner=THIS_MODULE,

3..llseek=no_llseek,

4..write=xxx_write,

5..unlocked_ioctl=xxx_ioctl,

6..open=xxx_open,

7..release=xxx_release,

8.};

一般我們?cè)趚xx_open中會(huì)用類似如下的代碼分配一塊內(nèi)存。

[cpp]view plaincopy

1.file->private_data=kmalloc(sizeof(structxxx),GFP_KERNEL);

然后在接下來(lái)的read/write/ioctl中,我們就可以通過(guò)file->private_data取到與此文件關(guān)聯(lián)的數(shù)據(jù)。

最后,在xxx_release中,我們會(huì)釋放file->private_data指向的內(nèi)存。

如果只是上面這幾種流程訪問(wèn)file->private_data所指向的數(shù)據(jù),基本上不會(huì)出問(wèn)題。

因?yàn)閮?nèi)核的文件系統(tǒng)框架已經(jīng)做了很完善的處理。

對(duì)于迸發(fā)訪問(wèn),我們自己也可以通過(guò)鎖等機(jī)制來(lái)解決。

然而,我們通常還會(huì)在一些異步的流程中訪問(wèn)file->private_data所指向的數(shù)據(jù),這些異步流程可能由定時(shí)器,中斷,進(jìn)程間通信等因素觸發(fā)。

并且,這些流程訪問(wèn)數(shù)據(jù)時(shí),沒(méi)有經(jīng)過(guò)內(nèi)核的文件系統(tǒng)框架。

那么這就有可能導(dǎo)致出現(xiàn)問(wèn)題了。

下面我們先來(lái)看看內(nèi)核文件系統(tǒng)框架的部分實(shí)現(xiàn)代碼,再來(lái)考慮如何規(guī)避可能出現(xiàn)的問(wèn)題。我們的分析基于linux-3.10.102的內(nèi)核源碼。

首先,要得到一個(gè)fd,必須先有一次調(diào)用C庫(kù)函數(shù)open的行為。而在C庫(kù)函數(shù)open返回之前,其他線程得不到fd,當(dāng)然也就不會(huì)對(duì)此fd進(jìn)行操作。等拿到fd時(shí),open操作都已經(jīng)完成了。

實(shí)際上,更夸張的情況還是有可能存在的。例如,可能由于程序的錯(cuò)誤甚至是程序員故意構(gòu)造特殊代碼,導(dǎo)致在open返回之前,其他線程就使用即將返回的fd進(jìn)行文件操作了。這種情況,這里就不討論了。有興趣的朋友,可以自己鉆研內(nèi)核代碼,看看會(huì)產(chǎn)生什么效果。

先看看文件打開(kāi)操作的主要函數(shù)調(diào)用:

sys_open,do_sys_open,do_filp_open,fd_install,__fd_install。

安裝fd的操作如下??梢?jiàn)這里是對(duì)文件表加了鎖的,并且不是針對(duì)單個(gè)文件,是整體性的加鎖。

[cpp]view plaincopy

1.void__fd_install(structfiles_struct*files,unsignedintfd,

2.structfile*file)

3.{

4.structfdtable*fdt;

5.spin_lock(&files->file_lock);

6.fdt=files_fdtable(files);

7.BUG_ON(fdt->fd[fd]!=NULL);

8.rcu_assign_pointer(fdt->fd[fd],file);

9.spin_unlock(&files->file_lock);

10.}

讀寫(xiě)操作,代碼結(jié)構(gòu)非常相似。這里只看寫(xiě)操作吧。其實(shí)現(xiàn)如下:

[cpp]view plaincopy

1.SYSCALL_DEFINE3(write,unsignedint,fd,constchar__user*,buf,

2.size_t,count)

3.{

4.structfdf=fdget(fd);

5.ssize_tret=-EBADF;

6.

7.if(f.file){

8.loff_tpos=file_pos_read(f.file);

9.ret=vfs_write(f.file,buf,count,&pos);

10.file_pos_write(f.file,pos);

11.fdput(f);

12.}

13.

14.returnret;

15.}

[cpp]view plaincopy

1.ssize_tvfs_write(structfile*file,constchar__user*buf,size_tcount,loff_t*pos)

2.{

3.ssize_tret;

4.

5.if(!(file->f_mode&FMODE_WRITE))

6.return-EBADF;

7.if(!file->f_op||(!file->f_op->write&&!file->f_op->aio_write))

8.return-EINVAL;

9.if(unlikely(!access_ok(VERIFY_READ,buf,count)))

10.return-EFAULT;

11.

12.ret=rw_verify_area(WRITE,file,pos,count);

13.if(ret>=0){

14.count=ret;

15.file_start_write(file);

16.if(file->f_op->write)

17.ret=file->f_op->write(file,buf,count,pos);

18.else

19.ret=do_sync_write(file,buf,count,pos);

20.if(ret>0){

21.fsnotify_modify(file);

22.add_wchar(current,ret);

23.}

24.inc_syscw(current);

25.file_end_write(file);

26.}

27.

28.returnret;

29.}

[cpp]view plaincopy

1.ssize_tdo_sync_write(structfile*filp,constchar__user*buf,size_tlen,loff_t*ppos)

2.{

3.structioveciov={.iov_base=(void__user*)buf,.iov_len=len};

4.structkiocbkiocb;

5.ssize_tret;

6.

7.init_sync_kiocb(&kiocb,filp);

8.kiocb.ki_pos=*ppos;

9.kiocb.ki_left=len;

10.kiocb.ki_nbytes=len;

11.

12.ret=filp->f_op->aio_write(&kiocb,&iov,1,kiocb.ki_pos);

13.if(-EIOCBQUEUED==ret)

14.ret=wait_on_sync_kiocb(&kiocb);

15.*ppos=kiocb.ki_pos;

16.returnret;

17.}

可以看出,讀寫(xiě)操作是無(wú)鎖的。也不好加鎖,因?yàn)樽x寫(xiě)操作,還有ioctl,有可能阻塞。如果需要鎖,用戶自己可以使用文件鎖,《UNIX環(huán)境高級(jí)編程》中有關(guān)于文件鎖的描述。

不過(guò)fdget與fdput中包含了一些rcu方面的操作,那是為了能夠與close fd的操作迸發(fā)進(jìn)行。

另外,可以看出,如果只實(shí)現(xiàn)一個(gè)f_op->aio_write,也是可以支持C庫(kù)函數(shù)write的。

再來(lái)看看ioctl的實(shí)現(xiàn)。

[cpp]view plaincopy

1.SYSCALL_DEFINE3(ioctl,unsignedint,fd,unsignedint,cmd,unsignedlong,arg)

2.{

3.interror;

4.structfdf=fdget(fd);

5.

6.if(!f.file)

7.return-EBADF;

8.error=security_file_ioctl(f.file,cmd,arg);

9.if(!error)

10.error=do_vfs_ioctl(f.file,fd,cmd,arg);

11.fdput(f);

12.returnerror;

13.}

對(duì)于非常規(guī)文件,或者常規(guī)文件中文件系統(tǒng)特有的命令,最終都會(huì)走到

filp->f_op->unlocked_ioctl

另外,ioctl也是無(wú)鎖的。同時(shí),流程中包含了fdget與fdput,這一點(diǎn)與read/write一樣。

再來(lái)看看關(guān)閉文件的操作。系統(tǒng)調(diào)用sys_close的實(shí)現(xiàn)如下(fs/open.c)

[cpp]view plaincopy

1.SYSCALL_DEFINE1(close,unsignedint,fd)

2.{

3.intretval=__close_fd(current->files,fd);

4.

5./*can'trestartclosesyscallbecausefiletableentrywascleared*/

6.if(unlikely(retval==-ERESTARTSYS||

7.retval==-ERESTARTNOINTR||

8.retval==-ERESTARTNOHAND||

9.retval==-ERESTART_RESTARTBLOCK))

10.retval=-EINTR;

11.

12.returnretval;

13.}

可見(jiàn)主要工作是__close_fd函數(shù)(fs/file.c)完成的,其代碼如下??梢?jiàn)他是對(duì)進(jìn)程的文件表加了鎖的。因此,open、close操作是有互斥的,并且不是針對(duì)某一文件的互斥,而是整體的互斥。

對(duì)于close一個(gè)fd時(shí),其他cpu上的線程若正要或正在讀寫(xiě)此fd怎么辦?可以看出,close操作并不會(huì)為此等待,而是直接繼續(xù)操作。

其中的rcu_assign_pointer(fdt->fd[fd], NULL);清除了此fd與file結(jié)構(gòu)的關(guān)聯(lián),因此在此之后通過(guò)此fd已經(jīng)訪問(wèn)不到相應(yīng)的file結(jié)構(gòu)了。至于在此之前就發(fā)起了的且尚未結(jié)束的訪問(wèn)怎么處理,答案是在filp_close中處理。

[cpp]view plaincopy

1.int__close_fd(structfiles_struct*files,unsignedfd)

2.{

3.structfile*file;

4.structfdtable*fdt;

5.

6.spin_lock(&files->file_lock);

7.fdt=files_fdtable(files);

8.if(fd>=fdt->max_fds)

9.gotoout_unlock;

10.file=fdt->fd[fd];

11.if(!file)

12.gotoout_unlock;

13.rcu_assign_pointer(fdt->fd[fd],NULL);

14.__clear_close_on_exec(fd,fdt);

15.__put_unused_fd(files,fd);

16.spin_unlock(&files->file_lock);

17.returnfilp_close(file,files);

18.

19.out_unlock:

20.spin_unlock(&files->file_lock);

21.return-EBADF;

22.}

filp_close又調(diào)用了fput, 后者的相關(guān)代碼如下??梢?jiàn)當(dāng)前任務(wù)若非內(nèi)核線程,接下來(lái)就是走_(dá)___fput,否則就是走delayed_fput。

但是最終都是走_(dá)_fput,__fput中會(huì)調(diào)用file->f_op->release,即我們的xxx_release。

不過(guò),從fput代碼可以看出,____fput會(huì)由rcu相關(guān)的work觸發(fā)。因此,可以預(yù)見(jiàn)當(dāng)____fput被調(diào)用時(shí),已經(jīng)沒(méi)有已經(jīng)發(fā)生且尚未結(jié)束的針對(duì)此文件的訪問(wèn)流程了。

[cpp]view plaincopy

1.staticvoid____fput(structcallback_head*work)

2.{

3.__fput(container_of(work,structfile,f_u.fu_rcuhead));

4.}

5.

6.

7.voidflush_delayed_fput(void)

8.{

9.delayed_fput(NULL);

10.}

11.

12.staticDECLARE_WORK(delayed_fput_work,delayed_fput);

13.

14.voidfput(structfile*file)

15.{

16.if(atomic_long_dec_and_test(&file->f_count)){

17.structtask_struct*task=current;

18.

19.if(likely(!in_interrupt()&&!(task->flags&PF_KTHREAD))){

20.init_task_work(&file->f_u.fu_rcuhead,____fput);

21.if(!task_work_add(task,&file->f_u.fu_rcuhead,true))

22.return;

23.}

24.

25.if(llist_add(&file->f_u.fu_llist,&delayed_fput_list))

26.schedule_work(&delayed_fput_work);

27.}

28.}

現(xiàn)在再來(lái)想想,我們上面提到的那些訪問(wèn)file->private_data所指向的數(shù)據(jù)的異步流程,這些流程并沒(méi)有走文件系統(tǒng)框架。

會(huì)不會(huì)出現(xiàn)這種情況,xxx_release已經(jīng)執(zhí)行過(guò)了,可是異步流程卻還來(lái)訪問(wèn)file->private_data所指向的數(shù)據(jù)呢?

其實(shí)xxx_release不妨不要釋放file->private_data指向的內(nèi)存,而是標(biāo)記一下他的狀態(tài)為已關(guān)閉。然后異步流程再訪問(wèn)此數(shù)據(jù)時(shí),先檢查一下?tīng)顟B(tài)。

若為已關(guān)閉,則妥善處理并釋放即可。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11161

    瀏覽量

    208467

原文標(biāo)題:關(guān)于Linux文件系統(tǒng)的幾點(diǎn)注意事項(xiàng)

文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    使用HTTP協(xié)議有哪幾點(diǎn)注意事項(xiàng)

    HTTP協(xié)議是什么?HTTP協(xié)議的工作原理是什么?使用HTTP協(xié)議有哪幾點(diǎn)注意事項(xiàng)
    發(fā)表于 09-29 09:23

    嵌入式工程模板搭建幾點(diǎn)注意事項(xiàng)

    嵌入式工程模板搭建幾點(diǎn)注意事項(xiàng)—以STM32l475VET6為例一. 系統(tǒng)時(shí)鐘初始化函數(shù)搭建工程基本模板,除過(guò)移植官方提供的相關(guān)文件外,另一個(gè)關(guān)鍵點(diǎn)是編寫(xiě)
    發(fā)表于 02-08 07:07

    Linux文件系統(tǒng)課程

    本章學(xué)習(xí)目標(biāo)理解什么是文件系統(tǒng)了解文件系統(tǒng)工作原理理解Fedora Core Linux文件系統(tǒng)的結(jié)構(gòu)掌握Fedora Core Linux
    發(fā)表于 04-10 17:07 ?0次下載

    Linux文件系統(tǒng)簡(jiǎn)介

    Linux文件系統(tǒng)簡(jiǎn)介 什么是根文件   根文件系統(tǒng)首先是一種文件系統(tǒng),但是相對(duì)于普通的文件系統(tǒng)
    發(fā)表于 04-21 17:01 ?5090次閱讀

    玩轉(zhuǎn)Linux,先把文件系統(tǒng)搞懂

    Linux 支持多種文件系統(tǒng),包括 ext2 、 ext3 、 vfat 、 ntfs 、 iso9660 、 jffs 、 romfs 和 nfs 等,為了對(duì)各類文件系統(tǒng)進(jìn)行統(tǒng)一管理, Li
    發(fā)表于 08-16 10:50 ?2036次閱讀
    玩轉(zhuǎn)<b class='flag-5'>Linux</b>,先把<b class='flag-5'>文件系統(tǒng)</b>搞懂

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)
    發(fā)表于 10-27 14:13 ?0次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、<b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>與設(shè)備<b class='flag-5'>文件系統(tǒng)</b>

    文件系統(tǒng)是什么?淺談EXT文件系統(tǒng)歷史

    在先前關(guān)于Linux文件系統(tǒng)的文章中,我很想去深入地討論更多EXT文件系統(tǒng)的特性的信息。所以,首先讓我們來(lái)回答這個(gè)問(wèn)題:什么是文件系統(tǒng)?一個(gè)
    發(fā)表于 06-28 09:03 ?5624次閱讀
    <b class='flag-5'>文件系統(tǒng)</b>是什么?淺談EXT<b class='flag-5'>文件系統(tǒng)</b>歷史

    Linux 內(nèi)核/sys 文件系統(tǒng)介紹

    linux2.6內(nèi)核引入sysfs文件系統(tǒng),sysfs可以看成與proc,devfs和devpty同類別的文件系統(tǒng),該文件系統(tǒng)是虛擬的文件系統(tǒng)
    發(fā)表于 04-25 16:20 ?4235次閱讀
    <b class='flag-5'>Linux</b> 內(nèi)核/sys <b class='flag-5'>文件系統(tǒng)</b>介紹

    可以了解的Linux 文件系統(tǒng)結(jié)構(gòu)

    Linux中的文件是什么?它的文件系統(tǒng)又是什么?那些配置文件又在哪里?我下載好的程序保存在哪里了?在 Linux
    發(fā)表于 04-27 14:06 ?692次閱讀
    可以了解的<b class='flag-5'>Linux</b> <b class='flag-5'>文件系統(tǒng)</b>結(jié)構(gòu)

    Linux最新UBI文件系統(tǒng)介紹

    嵌入式linux中文站關(guān)注嵌入式linux文件系統(tǒng)的發(fā)展。在linux-2.6.27以前,談到Flash文件系統(tǒng),大家很多時(shí)候多會(huì)想到cra
    發(fā)表于 04-27 19:37 ?6347次閱讀

    Linux嵌入式文件系統(tǒng)如何構(gòu)建

    Linux支持多種文件系統(tǒng),同樣,嵌入式Linux也支持多種文件系統(tǒng)。雖然在嵌入式系統(tǒng)中,由于資源受限的原因,它的
    發(fā)表于 06-18 09:23 ?965次閱讀

    Linux文件系統(tǒng)解析

    Linux 中,最直觀、最可見(jiàn)的部分就是 文件系統(tǒng)(file system)。下面我們就來(lái)一起探討一下關(guān)于 Linux 中國(guó)的文件系統(tǒng),
    的頭像 發(fā)表于 09-16 11:29 ?2400次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>解析

    適用于Linux的最佳通用文件系統(tǒng) Linux文件系統(tǒng)的安裝

    為您的計(jì)算機(jī)選擇正確的文件系統(tǒng)可能是一個(gè)困難的過(guò)程。您可能會(huì)想知道:為什么文件系統(tǒng)很重要?有沒(méi)有適用于安裝 Linux 的特定文件系統(tǒng)? 事實(shí)證明,有兩種
    發(fā)表于 08-03 10:22 ?305次閱讀
    適用于<b class='flag-5'>Linux</b>的最佳通用<b class='flag-5'>文件系統(tǒng)</b> <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>的安裝

    Linux文件系統(tǒng)特點(diǎn)

    Linux文件系統(tǒng)特點(diǎn) 文件系統(tǒng)要有嚴(yán)格的組織形式,使得文件能夠以塊為單位進(jìn)行存儲(chǔ)。 文件系統(tǒng)中也要有索引區(qū),用來(lái)方便查找一個(gè)
    的頭像 發(fā)表于 11-09 14:48 ?1040次閱讀
    <b class='flag-5'>Linux</b>的<b class='flag-5'>文件系統(tǒng)</b>特點(diǎn)

    Linux文件系統(tǒng)的掛載過(guò)程

    Linux文件系統(tǒng)(rootfs)是Linux系統(tǒng)中所有其他文件系統(tǒng)和目錄的起點(diǎn),它是內(nèi)核啟動(dòng)時(shí)掛載的第一個(gè)
    的頭像 發(fā)表于 10-05 16:50 ?145次閱讀