zoukankan      html  css  js  c++  java
  • Linux内核分析:dup、dup2的实现

     一、首先需要看一下这两个函数的作用:

    1 #include <unistd.h>
    2 
    3 int dup(int oldfd);
    4 int dup2(int oldfd, int newfd);

    根据manual的解释:

    dup:创建一份oldfd的拷贝,使用最小的文件描述符作为新的文件描述符。

    dup2:创建一份oldfd的拷贝,使用指定的newfd作为新的文件描述符。

    要看这两个函数是怎么实现的,首先得知道Linux对于文件描述符是怎么处理的,参考这篇文章

    二、分析dup

    1 static inline long
    2 dup (int fd)
    3 {
    4     return sys_dup(fd);
    5 }

    这里看到dup调用了函数sys_dup。

    1 asmlinkage long sys_dup(unsigned int fildes)
    2 {
    3     int ret = -EBADF;
    4     struct file * file = fget(fildes);
    5 
    6     if (file)
    7         ret = dupfd(file, 0);
    8     return ret;
    9 }

    在sys_dup函数中,关键的就是两步,fget获取指定文件描述符的struct file指针,然后调用dupfd,至于dupfd的具体实现,我们接着往下走。

     1 struct file fastcall *fget(unsigned int fd)
     2 {
     3     struct file *file;
     4     struct files_struct *files = current->files;
     5 
     6     spin_lock(&files->file_lock);
     7     file = fcheck_files(files, fd);
     8     if (file)
     9         get_file(file);
    10     spin_unlock(&files->file_lock);
    11     return file;
    12 }

    可以看到fget函数的实现就是首先获取一个files_struct指针,我们知道files_struct保存了所有打开文件信息(其中current是当前进程的struct task_struct指针),然后加锁,调用fcheck_files,获取file指针,如果file不为空,则调用get_file,下面我们看下这两个函数的实现。

     1 static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
     2 {
     3     struct file * file = NULL;
     4 
     5     if (fd < files->max_fds)
     6         file = files->fd[fd];
     7     return file;
     8 }
     9 
    10 #define get_file(x)    atomic_inc(&(x)->f_count)

    现在已经可以知道,fcheck_files函数的具体步骤是首先判断给定的文件描述符fd是否小于最大文件描述符max_fds,如果小于,则返回fd数组中对应该fd下标的指针。

    get_file的作用是原子的增加f_count,也就是该文件的引用计数(在close的时候会减这个值)。

    现在再回到sys_dup中,看一下dumfd的实现。

     1 static int dupfd(struct file *file, unsigned int start)
     2 {
     3     struct files_struct * files = current->files;
     4     int fd;
     5 
     6     spin_lock(&files->file_lock);
     7     fd = locate_fd(files, file, start);
     8     if (fd >= 0) {
     9         FD_SET(fd, files->open_fds);
    10         FD_CLR(fd, files->close_on_exec);
    11         spin_unlock(&files->file_lock);
    12         fd_install(fd, file);
    13     } else {
    14         spin_unlock(&files->file_lock);
    15         fput(file);
    16     }
    17 
    18     return fd;
    19 }

    该函数的具体步骤如下:

    1、通过current->files获取struct files_struct指针。

    2、加锁,完成后会解锁。

    3、调用locate_fd函数获取一个fd,具体获取规则下面再看。

    4、如果获取到的fd>=0,则调用FD_SET、FD_CLR、解锁、fd_install。关键在于fd_install;否则调用解锁、fput。

    下面再看一下locate_fd、fd_install、fput的实现。

     1 /*
     2  * locate_fd finds a free file descriptor in the open_fds fdset,
     3  * expanding the fd arrays if necessary.  Must be called with the
     4  * file_lock held for write.
     5  */
     6 
     7 static int locate_fd(struct files_struct *files, 
     8                 struct file *file, unsigned int orig_start)
     9 {
    10     unsigned int newfd;
    11     unsigned int start;
    12     int error;
    13 
    14     error = -EINVAL;
    15     if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
    16         goto out;
    17 
    18 repeat:
    19     /*
    20      * Someone might have closed fd's in the range
    21      * orig_start..files->next_fd
    22      */
    23     start = orig_start;
    24     if (start < files->next_fd)
    25         start = files->next_fd;
    26 
    27     newfd = start;
    28     if (start < files->max_fdset) {
    29         newfd = find_next_zero_bit(files->open_fds->fds_bits,
    30             files->max_fdset, start);
    31     }
    32     
    33     error = -EMFILE;
    34     if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
    35         goto out;
    36 
    37     error = expand_files(files, newfd);
    38     if (error < 0)
    39         goto out;
    40 
    41     /*
    42      * If we needed to expand the fs array we
    43      * might have blocked - try again.
    44      */
    45     if (error)
    46         goto repeat;
    47 
    48     if (start <= files->next_fd)
    49         files->next_fd = newfd + 1;
    50     
    51     error = newfd;
    52     
    53 out:
    54     return error;
    55 }

    根据该函数的注释即可知道它的所用就是:找到一个没有被使用的文件描述符,从start开始(这里就是dup和dup2的区别所在)。

     1 /*
     2  * Install a file pointer in the fd array.  
     3  *
     4  * The VFS is full of places where we drop the files lock between
     5  * setting the open_fds bitmap and installing the file in the file
     6  * array.  At any such point, we are vulnerable to a dup2() race
     7  * installing a file in the array before us.  We need to detect this and
     8  * fput() the struct file we are about to overwrite in this case.
     9  *
    10  * It should never happen - if we allow dup2() do it, _really_ bad things
    11  * will follow.
    12  */
    13 
    14 void fastcall fd_install(unsigned int fd, struct file * file)
    15 {
    16     struct files_struct *files = current->files;
    17     spin_lock(&files->file_lock);
    18     if (unlikely(files->fd[fd] != NULL))
    19         BUG();
    20     files->fd[fd] = file;
    21     spin_unlock(&files->file_lock);
    22 }

    fd_install的作用就是把fd指针数组对应fd下标的指针赋值为file。

    到此回到dupfd,返回获取的fd,这就是新的拷贝,然后sys_dup就返回该值。

    三、dup2

    dup2的实现跟dup类似,关键的差别就是dup2返回指定的newfd,dup返回最小可用的fd。

  • 相关阅读:
    camp训练day2
    LCA板子题
    牛客多校第一场
    P1063 能量项链 区间DP
    64. Minimum Path Sum
    46. Permutations
    216. Combination Sum III
    62. Unique Paths
    53. Maximum Subarray
    22. Generate Parentheses
  • 原文地址:https://www.cnblogs.com/lit10050528/p/6206235.html
Copyright © 2011-2022 走看看