0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于Linux文件系统的几点注意事项

Q4MP_gh_c472c21 来源:未知 作者:李建兵 2018-03-17 11:14 次阅读

做内核开发的朋友,可能对下面的代码都很眼熟。

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.};

一般我们在xxx_open中会用类似如下的代码分配一块内存。

[cpp]view plaincopy

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

然后在接下来的read/write/ioctl中,我们就可以通过file->private_data取到与此文件关联的数据。

最后,在xxx_release中,我们会释放file->private_data指向的内存。

如果只是上面这几种流程访问file->private_data所指向的数据,基本上不会出问题。

因为内核的文件系统框架已经做了很完善的处理。

对于迸发访问,我们自己也可以通过锁等机制来解决。

然而,我们通常还会在一些异步的流程中访问file->private_data所指向的数据,这些异步流程可能由定时器,中断,进程间通信等因素触发。

并且,这些流程访问数据时,没有经过内核的文件系统框架。

那么这就有可能导致出现问题了。

下面我们先来看看内核文件系统框架的部分实现代码,再来考虑如何规避可能出现的问题。我们的分析基于linux-3.10.102的内核源码。

首先,要得到一个fd,必须先有一次调用C库函数open的行为。而在C库函数open返回之前,其他线程得不到fd,当然也就不会对此fd进行操作。等拿到fd时,open操作都已经完成了。

实际上,更夸张的情况还是有可能存在的。例如,可能由于程序的错误甚至是程序员故意构造特殊代码,导致在open返回之前,其他线程就使用即将返回的fd进行文件操作了。这种情况,这里就不讨论了。有兴趣的朋友,可以自己钻研内核代码,看看会产生什么效果。

先看看文件打开操作的主要函数调用:

sys_open,do_sys_open,do_filp_open,fd_install,__fd_install。

安装fd的操作如下。可见这里是对文件表加了锁的,并且不是针对单个文件,是整体性的加锁。

[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.}

读写操作,代码结构非常相似。这里只看写操作吧。其实现如下:

[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.}

可以看出,读写操作是无锁的。也不好加锁,因为读写操作,还有ioctl,有可能阻塞。如果需要锁,用户自己可以使用文件锁,《UNIX环境高级编程》中有关于文件锁的描述。

不过fdget与fdput中包含了一些rcu方面的操作,那是为了能够与close fd的操作迸发进行。

另外,可以看出,如果只实现一个f_op->aio_write,也是可以支持C库函数write的。

再来看看ioctl的实现。

[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.}

对于非常规文件,或者常规文件中文件系统特有的命令,最终都会走到

filp->f_op->unlocked_ioctl

另外,ioctl也是无锁的。同时,流程中包含了fdget与fdput,这一点与read/write一样。

再来看看关闭文件的操作。系统调用sys_close的实现如下(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.}

可见主要工作是__close_fd函数(fs/file.c)完成的,其代码如下。可见他是对进程的文件表加了锁的。因此,open、close操作是有互斥的,并且不是针对某一文件的互斥,而是整体的互斥。

对于close一个fd时,其他cpu上的线程若正要或正在读写此fd怎么办?可以看出,close操作并不会为此等待,而是直接继续操作。

其中的rcu_assign_pointer(fdt->fd[fd], NULL);清除了此fd与file结构的关联,因此在此之后通过此fd已经访问不到相应的file结构了。至于在此之前就发起了的且尚未结束的访问怎么处理,答案是在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又调用了fput, 后者的相关代码如下。可见当前任务若非内核线程,接下来就是走____fput,否则就是走delayed_fput。

但是最终都是走__fput,__fput中会调用file->f_op->release,即我们的xxx_release。

不过,从fput代码可以看出,____fput会由rcu相关的work触发。因此,可以预见当____fput被调用时,已经没有已经发生且尚未结束的针对此文件的访问流程了。

[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.}

现在再来想想,我们上面提到的那些访问file->private_data所指向的数据的异步流程,这些流程并没有走文件系统框架。

会不会出现这种情况,xxx_release已经执行过了,可是异步流程却还来访问file->private_data所指向的数据呢?

其实xxx_release不妨不要释放file->private_data指向的内存,而是标记一下他的状态为已关闭。然后异步流程再访问此数据时,先检查一下状态。

若为已关闭,则妥善处理并释放即可。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11298

    浏览量

    209391

原文标题:关于Linux文件系统的几点注意事项

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    使用HTTP协议有哪几点注意事项

    HTTP协议是什么?HTTP协议的工作原理是什么?使用HTTP协议有哪几点注意事项
    发表于 09-29 09:23

    嵌入式工程模板搭建几点注意事项

    嵌入式工程模板搭建几点注意事项—以STM32l475VET6为例一. 系统时钟初始化函数搭建工程基本模板,除过移植官方提供的相关文件外,另一个关键点是编写
    发表于 02-08 07:07

    Linux文件系统课程

    本章学习目标理解什么是文件系统了解文件系统工作原理理解Fedora Core Linux文件系统的结构掌握Fedora Core Linux
    发表于 04-10 17:07 0次下载

    Linux文件系统简介

    Linux文件系统简介 什么是根文件   根文件系统首先是一种文件系统,但是相对于普通的文件系统
    发表于 04-21 17:01 5111次阅读

    玩转Linux,先把文件系统搞懂

    Linux 支持多种文件系统,包括 ext2 、 ext3 、 vfat 、 ntfs 、 iso9660 、 jffs 、 romfs 和 nfs 等,为了对各类文件系统进行统一管理, Li
    发表于 08-16 10:50 2075次阅读
    玩转<b class='flag-5'>Linux</b>,先把<b class='flag-5'>文件系统</b>搞懂

    Linux设备驱动开发详解》第5章、Linux文件系统与设备文件系统

    Linux设备驱动开发详解》第5章、Linux文件系统与设备文件系统
    发表于 10-27 14:13 0次下载
    《<b class='flag-5'>Linux</b>设备驱动开发详解》第5章、<b class='flag-5'>Linux</b><b class='flag-5'>文件系统</b>与设备<b class='flag-5'>文件系统</b>

    文件系统是什么?浅谈EXT文件系统历史

    在先前关于Linux文件系统的文章中,我很想去深入地讨论更多EXT文件系统的特性的信息。所以,首先让我们来回答这个问题:什么是文件系统?一个
    发表于 06-28 09:03 5700次阅读
    <b class='flag-5'>文件系统</b>是什么?浅谈EXT<b class='flag-5'>文件系统</b>历史

    Linux 内核/sys 文件系统介绍

    linux2.6内核引入sysfs文件系统,sysfs可以看成与proc,devfs和devpty同类别的文件系统,该文件系统是虚拟的文件系统
    发表于 04-25 16:20 4291次阅读
    <b class='flag-5'>Linux</b> 内核/sys <b class='flag-5'>文件系统</b>介绍

    可以了解的Linux 文件系统结构

    Linux中的文件是什么?它的文件系统又是什么?那些配置文件又在哪里?我下载好的程序保存在哪里了?在 Linux
    发表于 04-27 14:06 716次阅读
    可以了解的<b class='flag-5'>Linux</b> <b class='flag-5'>文件系统</b>结构

    Linux最新UBI文件系统介绍

    嵌入式linux中文站关注嵌入式linux文件系统的发展。在linux-2.6.27以前,谈到Flash文件系统,大家很多时候多会想到cra
    发表于 04-27 19:37 6386次阅读

    Linux嵌入式文件系统如何构建

    Linux支持多种文件系统,同样,嵌入式Linux也支持多种文件系统。虽然在嵌入式系统中,由于资源受限的原因,它的
    发表于 06-18 09:23 993次阅读

    Linux文件系统解析

    Linux 中,最直观、最可见的部分就是 文件系统(file system)。下面我们就来一起探讨一下关于 Linux 中国的文件系统
    的头像 发表于 09-16 11:29 2460次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>文件系统</b>解析

    适用于Linux的最佳通用文件系统 Linux文件系统的安装

    为您的计算机选择正确的文件系统可能是一个困难的过程。您可能会想知道:为什么文件系统很重要?有没有适用于安装 Linux 的特定文件系统? 事实证明,有两种
    发表于 08-03 10:22 341次阅读
    适用于<b class='flag-5'>Linux</b>的最佳通用<b class='flag-5'>文件系统</b> <b class='flag-5'>Linux</b><b class='flag-5'>文件系统</b>的安装

    Linux文件系统特点

    Linux文件系统特点 文件系统要有严格的组织形式,使得文件能够以块为单位进行存储。 文件系统中也要有索引区,用来方便查找一个
    的头像 发表于 11-09 14:48 1179次阅读
    <b class='flag-5'>Linux</b>的<b class='flag-5'>文件系统</b>特点

    Linux文件系统的挂载过程

    Linux文件系统(rootfs)是Linux系统中所有其他文件系统和目录的起点,它是内核启动时挂载的第一个
    的头像 发表于 10-05 16:50 411次阅读