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

 #include <unistd.h>

 int dup(int oldfd);
int dup2(int oldfd, int newfd);

根据manual的解释:

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

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

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

二、分析dup

 static inline long
dup (int fd)
{
return sys_dup(fd);
}

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

 asmlinkage long sys_dup(unsigned int fildes)
{
int ret = -EBADF;
struct file * file = fget(fildes); if (file)
ret = dupfd(file, );
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下标的指针。

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 >= ) {
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 < )
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 + ; error = newfd; out:
return error;
}

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

 /*
* Install a file pointer in the fd array.
*
* The VFS is full of places where we drop the files lock between
* setting the open_fds bitmap and installing the file in the file
* array. At any such point, we are vulnerable to a dup2() race
* installing a file in the array before us. We need to detect this and
* fput() the struct file we are about to overwrite in this case.
*
* It should never happen - if we allow dup2() do it, _really_ bad things
* will follow.
*/ 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就返回该值。

三、dup2

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

Linux内核分析:dup、dup2的实现的更多相关文章

  1. 《Linux内核分析》 第六周

    <Linux内核分析> 第6周 一.进程的描述 1.进程控制块PCB 2.linux下的进程转化图 TASK_RUNNING可以是就绪态或者执行态,具体取决于系统调用 TASK_ZOMBI ...

  2. linux内核分析作业8:理解进程调度时机跟踪分析进程调度与进程切换的过程

    1. 实验目的 选择一个系统调用(13号系统调用time除外),系统调用列表,使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 分析汇编代码调用系统调用的工作过程,特别是参数的传递的方 ...

  3. Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序

            1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...

  4. linux内核分析作业6:分析Linux内核创建一个新进程的过程

    task_struct结构: struct task_struct {   volatile long state;进程状态  void *stack; 堆栈  pid_t pid; 进程标识符  u ...

  5. linux内核分析作业5:分析system_call中断处理过程

    1.增加 Menu 内核命令行 调试系统调用. 步骤:删除menu git clone        (tab) make rootfs 这就是我们将 fork 函数写入 Menu 系统内核后的效果, ...

  6. linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作

    一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...

  7. linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码

    计算机如何工作 三个法宝:存储程序计算机.函数调用堆栈.中断机制. 堆栈 函数调用框架 传递参数 保存返回地址 提供局部变量空间 堆栈相关的寄存器 Esp 堆栈指针  (stack pointer) ...

  8. linux内核分析作业3:跟踪分析Linux内核的启动过程

    内核源码目录 1. arch:录下x86重点关注 2. init:目录下main.c中的start_kernel是启动内核的起点 3. ipc:进程间通信的目录 实验 使用实验楼的虚拟机打开shell ...

  9. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

  10. 《Linux内核分析》期末总结

    Linux内核设计期中总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 前八周博客汇总及总结 Linux内核设计第一周——从汇编语言出发理解计算机工作原理 我们学习了汇编语言的基础知识,这一 ...

随机推荐

  1. js 相关知识整理(一)

    真正声明变量,是用逗号隔开的 EcM5:严格模式“use strict” java与js 语言的区别: 1.弱类型语言 1.声明变量时不需要提前指定数据类型 2.同一个变量可先后保存不同类型的数据 3 ...

  2. status bar、navigationBar、tableView吸顶view设置

    1. 隐藏navigationBar self.navigationController.navigationBar.hidden = YES; 2. status bar设置 -(void)view ...

  3. Ubuntu 14.04 编译安装 boost 1.58

    简介 Boost is a set of libraries for the C++ programming language that provide support for tasks and s ...

  4. 一篇文章让Oracle程序猿学会MySql【未完待续】

    一篇文章让Oracle DB学会MySql[未完待续] 随笔前言: 本篇文章是针对已经能够熟练使用Oracle数据库的DB所写的快速学会MySql,为什么敢这么说,是因为本人认为Oracle在功能性方 ...

  5. angularJs之http后台访问数据

    AngularJS  XMLHttpRequest $http  是AngularJS中的一个核心服务,用于读取远程服务器的数据. 读取JSON 文件 以下是存储在web服务器上的JSON 文件: h ...

  6. vim 命令加解密文件

    加密文件 vim file :X  (大写X,是加密 ,小写x是保存) 输入密码: 确认密码: 解除密码: vim file :set key= :wq 命令模式下,输入 /word 后回车,即查找w ...

  7. 【原创】JMeter学习(三十七)Jmeter录制手机app脚本

    环境准备: 1.手机 2.wifi 3.Jmeter   具体步骤: 1.启动Jmeter: 2.“测试计划”中添加“线程组”: 3.“工作台”中添加“HTTP代理服务器”: 4.配置代理服务器:Gl ...

  8. Python之路-python(mysql介绍和安装、pymysql、ORM sqlachemy)

    本节内容 1.数据库介绍 2.mysql管理 3.mysql数据类型 4.常用mysql命令 创建数据库 外键 增删改查表 5.事务 6.索引 7.python 操作mysql 8.ORM sqlac ...

  9. c语言第8次作业

    #include<stdio.h> int main() { ]={}; int m; ; ;m<;m++) { a[m]=m+; !=&&a[m]%!=) n++; ...

  10. Android targetSdkVersion 对生命周期的影响

    一直都认为当手机进入休眠时,Activity的生命周期会进入onPause()-->onStop()状态,但是今天偶然遇到了一个百思不得其解的问题,如果在AndroidMainfest.xml文 ...