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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM