(内核2.4.37)

一、

当我们打开一个文件的时候,需要获得文件的文件描述符(前面已经说过其实就是文件数组下标),一般是通过函数open来完成,这个系统调用在<unistd.h>头文件中声明定义,我们看一下源码:

[cpp] view plaincopyprint?
  1. 530 static inline long open(const char * name, int mode, int flags)
  2. 531 {
  3. 532         return sys_open(name, mode, flags);
  4. 533 }

Ps:对于这些参数一般我们都很熟悉,经常使用,这里顺便提出记忆一下:

mode:参数可选:

[cpp] view plaincopyprint?
  1. 32 #define S_IRWXU 00700     文件所有者可读可写可执行
  2. 33 #define S_IRUSR 00400     文件所有者可读
  3. 34 #define S_IWUSR 00200     文件所有者可写
  4. 35 #define S_IXUSR 00100     文件所有者可执行
  5. 36
  6. 37 #define S_IRWXG 00070     文件用户组可写可读可执行
  7. 38 #define S_IRGRP 00040     文件用户组可读
  8. 39 #define S_IWGRP 00020     文件用户组可写
  9. 40 #define S_IXGRP 00010     文件用户组可执行
  10. 41
  11. 42 #define S_IRWXO 00007     其他用户可写可读可执行
  12. 43 #define S_IROTH 00004     其他用户可读
  13. 44 #define S_IWOTH 00002     其他用户可写
  14. 45 #define S_IXOTH 00001     其他用户可执行

flags:在fcntl.h中定义

[cpp] view plaincopyprint?
  1. 7 #define O_RDONLY             00
  2. 8 #define O_WRONLY             01
  3. 9 #define O_RDWR               02
  4. 10 #define O_CREAT            0100 /* not fcntl */
  5. 11 #define O_EXCL             0200 /* not fcntl */
  6. 12 #define O_NOCTTY           0400 /* not fcntl */
  7. 13 #define O_TRUNC           01000 /* not fcntl */
  8. 14 #define O_APPEND          02000
  9. 15 #define O_NONBLOCK        04000
  10. 16 #define O_NDELAY        O_NONBLOCK
  11. 17 #define O_SYNC           010000
  12. 18 #define FASYNC           020000 /* fcntl, for BSD compatibility */
  13. 19 #define O_DIRECT         040000 /* direct disk access hint */
  14. 20 #define O_LARGEFILE     0100000
  15. 21 #define O_DIRECTORY     0200000 /* must be a directory */
  16. 22 #define O_NOFOLLOW      0400000 /* don't follow links */

O_RDONLY          以只读方式打开文件
O_WRONLY         以只写方式打开文件
O_RDWR              以读和写的方式打开文件
上面三个只能选择一个,下面的可以合理的任意组合:
O_CREAT             打开文件,如果文件不存在则建立文件
O_EXCL                如果已经置O_CREAT且文件存在,则强制open()失败
O_TRUNC             将文件的长度截为0
O_APPEND           强制write()从文件尾开始
对于终端文件,上面四个是无效,提供了两个新的标志:
O_NOCTTY           停止这个终端作为控制终端
O_NONBLOCK      使open()、read()、write()不被阻塞。

我们可以看到,里面实际调用的是sys_open这个系统调用,其实想想也很正常,对于我的一个系统而言,可以存在很多组不同的文件系统,对于不同的文件系统,打开文件的方式肯定是不一样的,所有内核需要根据具体的文件系统的类型去调用不同的函数进行执行。现在看看sys_open函数(fs/open.c中):

[cpp] view plaincopyprint?
  1. 800 asmlinkage long sys_open(const char * filename, int flags, int mode)
  2. 801 {
  3. 802         char * tmp;
  4. 803         int fd, error;
  5. 804
  6. 805 #if BITS_PER_LONG != 32
  7. 806         flags |= O_LARGEFILE;
  8. 807 #endif
  9. 808         tmp = getname(filename);   /* 1 */
  10. 809         fd = PTR_ERR(tmp);
  11. 810         if (!IS_ERR(tmp)) {
  12. 811                 fd = get_unused_fd();  /* 2 */
  13. 812                 if (fd >= 0) {
  14. 813                         struct file *f = filp_open(tmp, flags, mode); /* 3 */
  15. 814                         error = PTR_ERR(f);
  16. 815                         if (IS_ERR(f))
  17. 816                                 goto out_error;
  18. 817                         fd_install(fd, f);    /* 4 */
  19. 818                 }
  20. 819 out:
  21. 820                 putname(tmp);
  22. 821         }
  23. 822         return fd;
  24. 823
  25. 824 out_error:
  26. 825         put_unused_fd(fd);
  27. 826         fd = error;
  28. 827         goto out;
  29. 828 }

主要看上面注释表示的四大步骤/* 1 */  /* 2 */  /* 3 */  /* 4 */

/* 1 */:这步是一个辅助步骤,将filename从用户态拷贝到内核态变量中。基本步骤涉及一下几个函数:

[cpp] view plaincopyprint?
  1. 125 char * getname(const char * filename)
  2. 126 {
  3. 127         char *tmp, *result;
  4. 128
  5. 129         result = ERR_PTR(-ENOMEM);
  6. 130         tmp = __getname();     /* 这玩意吧其实是分配内核中空间用户装name */
  7. 131         if (tmp)  {
  8. 132                 int retval = do_getname(filename, tmp);  /* 其实就是将filename拷贝到tmp中 */
  9. 133
  10. 134                 result = tmp;
  11. 135                 if (retval < 0) {
  12. 136                         putname(tmp);
  13. 137                         result = ERR_PTR(retval);
  14. 138                 }
  15. 139         }
  16. 140         return result;
  17. 141 }

看一下__getname()函数:

[cpp] view plaincopyprint?
  1. 1099 #define __getname()     kmem_cache_alloc(names_cachep, SLAB_KERNEL)

就是在内核的slab空间中分配能够容纳name的空间~~~

看一下do_getname()函数:

[cpp] view plaincopyprint?
  1. 104 static inline int do_getname(const char *filename, char *page)
  2. 105 {
  3. 106         int retval;
  4. 107         unsigned long len = PATH_MAX;
  5. 108
  6. 109         if ((unsigned long) filename >= TASK_SIZE) {
  7. 110                 if (!segment_eq(get_fs(), KERNEL_DS))
  8. 111                         return -EFAULT;
  9. 112         } else if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
  10. 113                 len = TASK_SIZE - (unsigned long) filename;
  11. 114
  12. 115         retval = strncpy_from_user((char *)page, filename, len);/* 核心的一个步骤,其实就是将filename拷贝到刚刚在内核中分配的空间中 */
  13. 116         if (retval > 0) {
  14. 117                 if (retval < len)
  15. 118                         return 0;
  16. 119                 return -ENAMETOOLONG;
  17. 120         } else if (!retval)
  18. 121                 retval = -ENOENT;
  19. 122         return retval;
  20. 123 }

/* 2 */:这一步是需要找到一个没有使用的文件描述符fd

看一下这个函数get_unused_fd:看这个链接:get_unused_fd

/* 3 */:再回到上面看/* 3 */步骤,到现在为止,我们已经找到了一个可用的文件描述符fd了,然后我们要做的就是打开指定文件,然后将这个fd和打开的文件关联即可,/* 3 */步骤就是打开我们指定的文件!

下面会涉及到名字结构nameidata,所以先看看这个结构体:

[cpp] view plaincopyprint?
  1. 700 struct nameidata {
  2. 701         struct dentry *dentry;   /* 当前目录项对象 */
  3. 702         struct vfsmount *mnt;    /* 已安装的文件系统挂载点 */
  4. 703         struct qstr last;        /* 路径名称最后一部分 */
  5. 704         unsigned int flags;      /* 查询标识 */
  6. 705         int last_type;           /* 路径名称最后一部分类型 */
  7. 706 };

看这个函数filp_open:

[cpp] view plaincopyprint?
  1. 644 /*
  2. 645  * Note that while the flag value (low two bits) for sys_open means:
  3. 646  *      00 - read-only
  4. 647  *      01 - write-only
  5. 648  *      10 - read-write
  6. 649  *      11 - special
  7. 650  * it is changed into
  8. 651  *      00 - no permissions needed
  9. 652  *      01 - read-permission
  10. 653  *      10 - write-permission
  11. 654  *      11 - read-write
  12. 655  * for the internal routines (ie open_namei()/follow_link() etc). 00 is
  13. 656  * used by symlinks.
  14. 657  */
  15. 658 struct file *filp_open(const char * filename, int flags, int mode)
  16. 659 {
  17. 660         int namei_flags, error;
  18. 661         struct nameidata nd;
  19. 662
  20. 663         namei_flags = flags;
  21. 664         if ((namei_flags+1) & O_ACCMODE)
  22. 665                 namei_flags++;
  23. 666         if (namei_flags & O_TRUNC)
  24. 667                 namei_flags |= 2;
  25. 668         /* 根据文件名打开文件 */
  26. 669         error = open_namei(filename, namei_flags, mode, &nd);
  27. 670         if (!error)   /* 下面打开这个文件,这个函数返回的是file结构体指针!!! */
  28. 671                 return dentry_open(nd.dentry, nd.mnt, flags);
  29. 672
  30. 673         return ERR_PTR(error);
  31. 674 }

这个函数比较复杂,请看这个链接:open_namei

回头再看这个函数,dentry_open:这个函数返回的file结构体指针:

[cpp] view plaincopyprint?
  1. 676 struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
  2. 677 {
  3. 678         struct file * f;
  4. 679         struct inode *inode;
  5. 680         static LIST_HEAD(kill_list);
  6. 681         int error;
  7. 682
  8. 683         error = -ENFILE;
  9. 684         f = get_empty_filp();    /* 得到一个空的file结构体,如果出错或者内存不足,返回1error */
  10. 685         if (!f)
  11. 686                 goto cleanup_dentry;
  12. 687         f->f_flags = flags;   /* 一些赋值操作 */
  13. 688         f->f_mode = (flags+1) & O_ACCMODE;
  14. 689         inode = dentry->d_inode; /* 获得文件inode */
  15. 690         if (f->f_mode & FMODE_WRITE) {
  16. 691                 error = get_write_access(inode);
  17. 692                 if (error)
  18. 693                         goto cleanup_file;
  19. 694         }
  20. 695         /* 一些赋值操作 */
  21. 696         f->f_dentry = dentry; /* 目录项 */
  22. 697         f->f_vfsmnt = mnt;    /* 挂载点 */
  23. 698         f->f_pos = 0;         /* 文件相对开头偏移 */
  24. 699         f->f_reada = 0;       /* 预读标志 */
  25. 700         f->f_op = fops_get(inode->i_fop);   /* 文件操作函数 */
  26. 701         file_move(f, &inode->i_sb->s_files);/* 将新建的file链接进入inode对应的超级块的file链表中 */
  27. 702
  28. 703         /* preallocate kiobuf for O_DIRECT */
  29. 704         f->f_iobuf = NULL;
  30. 705         f->f_iobuf_lock = 0;
  31. 706         if (f->f_flags & O_DIRECT) {
  32. 707                 error = alloc_kiovec(1, &f->f_iobuf); /* 分配io buffer */
  33. 708                 if (error)
  34. 709                         goto cleanup_all;
  35. 710         }
  36. 711 <span style="white-space:pre">    </span>    /* 下面尝试打开文件,保证能够正常打开这个文件 */
  37. 712         if (f->f_op && f->f_op->open) {
  38. 713                 error = f->f_op->open(inode,f);
  39. 714                 if (error)
  40. 715                         goto cleanup_all;
  41. 716         }
  42. 717         f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
  43. 718
  44. 719         return f;  /* 返回创建好的file */
  45. 720         /* 下面都是出错处理 */
  46. 721 cleanup_all:
  47. 722         if (f->f_iobuf)
  48. 723                 free_kiovec(1, &f->f_iobuf);
  49. 724         fops_put(f->f_op);
  50. 725         if (f->f_mode & FMODE_WRITE)
  51. 726                 put_write_access(inode);
  52. 727         file_move(f, &kill_list); /* out of the way.. */
  53. 728         f->f_dentry = NULL;
  54. 729         f->f_vfsmnt = NULL;
  55. 730 cleanup_file:
  56. 731         put_filp(f);
  57. 732 cleanup_dentry:
  58. 733         dput(dentry);
  59. 734         mntput(mnt);
  60. 735         return ERR_PTR(error);
  61. 736 }
  62. 737

看一下这个函数get_empty_filp,得到一个空的file结构体:

[cpp] view plaincopyprint?
  1. <span style="font-size:14px;"> </span>26 /* Find an unused file structure and return a pointer to it.
  2. 27  * Returns NULL, if there are no more free file structures or
  3. 28  * we run out of memory.
  4. 29  *
  5. 30  * SMP-safe.
  6. 31  */
  7. 32 struct file * get_empty_filp(void)
  8. 33 {
  9. 34         static int old_max = 0;
  10. 35         struct file * f;
  11. 36
  12. 37         file_list_lock();
  13. 38         if (files_stat.nr_free_files > NR_RESERVED_FILES) {  /* 如果允许打开的数量已经超过系统允许的 */
  14. 39         used_one:
  15. 40                 f = list_entry(free_list.next, struct file, f_list); /* 在free_list中删除一个,留下了给新的file使用 */
  16. 41                 list_del(&f->f_list);
  17. 42                 files_stat.nr_free_files--;
  18. 43         new_one: /* 下面创建一个新的file结构体 */
  19. 44                 memset(f, 0, sizeof(*f));
  20. 45                 atomic_set(&f->f_count,1);
  21. 46                 f->f_version = ++event;
  22. 47                 f->f_uid = current->fsuid;
  23. 48                 f->f_gid = current->fsgid;
  24. 49                 f->f_maxcount = INT_MAX;
  25. 50                 list_add(&f->f_list, &anon_list);
  26. 51                 file_list_unlock();
  27. 52                 return f;   /* 返回file */
  28. 53         }
  29. 54         /*
  30. 55          * Use a reserved one if we're the superuser
  31. 56          */
  32. 57         if (files_stat.nr_free_files && !current->euid)
  33. 58                 goto used_one;
  34. 59         /*
  35. 60          * Allocate a new one if we're below the limit.  如果还可以创建file结构体,那么创建一个新的就OK
  36. 61          */
  37. 62         if (files_stat.nr_files < files_stat.max_files) {
  38. 63                 file_list_unlock();
  39. 64                 f = kmem_cache_alloc(filp_cachep, SLAB_KERNEL);  /* 在slab中分配一个新的file缓存 */
  40. 65                 file_list_lock();
  41. 66                 if (f) {
  42. 67                         files_stat.nr_files++; /* 数量++ */
  43. 68                         goto new_one;          /* 初始化这个新的值 */
  44. 69                 }
  45. 70                 /* Big problems... */
  46. 71                 printk(KERN_WARNING "VFS: filp allocation failed\n");
  47. 72
  48. 73         } else if (files_stat.max_files > old_max) {
  49. 74                 printk(KERN_INFO "VFS: file-max limit %d reached\n", files_stat.max_files);
  50. 75                 old_max = files_stat.max_files;
  51. 76         }
  52. 77         file_list_unlock();
  53. 78         return NULL;
  54. 79 }

/* 4 */:最后看一下fd_install函数,这个函数比较简单,就是将之前申请的文件描述符fd和打开的文件file结构体关联起来:

[cpp] view plaincopyprint?
  1. <span style="font-size:14px;"> </span>74 /*
  2. 75  * Install a file pointer in the fd array.
  3. 76  *
  4. 77  * The VFS is full of places where we drop the files lock between
  5. 78  * setting the open_fds bitmap and installing the file in the file
  6. 79  * array.  At any such point, we are vulnerable to a dup2() race
  7. 80  * installing a file in the array before us.  We need to detect this and
  8. 81  * fput() the struct file we are about to overwrite in this case.
  9. 82  *
  10. 83  * It should never happen - if we allow dup2() do it, _really_ bad things
  11. 84  * will follow.
  12. 85  */
  13. 86
  14. 87 void fd_install(unsigned int fd, struct file * file)
  15. 88 {
  16. 89         struct files_struct *files = current->files;  /* 获得当前进程文件打开表 */
  17. 90
  18. 91         write_lock(&files->file_lock);
  19. 92         if (files->fd[fd])   /* 如果这个fd下已经存在文件了,那么error! */
  20. 93                 BUG();
  21. 94         files->fd[fd] = file;/* 关联这个fd和新打开的文件 */
  22. 95         write_unlock(&files->file_lock);
  23. 96 }

至此,文件open操作完成了~~~

Linux文件系统(七)---系统调用之open操作(一)相关推荐

  1. Spark修炼之道(基础篇)——Linux大数据开发基础:第二节:Linux文件系统、文件夹(一)...

    本节主要内容 怎样获取帮助文档 Linux文件系统简单介绍 文件夹操作 訪问权限 1. 怎样获取帮助文档 在实际工作过程其中,常常会忘记命令的使用方式.比如ls命令后面能够跟哪些參数,此时能够使用ma ...

  2. 文件系统调用和Linux文件系统基础

    文件系统调用和Linux文件系统基础 keywords fdisk.LBA.CHS.MBR.super struct.directory.file.inode.inode table.block.fi ...

  3. Linux命令+shell脚本大全:操作文件系统

    Linux提供了一些不同的工具,我们可以利用它们轻松地在命令行中进行文件系统操作.可 使用键盘随心所欲地创建新的文件系统或者修改已有的文件系统.本节将会带你逐步了解命令行 下的文件系统交互的命令. 8 ...

  4. 低调的 Linux 文件系统家族

    在 Linux 中,最直观.最可见的部分就是 文件系统(file system).下面我们就来一起探讨一下关于 Linux 中国的文件系统,系统调用以及文件系统实现背后的原理和思想. 这些思想中有一些 ...

  5. 文件名为空linux,文件系统:隐匿在Linux背后的机制

    原标题:文件系统:隐匿在Linux背后的机制 在 Linux 中,最直观.最可见的部分就是 文件系统(file system) .下面我们就来一起探讨一下关于 Linux 中国的文件系统,系统调用以及 ...

  6. Linux 文件系统

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1 Linux 文件系统基本概念 2 Linux 文件系统调用 3 Linux 文件系统的实现 3.1 Linux 虚拟文件 ...

  7. [ linux ] 文件系统和目录结构详解

    昨天,有个小学弟了我一个linux面试题目,和她解答完之后我就想在C站开一个专栏,用于linux和windows的学习 我是这么想的,从linux入手,再写windows,最后总结常见区别 本文主要写 ...

  8. Linux 文件系统剖析

    Linux 文件系统剖析 按照分层结构讨论 Linux 文件系统 M. Tim Jones, 顾问工程师, Emulex Corp. 简介: 在文件系统方面,Linux® 可以算得上操作系统中的 &q ...

  9. linux文件系统dentry_NFS 文件系统源代码剖析

    NFS 文件系统概述 NFS(Network File System,网络文件系统)是一种基于网络的文件系统.它可以将远端服务器文件系统的目录挂载到本地文件系统的目录上,允许用户或者应用程序像访问本地 ...

  10. 文件系统:Linux文件系统剖析

    查看原文:http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/ 在文件系统方面,Linux® 可以算得上操作系统中的 " ...

最新文章

  1. 闭关第1天——儿童节快乐,永远年轻快乐
  2. Nuke编辑工具包新版 Cara VR 插件发布
  3. c语言或者cpp中位运算的技巧
  4. WPFToolkit DataGrid 使用介绍zz
  5. fedora 安装oracle 12c,Fedora 12下安装Oracle 11客户端
  6. 前端集成weex,你需要学习的objective-c基础
  7. 父类、派生类、方法重写、实例化后的执行顺序
  8. go字符串转byte_go语言学习-基本数据类型
  9. linux系统下装windows双系统,LINUX下安装WINDOWS双系统
  10. 其实我只想设置客户端实现跨域请求
  11. wannafly-day1 Problem A - Birthday
  12. WPF中INotifyPropertyChanged用法与数据绑定
  13. 短信验证码功能(阿里云版)
  14. 美电信运营商推云计算业务
  15. wamp mysql 密码_wamp如何设置数据库的密码
  16. 信息学奥赛C++语言:什么时候开会
  17. 2019 NIVIDIA ASIC/PD笔试题
  18. 对策论基础---矩阵对策的解法
  19. C语言3067答案,教师招聘《小学教育心理学》通关试题每日练(2020年03月03日-3067)...
  20. 微信小程序更新数组数据,页面不刷新问题

热门文章

  1. poj 1027 深搜
  2. 学用MVC4做网站:序
  3. 贡献一个新浪的幻灯片(javascript)
  4. flume学习(二):flume将log4j日志数据写入到hdfs
  5. 2017-2018-2 PDE 讨论班
  6. Windows Server 2012 R2 WSUS-7:查看状态报告
  7. ADSL桥接模式和路由模式的区别(转)
  8. libsvm 实现多分类原理
  9. 美图个性化推荐的实践与探索
  10. Matlab学习(可以用MATLAB作曲)