一、首先需要看一下這兩個函數的作用:
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。