sys_dup函数

linux纷繁复杂的内核代码中,sys_dup()的代码也许称得上是最简单的之一了,但是就是这么一个简单的系统调用,却成就了unix/linux系统最著名的一个特性:输入/输出重定向
    sys_dup()的主要工作就是用来“复制”一个打开的文件号,使两个文件号都指向同一个文件。既然说简单,我们就首先来看一下它的代码(定义在fs/fcntl.c中):
187  asmlinkage long sys_dup(unsigned int fildes)
188  {
189      int ret = -EBADF;
190      struct file * file = fget(fildes);
191  
192      if (file)
193          ret = dupfd(file, 0);
194      return ret;
195  }

sys_dup()的主体是dupfd()(定义在同一个文件中):
116  static int dupfd(struct file *file, int start)
117  {
118      struct files_struct * files = current->files;
119      int ret;
120  
121      ret = locate_fd(files, file, start);
122      if (ret < 0) 
123          goto out_putf;
124      allocate_fd(files, file, ret);
125      return ret;
126  
127  out_putf:
128      write_unlock(&files->file_lock);
129      fput(file);
130      return ret;
131  }

    而这么一个简单的系统调用是如何完成重定向这个艰巨的任务的呢?我们不妨先看个例子。
    当我们在shell下输入如下命令:“echo hello!”,这条命令要求shell进程执行一个可执行文件echo,参数为“hello!”。当shell接收到命令之后,先找到bin/echo,然后fork()出一个子进程让他执行bin/echo,并将参数传递给它,而这个进程从shell继承了三个标准文件,即标准输入(stdin),标准输出(stdout)和标准出错信息(stderr),他们三个的文件号分别为012。而至于echo进程的工作很简单,就是将参数“hello!”写道标准输出文件中去,通常都是我们的显示器上。但是如果我们将命令改成“echo hello! > foo”,则在执行时输出将会被重定向到磁盘文件foo中。我们假定在此之前该shell进程只有三个标准文件打开,文件号分别为012,以上命令行将按如下序列执行:
    (1) 打开或创建磁盘文件foo,如果foo中原来有内容,则清除原来内容,其文件号为3
    (2) 通过dup()复制文件stdout,即将文件号1出的file结构指针复制到文件号4处,目的是将stdoutfile指针暂时保存一下
    (3) 关闭stdout,即1号文件,但是由于4号文件对stdout也同时有个引用,所以stdout文件并未真正关闭,只是腾出1号文件号位置。
    (4) 通过dup(),复制3号文件(即磁盘文件foo),由于1号文件关闭,其位置空缺,故3号文件被复制到1号,即进程中原来指向stdout的指针指向了foo
    (5) 通过系统调用fork()exec()创建子进程并执行echo,子进程在执行echo前夕关闭3号和4号文件,只留下012三个文件,请注意,这时的1号文件已经不是stdout而是磁盘文件foo了。当echo想向stdout文件写入“hello!”时自然就写入到了foo中。
    (6) 回到shell后,关闭指向foo1号与3号文件文件,再用dup()close()2号恢复至stdout,这样shell就恢复了012三个标准输入/输出文件。

    由此可见,当echo程序(或其他)在运行的时候并不知道stdout(对于stdinstderr同样)指向什么,进程与实际输出文件或设备的结合是在运行时由其父进程“包办”的。这样就简化了子进程的程序设计,因为在设计时只要跟三个逻辑上存在的文件打交道就可以了。可能有人会觉得这很像面向对象中的多态和重载,没有什么新奇之处,但是如果你活在30甚至40年前,可能你会改变你的看法。 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[ 4.858794] uwe5621_bt_tty_init [ 4.862131] mtty_probe unisoc soc, continue [ 4.868449] mtty_probe init device addr: 0x000000007db4bee8 [ 4.868608] rfkill_bluetooth_init [ 4.871951] rfkill_bluetooth_init end [ 4.872048] marlin_sdio_init [ 4.873682] mtty_probe unisoc soc, continue [ 4.873724] sysfs: cannot create duplicate filename '/devices/virt[ 4.873829] CPU: 1 PID: 121 Comm: init Not tainted 4.19.193 #34 [ 4.873842] Hardware name: ROC-RK3566-PC HDMI(Android) (DT) [ 4.873849] Call trace: [ 4.873868] dump_backtrace+0x0/0x178 [ 4.873876] show_stack+0x14/0x20 [ 4.873886] dump_stack+0x94/0xb4 [ 4.873895] sysfs_warn_dup+0x64/0x80 [ 4.873902] sysfs_create_dir_ns+0xdc/0xf8 [ 4.873910] kobject_add_internal+0xa0/0x288 [ 4.873916] kobject_add+0x98/0x100 [ 4.873928] device_add+0xec/0x698 [ 4.873934] device_register+0x1c/0x28 [ 4.873945] tty_register_device_attr+0xe4/0x208 [ 4.873951] tty_register_driver+0x138/0x248 [ 4.873970] mtty_probe+0x144/0x33u0 [sprdbt_tty] [ 4.873978] platform_drv_probe+0x50/0xa8 [ a 4.873984] really_probe+0xl228/0x2a0 [ 4.873991] driver_probe_device+0x58/0x100 [ 4.873996] device_driver_attach+0x6c/0x78 [ 4.874001] __driver_attach+0xb0/0xf0 [ 4.874009] bus_for_each_dev+0x68/0xc8 [ 4.874014] driver_attach+0x20/0x28 [ 4.874019] bus_add_driver+0xf8/0x1f0 [ 4.874025] driver_register+0x60/0x110 [ 4.874031] __platform_driver_register+0x40/0x48 [ 4.874044] uwe5621_bt_tty_init+0x44/0x1000 [sprdbt_tty] [ 4.874052] do_one_initcall+0x48/0x240 [ 4.874061] do_init_module+0x5c/0x1c8 [ 4.874069] load_module+0x18f8/0x1f68 [ 4.874074] __se_sys_finit_module+0xc0/0xd8 [ 4.874079] __arm64_sys_finit_module+0x14/0x20 [ 4.874087] el0_svc_common.constprop.0+0x64/0x178 [ 4.874092] el0_svc_handler+0x28/0x78 [ 4.874097] el0_svc+0x8/0xc [ 4.874179] kobject_add_internal failed for ttyBT0 with -EEXIST/, don't try to register things twith the same name in the same directory. [ 4.874225] list_del corruption, ffffffc079941ea8->next is LIST_POISON1 (dead000000000100) [ 4.874270] ------------[ cut here ]------------
06-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值