• Linux文件系统(七)---系统调用之open操作(一)



    (内核2.4.37)


    一、

    当我们打开一个文件的时候。须要获得文件的文件描写叙述符(前面已经说过事实上就是文件数组下标)。通常是通过函数open来完毕。这个系统调用在<unistd.h>头文件里声明定义,我们看一下源代码:

    530 static inline long open(const char * name, int mode, int flags)
    531 {
    532         return sys_open(name, mode, flags);
    533 }

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

    mode:參数可选:

     32 #define S_IRWXU 00700     文件全部者可读可写可运行
     33 #define S_IRUSR 00400     文件全部者可读
     34 #define S_IWUSR 00200     文件全部者可写
     35 #define S_IXUSR 00100     文件全部者可运行
     36 
     37 #define S_IRWXG 00070     文件用户组可写可读可运行
     38 #define S_IRGRP 00040     文件用户组可读
     39 #define S_IWGRP 00020     文件用户组可写
     40 #define S_IXGRP 00010     文件用户组可运行
     41 
     42 #define S_IRWXO 00007     其它用户可写可读可运行
     43 #define S_IROTH 00004     其它用户可读
     44 #define S_IWOTH 00002     其它用户可写
     45 #define S_IXOTH 00001     其它用户可运行

    flags:在fcntl.h中定义

      7 #define O_RDONLY             00
      8 #define O_WRONLY             01
      9 #define O_RDWR               02
     10 #define O_CREAT            0100 /* not fcntl */
     11 #define O_EXCL             0200 /* not fcntl */
     12 #define O_NOCTTY           0400 /* not fcntl */
     13 #define O_TRUNC           01000 /* not fcntl */
     14 #define O_APPEND          02000
     15 #define O_NONBLOCK        04000
     16 #define O_NDELAY        O_NONBLOCK
     17 #define O_SYNC           010000
     18 #define FASYNC           020000 /* fcntl, for BSD compatibility */
     19 #define O_DIRECT         040000 /* direct disk access hint */
     20 #define O_LARGEFILE     0100000
     21 #define O_DIRECTORY     0200000 /* must be a directory */
     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中):

    800 asmlinkage long sys_open(const char * filename, int flags, int mode)
    801 {
    802         char * tmp;
    803         int fd, error;
    804 
    805 #if BITS_PER_LONG != 32
    806         flags |= O_LARGEFILE;
    807 #endif
    808         tmp = getname(filename);   /* 1 */
    809         fd = PTR_ERR(tmp);
    810         if (!IS_ERR(tmp)) {
    811                 fd = get_unused_fd();  /* 2 */
    812                 if (fd >= 0) {
    813                         struct file *f = filp_open(tmp, flags, mode); /* 3 */
    814                         error = PTR_ERR(f);
    815                         if (IS_ERR(f))
    816                                 goto out_error;
    817                         fd_install(fd, f);    /* 4 */
    818                 }
    819 out:
    820                 putname(tmp);
    821         }
    822         return fd;
    823 
    824 out_error:
    825         put_unused_fd(fd);
    826         fd = error;
    827         goto out;
    828 }

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

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

    125 char * getname(const char * filename)
    126 {
    127         char *tmp, *result;
    128 
    129         result = ERR_PTR(-ENOMEM);
    130         tmp = __getname();     /* 这玩意吧事实上是分配内核中空间用户装name */
    131         if (tmp)  {
    132                 int retval = do_getname(filename, tmp);  /* 事实上就是将filename复制到tmp中 */
    133 
    134                 result = tmp;
    135                 if (retval < 0) {
    136                         putname(tmp);
    137                         result = ERR_PTR(retval);
    138                 }
    139         }
    140         return result;
    141 }

    看一下__getname()函数:

    1099 #define __getname()     kmem_cache_alloc(names_cachep, SLAB_KERNEL)
    就是在内核的slab空间中分配可以容纳name的空间~~~


    看一下do_getname()函数:

    104 static inline int do_getname(const char *filename, char *page)
    105 {
    106         int retval;
    107         unsigned long len = PATH_MAX;
    108 
    109         if ((unsigned long) filename >= TASK_SIZE) {
    110                 if (!segment_eq(get_fs(), KERNEL_DS))
    111                         return -EFAULT;
    112         } else if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
    113                 len = TASK_SIZE - (unsigned long) filename;
    114 
    115         retval = strncpy_from_user((char *)page, filename, len);/* 核心的一个步骤,事实上就是将filename复制到刚刚在内核中分配的空间中 */
    116         if (retval > 0) {
    117                 if (retval < len)
    118                         return 0;
    119                 return -ENAMETOOLONG;
    120         } else if (!retval)
    121                 retval = -ENOENT;
    122         return retval;
    123 }

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

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


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

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

    700 struct nameidata {
    701         struct dentry *dentry;   /* 当前文件夹项对象 */
    702         struct vfsmount *mnt;    /* 已安装的文件系统挂载点 */
    703         struct qstr last;        /* 路径名称最后一部分 */
    704         unsigned int flags;      /* 查询标识 */
    705         int last_type;           /* 路径名称最后一部分类型 */
    706 };

    看这个函数filp_open:

    644 /*
    645  * Note that while the flag value (low two bits) for sys_open means:
    646  *      00 - read-only
    647  *      01 - write-only
    648  *      10 - read-write
    649  *      11 - special
    650  * it is changed into
    651  *      00 - no permissions needed
    652  *      01 - read-permission
    653  *      10 - write-permission
    654  *      11 - read-write
    655  * for the internal routines (ie open_namei()/follow_link() etc). 00 is
    656  * used by symlinks.
    657  */
    658 struct file *filp_open(const char * filename, int flags, int mode)
    659 {
    660         int namei_flags, error;
    661         struct nameidata nd;
    662 
    663         namei_flags = flags;
    664         if ((namei_flags+1) & O_ACCMODE)
    665                 namei_flags++;
    666         if (namei_flags & O_TRUNC)
    667                 namei_flags |= 2;
    668         /* 依据文件名称打开文件 */
    669         error = open_namei(filename, namei_flags, mode, &nd);
    670         if (!error)   /* 以下打开这个文件,这个函数返回的是file结构体指针!

    */ 671 return dentry_open(nd.dentry, nd.mnt, flags); 672 673 return ERR_PTR(error); 674 }


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


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

    676 struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
    677 {
    678         struct file * f;
    679         struct inode *inode;
    680         static LIST_HEAD(kill_list);
    681         int error;
    682 
    683         error = -ENFILE;
    684         f = get_empty_filp();    /* 得到一个空的file结构体,假设出错或者内存不足,返回1error */
    685         if (!f)
    686                 goto cleanup_dentry;
    687         f->f_flags = flags;   /* 一些赋值操作 */
    688         f->f_mode = (flags+1) & O_ACCMODE;
    689         inode = dentry->d_inode; /* 获得文件inode */
    690         if (f->f_mode & FMODE_WRITE) {
    691                 error = get_write_access(inode);
    692                 if (error)
    693                         goto cleanup_file;
    694         }
    695         /* 一些赋值操作 */
    696         f->f_dentry = dentry; /* 文件夹项 */
    697         f->f_vfsmnt = mnt;    /* 挂载点 */
    698         f->f_pos = 0;         /* 文件相对开头偏移 */
    699         f->f_reada = 0;       /* 预读标志 */
    700         f->f_op = fops_get(inode->i_fop);   /* 文件操作函数 */
    701         file_move(f, &inode->i_sb->s_files);/* 将新建的file链接进入inode相应的超级块的file链表中 */
    702 
    703         /* preallocate kiobuf for O_DIRECT */
    704         f->f_iobuf = NULL;
    705         f->f_iobuf_lock = 0;
    706         if (f->f_flags & O_DIRECT) {
    707                 error = alloc_kiovec(1, &f->f_iobuf); /* 分配io buffer */
    708                 if (error)
    709                         goto cleanup_all;
    710         }
    711 <span style="white-space:pre">	</span>    /* 以下尝试打开文件,保证可以正常打开这个文件 */
    712         if (f->f_op && f->f_op->open) { 
    713                 error = f->f_op->open(inode,f);
    714                 if (error)
    715                         goto cleanup_all;
    716         }
    717         f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
    718 
    719         return f;  /* 返回创建好的file */
    720         /* 以下都是出错处理 */
    721 cleanup_all:
    722         if (f->f_iobuf)
    723                 free_kiovec(1, &f->f_iobuf);
    724         fops_put(f->f_op);
    725         if (f->f_mode & FMODE_WRITE)
    726                 put_write_access(inode);
    727         file_move(f, &kill_list); /* out of the way.. */
    728         f->f_dentry = NULL;
    729         f->f_vfsmnt = NULL;
    730 cleanup_file:
    731         put_filp(f);
    732 cleanup_dentry:
    733         dput(dentry);
    734         mntput(mnt);
    735         return ERR_PTR(error);
    736 }
    737 

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

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


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

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

    至此。文件open操作完毕了~~~
















  • 相关阅读:
    C#对SQLite、Access数据库操作的封装,很好用的~
    如何使用MFC连接Access数据库
    字节、十六进制字符串相互转换(asc2hex、hex2asc)
    Oracle数据库模式关系和数据备份导出导入
    Oracle数据库sql常用
    Oracle数据库的函数,存储过程,程序包,游标,触发器
    收集一下
    JS 获取随机颜色值
    IDEA webapp文件夹不识别解决方案
    使用postman请求响应Invalid CORS request
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7060629.html
Copyright © 2020-2023  润新知