linux sys_dup

sys_dup()的主要工作就是用來“複製”一個打開的文件號,使兩個文件號都指向同一個文件。先看一下涉及到的數據結構:

1 數據結構

在Linux中每一個進程的數據是存儲在一個task_struct結構(定義在sched.h中)中的。

struct task_struct {
  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/* filesystem information */
    struct fs_struct *fs;
/* open file information */
    struct files_struct *files;
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
};

該結構中有一個用於保存打開文件信息的成員:files,該成員類型是:struct files_struct*(定義在file.h)。

/*
 * Open file table structure
 */
struct files_struct {
        atomic_t count;
        spinlock_t file_lock;     /* Protects all the below members.  Nests inside tsk->alloc_lock */
        int max_fds;
        int max_fdset;
        int next_fd;
        struct file ** fd;      /* current fd array */
        fd_set *close_on_exec;
        fd_set *open_fds;
        fd_set close_on_exec_init;
        fd_set open_fds_init;
        struct file * fd_array[NR_OPEN_DEFAULT];
};

可以看到該結構中保存了所有與進程打開文件相關的信息,其中fd_array是一個struct file*(定義在file.h)類型的數組。

struct file {
    struct list_head    f_list;
    struct dentry        *f_dentry;
    struct vfsmount         *f_vfsmnt;
    struct file_operations    *f_op;
    atomic_t        f_count;
    unsigned int         f_flags;
    mode_t            f_mode;
    int            f_error;
    loff_t            f_pos;
    struct fown_struct    f_owner;
    unsigned int        f_uid, f_gid;
    struct file_ra_state    f_ra;

    size_t            f_maxcount;
    unsigned long        f_version;
    void            *f_security;

    /* needed for tty driver, and maybe others */
    void            *private_data;

#ifdef CONFIG_EPOLL
    /* Used by fs/eventpoll.c to link all the hooks to this file */
    struct list_head    f_ep_links;
    spinlock_t        f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
    struct address_space    *f_mapping;
};

struct file就是保存了每個打開文件信息的數據結構。

2 實現

asmlinkage long sys_dup(unsigned int fildes)
{
    int ret = -EBADF;
    struct file * file = fget(fildes);

    if (file)
        ret = dupfd(file, 0);
    return ret;
}

在sys_dup函數中,關鍵的就是兩步,fget獲取指定文件描述符的struct file指針,然後調用dupfd,至於dupfd的具體實現,我們接着往下走。

struct file fastcall *fget(unsigned int fd)
{
    struct file *file;
    struct files_struct *files = current->files;

    spin_lock(&files->file_lock);
    file = fcheck_files(files, fd);
    if (file)
        get_file(file);
    spin_unlock(&files->file_lock);
    return file;
}

可以看到fget函數的實現就是首先獲取一個files_struct指針,我們知道files_struct保存了所有打開文件信息(其中current是當前進程的struct task_struct指針),然後加鎖,調用fcheck_files,獲取file指針,如果file不爲空,則調用get_file,下面我們看下這兩個函數的實現。

static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
{
    struct file * file = NULL;

    if (fd < files->max_fds)
        file = files->fd[fd];
    return file;
}

#define get_file(x)    atomic_inc(&(x)->f_count)

現在已經可以知道,fcheck_files函數的具體步驟是首先判斷給定的文件描述符fd是否小於最大文件描述符max_fds,如果小於,則返回fd數組中對應該fd下標的指針,即取出原來文件的file指針

get_file的作用是原子的增加f_count,也就是該文件的引用計數(在close的時候會減這個值)。

現在再回到sys_dup中,看一下dumfd的實現。

static int dupfd(struct file *file, unsigned int start)
{
    struct files_struct * files = current->files;
    int fd;

    spin_lock(&files->file_lock);
    fd = locate_fd(files, file, start);
    if (fd >= 0) {
        FD_SET(fd, files->open_fds);
        FD_CLR(fd, files->close_on_exec);
        spin_unlock(&files->file_lock);
        fd_install(fd, file);
    } else {
        spin_unlock(&files->file_lock);
        fput(file);
    }

    return fd;
}

該函數的具體步驟如下:

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的實現。

/*
 * locate_fd finds a free file descriptor in the open_fds fdset,
 * expanding the fd arrays if necessary.  Must be called with the
 * file_lock held for write.
 */

static int locate_fd(struct files_struct *files, 
                struct file *file, unsigned int orig_start)
{
    unsigned int newfd;
    unsigned int start;
    int error;

    error = -EINVAL;
    if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
        goto out;

repeat:
    /*
     * Someone might have closed fd's in the range
     * orig_start..files->next_fd
     */
    start = orig_start;
    if (start < files->next_fd)
        start = files->next_fd;

    newfd = start;
    if (start < files->max_fdset) {
        newfd = find_next_zero_bit(files->open_fds->fds_bits,
            files->max_fdset, start);
    }
    
    error = -EMFILE;
    if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
        goto out;

    error = expand_files(files, newfd);
    if (error < 0)
        goto out;

    /*
     * If we needed to expand the fs array we
     * might have blocked - try again.
     */
    if (error)
        goto repeat;

    if (start <= files->next_fd)
        files->next_fd = newfd + 1;
    
    error = newfd;
    
out:
    return error;
}

根據該函數的註釋即可知道它的所用就是:找到一個沒有被使用的文件描述符,從start開始

void fastcall fd_install(unsigned int fd, struct file * file)
{
    struct files_struct *files = current->files;
    spin_lock(&files->file_lock);
    if (unlikely(files->fd[fd] != NULL))
        BUG();
    files->fd[fd] = file;
    spin_unlock(&files->file_lock);
}

fd_install的作用就是把fd指針數組對應fd下標的指針賦值爲file。

到此回到dupfd,返回獲取的fd,這就是新的拷貝,然後sys_dup就返回該值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章