注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

BCB-DG's Blog

...

 
 
 

日志

 
 

Linux内核中的dup系统调用  

2013-09-24 10:58:05|  分类: Linux |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
//轉
内核版本:2.6.14
  dup系统调用的服务例程为sys_dup函数,定义在fs/fcntl.c中。sys_dup()的代码也许称得上是最简单的之一了,但是就是这么 一个简单的系统调用,却成就了linux系统最著名的一个特性:输入/输出重定向。sys_dup()的主要工作就是用来“复制”一个打开的文件号,并使 两个文件号都指向同一个文件,下面我们来分析一下它的代码。
  1、sys_dup源码分析
  asmlinkage long sys_dup(unsigned int fildes)//sys_dup函数的参数,即fildes,是文件描述符fd
  {
  int ret = -EBADF;
  struct file * file = fget(fildes);//通过文件描述符找到对应的文件
  if (file)
  ret = dupfd(file, 0);//分配一个新的文件描述符fd,并将fd和file联系起来
  return ret;
  }
  1.1 fget(fildes)
  struct file fastcall *fget(unsigned int fd)
  {
  struct file *file;
  struct files_struct *files = current->files;//获得当前进程的打开文件表
  rcu_read_lock();
  file = fcheck_files(files, fd);//根据fd从打开文件表files里取出相应的file结构变量
  if (file) {
  if (!rcuref_inc_lf(&file->f_count)) {  //增加引用
  /* File object ref couldn't be taken */
  rcu_read_unlock();
  return NULL;
  }
  }
  rcu_read_unlock();
  return file;
  }
  static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
  {
  struct file * file = NULL;
  struct fdtable *fdt = files_fdtable(files);
  if (fd < fdt->max_fds)
  file = rcu_dereference(fdt->fd[fd]);
  return file;
  }
  1.2 dupfd(file, 0)
  static int dupfd(struct file *file, unsigned int start)
  {
  struct files_struct * files = current->files;
  struct fdtable *fdt;
  int fd;
  spin_lock(&files->file_lock);
  fd = locate_fd(files, file, start);//分配文件描述符
  if (fd >= 0) {
  /* locate_fd() may have expanded fdtable, load the ptr */
  fdt = files_fdtable(files);//获得文件描述符表
  FD_SET(fd, fdt->open_fds);//设置打开文件标记
  FD_CLR(fd, fdt->close_on_exec);
  spin_unlock(&files->file_lock);
  fd_install(fd, file);//建立fd和file的联系,之后通过fd就可以找到file
  } else {
  spin_unlock(&files->file_lock);
  fput(file);
  }
  return fd;
  }
  2、内核初始化中的相关源码分析
  static int init(void * unused)
  {
  ...
  if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
  printk(KERN_WARNING "Warning: unable to open an initial console.\n");
//打开控制台,这样init进程就拥有一个控制台,并可以从中读取输入信息,也可以向其中写入信息
  (void) sys_dup(0);//调用dup打开/dev/console文件描述符两次,这样控制太设备也可以供表述输出和标准错误使用(文件描述符为1和2)
  (void) sys_dup(0);
  //假设sys_open((const char __user *) "/dev/console", O_RDWR, 0) 成功执行,init进程就拥有3个文件描述符(标准输入、标准输出和标准错误)
  ...
  }
  3、系统重定向
  我们通过一个简单的来讲解重定向。
  当我们在shell下输入如下命令:“echo hello!”,这条命令要求shell进程执行一个可执行文件echo,参数为“hello!”。当shell接收到命令之后,先找到 bin/echo,然后fork()出一个子进程让他执行bin/echo,并将参数传递给它,而这个进程从shell继承了三个标准文件,即标准输入 (stdin),标准输出(stdout)和标准出错信息(stderr),它们三个的文件号分别为0、1、2。而至于echo进程的工作很简单,就是将 参数“hello!”写道标准输出文件中去,通常都是我们的显示器上。但是如果我们将命令改成“echo hello! > foo”,则在执行时输出将会被重定向到磁盘文件foo中。我们假定在此之前该shell进程只有三个标准文件打开,文件号分别为0、1、2,以上命令行 将按如下序列执行:
  (1)打开或创建磁盘文件foo,如果foo中原来有内容,则清除原来内容,其文件号为3。
  (2)通过dup()复制文件stdout,即将文件号1出的file结构指针复制到文件号4处,目的是将stdout的file指针暂时保存一下
  (3)关闭stdout,即1号文件,但是由于4号文件对stdout也同时有个引用,所以stdout文件并未真正关闭,只是腾出1号文件号位置。
  (4)通过dup(),复制3号文件(即磁盘文件foo),由于1号文件关闭,其位置空缺,故3号文件被复制到1号,即进程中原来指向stdout的指针指向了foo。
  (5)通过系统调用fork()和exec()创建子进程并执行echo,子进程在执行echo前夕关闭3号和4号文件,只留下0、1、2三个 文件,请注意,这时的1号文件已经不是stdout而是磁盘文件foo了。当echo想向stdout文件写入“hello!”时自然就写入到了foo 中。
  (6)回到shell后,关闭指向foo的1号与3号文件,再用dup()和close()将2号恢复至stdout,这样shell就恢复了0、1、2三个标准输入/输出文件。
  由此可见,当echo程序(或其他)在运行的时候并不知道stdout(对于stdin和stderr同样)指向什么,进程与实际输出文件或设 备的结合是在运行时由其父进程“包办”的。这样就简化了子进程的程序设计,因为在设计时只要跟三个逻辑上存在的文件打交道就可以了,类似于面向对象中的多 态和重载。
  评论这张
 
阅读(434)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017