linux内核中的dup系统调用
内核版本:2.6.14
嵌入式开发交流群:289195589,欢迎加入!
dup系统调用的服务例程为sys_dup函数,定义在fs/fcntl.c中。sys_dup()的代码也许称得上是最简单的之一了,但是就是这么一个简单的系统调用,却成就了linux系统最著名的一个特性:输入/输出重定向。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; }
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; }
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; }
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个文件描述符(标准输入、标准输出和标准错误) ... }
我们通过一个简单的来讲解重定向。
当我们在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,以上命令行将按如下序列执行: