linux内核空间open,Linux内核源码分析 - open

在linux下,假设我们想打开文件/dev/tty,我们可以使用系统调用open,比如:

int fd = open("/dev/tty", O_RDWR, 0); 本文将从源码角度看下,在linux内核中,open方法是如何打开文件的。

首先看下入口函数。

// fs/open.c SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) { ... return do_sys_open(AT_FDCWD, filename, flags, mode); } 该方法调用了do_sys_open方法

// fs/open.c long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; int fd = build_open_flags(flags, mode, &op); struct filename *tmp; ... tmp = getname(filename); ... fd = get_unused_fd_flags(flags); if (fd >= 0) { struct file *f = do_filp_open(dfd, tmp, &op); if (IS_ERR(f)) { ... } else { ... fd_install(fd, f); } } ... return fd; } 该方法大致操作为:

调用build_open_flags方法,初始化struct open_flags实例op。

// fs/internal.h struct open_flags { int open_flag; umode_t mode; int acc_mode; int intent; int lookup_flags; }; // fs/open.c static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op) { int lookup_flags = 0; int acc_mode = ACC_MODE(flags); ... if (flags & (O_CREAT | __O_TMPFILE)) op->mode = (mode & S_IALLUGO) | S_IFREG; else op->mode = 0; ... op->open_flag = flags; ... op->acc_mode = acc_mode;

op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;

...

if (flags & O_DIRECTORY)

lookup_flags |= LOOKUP_DIRECTORY;

...

op->lookup_flags = lookup_flags;

return 0;

} 2. 调用getname方法,分配并初始化struct filename实例tmp。

// include/linux/fs.h struct filename { const char name; / pointer to actual string */ const __user char uptr; / original userland pointer */ int refcnt; struct audit_names *aname; const char iname[]; }; // fs/namei.c struct filename * getname_flags(const char __user *filename, int flags, int *empty) { struct filename *result; char *kname; ... result = __getname(); // 分配内存 ... kname = (char *)result->iname; result->name = kname;

len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX);

...

result->refcnt = 1;

...

result->uptr = filename;

...

return result;

}

struct filename * getname(const char __user * filename) { return getname_flags(filename, 0, NULL); } 3. 调用get_unused_fd_flags方法获取一个未被使用的文件描述符fd。

调用do_filp_open方法,继续执行open操作,并将返回值赋值给类型为struct file的实例指针f。

如果do_filp_open成功,则调用fd_install方法,建立从fd到struct file的对应关系。

返回fd给用户。

我们再继续看下do_filp_open方法。

// fs/namei.c struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { struct nameidata nd; int flags = op->lookup_flags; struct file *filp;

set_nameidata(&nd, dfd, pathname);

filp = path_openat(&nd, op, flags | LOOKUP_RCU);

...

return filp;

} 该方法先调用set_nameidata方法,初始化struct nameidata类型实例nd。

// fs/namei.c struct nameidata { struct path path; struct qstr last; struct path root; struct inode inode; / path.dentry.d_inode */ unsigned int flags; unsigned seq, m_seq; int last_type; unsigned depth; int total_link_count; struct saved { struct path link; struct delayed_call done; const char *name; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; struct filename *name; struct nameidata *saved; struct inode *link_inode; unsigned root_seq; int dfd; } __randomize_layout;

static void set_nameidata(struct nameidata *p, int dfd, struct filename *name) { struct nameidata *old = current->nameidata; p->stack = p->internal; p->dfd = dfd; p->name = name; p->total_link_count = old ? old->total_link_count : 0; p->saved = old; current->nameidata = p; } 再调用path_openat方法继续执行open操作。

// fs/namei.c static struct file *path_openat(struct nameidata *nd, const struct open_flags *op, unsigned flags) { struct file *file; int error;

file = alloc_empty_file(op->open_flag, current_cred());

...

if (unlikely(file->f_flags & __O_TMPFILE)) {

...

} else {

const char *s = path_init(nd, flags);

while (!(error = link_path_walk(s, nd)) &&

(error = do_last(nd, file, op)) > 0) {

...

}

...

}

if (likely(!error)) {

if (likely(file->f_mode & FMODE_OPENED))

return file;

...

}

...

return ERR_PTR(error);

} 该方法中,先调用alloc_empty_file方法,分配一个空的struct file实例,再调用path_init、link_path_walk、do_last等方法执行后续的open操作,如果都成功了,返回file给上层。

先看下path_init方法。

// fs/namei.c static const char *path_init(struct nameidata *nd, unsigned flags) { const char *s = nd->name->name; ... nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; nd->depth = 0; ... nd->root.mnt = NULL; nd->path.mnt = NULL; nd->path.dentry = NULL; ... if (*s == '/') { set_root(nd); if (likely(!nd_jump_root(nd))) return s; return ERR_PTR(-ECHILD); } ... } 假设我们要open的路径为/dev/tty,该方法在进行一些初始化赋值之后,会调用set_root方法,设置nd->root字段为fs->root,即根目录

// fs/namei.c static void set_root(struct nameidata *nd) { struct fs_struct *fs = current->fs;

if (nd->flags & LOOKUP_RCU) {

...

do {

...

nd->root = fs->root;

...

} while (read_seqcount_retry(&fs->seq, seq));

} else {

...

}

} 再调用nd_jump_root方法,设置nd->path字段为nd->root,nd->inode字段为nd->root->d_inode。

// fs/namei.c static int nd_jump_root(struct nameidata *nd) { if (nd->flags & LOOKUP_RCU) { struct dentry *d; nd->path = nd->root; d = nd->path.dentry; nd->inode = d->d_inode; ... } else { ... } nd->flags |= LOOKUP_JUMPED; return 0; } 如果上述方法都没有问题,最后返回s给上层,至此,path_init方法结束。

由上可见,path_init方法主要是用来初始化struct nameidata实例中的path、root、inode等字段。

我们再来看下link_path_walk方法。

// fs/namei.c static int link_path_walk(const char *name, struct nameidata *nd) { ...

while (name=='/') name++; ... / At this point we know we have a real path component. */ for(;;) { u64 hash_len; int type; ...

hash_len = hash_name(nd->path.dentry, name);

type = LAST_NORM;

...

nd->last.hash_len = hash_len;

nd->last.name = name;

nd->last_type = type;

name += hashlen_len(hash_len);

if (!*name)

goto OK;

do {

name++;

} while (unlikely(*name == '/'));

if (unlikely(!*name)) {

OK: /* pathname body, done */

if (!nd->depth)

return 0;

...

} else {

/* not the last component */

err = walk_component(nd, WALK_FOLLOW | WALK_MORE);

}

...

}

}

该方法的大致操作为:

跳过开始的‘/’字符。

调用hash_name方法,获取下一个path component的hash和len,并复制给hash_len。

path component就是以‘/’字符分割的路径的各个部分。

将该path component的信息赋值给nd->last字段。

修改name的值,使其指向path的下一个component。

如果下一个component为空,则goto到OK这个label,执行一些操作之后,最后return 0给上层。

如果下一个component不为空,则执行walk_component方法,找到nd->last字段指向的component对应的dentry、inode等信息,并更新nd->path、nd->inode等字段,使其指向新的路径。

以open /dev/tty为例,该方法最终的结果是,更新struct nameidata实例指针nd中的path、inode字段,使其指向路径/dev/,更新nd中的last值,使其为tty。

最后,再来看下do_last方法。

// fs/namei.c static int do_last(struct nameidata *nd, struct file *file, const struct open_flags *op) { ... if (!(open_flag & O_CREAT)) { ... error = lookup_fast(nd, &path, &inode, &seq); if (likely(error > 0)) goto finish_lookup; ... } else { ... } ... finish_lookup: error = step_into(nd, &path, 0, inode, seq); ... error = vfs_open(&nd->path, file); ... return error; }

该方法中,先调用lookup_fast,找路径中的最后一个component,如果成功,就会跳到finish_lookup对应的label,然后执行step_into方法,更新nd中的path、inode等信息,使其指向目标路径。

之后,调用vfs_open方法,继续执行open操作。

最后,返回error给上层,如果成功,error为0。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/429320.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库管理和应用

http://www.dbmng.com/item-11.html转载于:https://www.cnblogs.com/tianciliangen/p/6828062.html

linux命令帮助怎么看,Linux命令帮助

Linux命令帮助教程在Linux man命令详解概述man 命令,它是 Manual pages 的缩写,它是 Linux 系统中在线软件文档的一种普遍的形式,其内容包括计算机程序(包括库和系统调用)、正式的标准和惯例,抽象的概念等。man 工具是显示系统手册…

【转】Yelp是如何实现每天运行数百万个测试的

Yelp每天要运行数百万个测试,确保开发人员提交的代码不会对已有的功能造成破坏。如此巨大规模的测试,他们是怎么做到的呢?以下内容翻译自 Yelp 的技术博客,并已获得翻译授权,查看原文 How Yelp Runs Millions of Tests…

go语言linux下载文件,学习 Go 语言(Golang)PDF

1、简介Go 编程语言是一个使得程序员更加有效率的开源项目。Go 是有表达力、简 洁、清晰和有效率的。它的并行机制使其很容易编写多核和网络应用,而新奇的类型系统允许构建有性的模块化程序。Go 编译到机器码非常快 速,同时具有便利的垃圾回收和强大的运…

数据同步这点事

最近一段时间,在做数据ETL相关的事,结合实践以及自己的思考,记录下来,以做参考。 概述 一般来说,数据团队自己是很少生产数据的,一般都是对业务线的数据进行分析加工,从而让数据产生价值。一方面…

linux下的awk程序在哪里编写,如何编写awk命令和脚本

awk命令是处理或分析文本文件(尤其是按行和列组织的数据文件)的强大方法.您可以从命令行运行简单的awk命令. 应该将更复杂的任务作为awk程序(所谓的awk脚本)写入文件.awk命令的基本格式如下:awkpattern {action}输入文件>输出文件这意味着: 占用输入文件的每一行&#xff1b…

TreeSet类的排序

TreeSet支持两种排序方法:自然排序和定制排序。TreeSet默认采用自然排序。 1、自然排序 TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系,然后将集合元素按升序排列,这种方式就是自然排序。(比较的前提&…

linux边看系统信息,Linux查看系统信息大全[备忘]

系统# uname -a # 查看内核/操作系统/CPU信息# head -n 1 /etc/issue # 查看操作系统版本# cat /proc/cpuinfo # 查看CPU信息# hostname # 查看计算机名# lspci -tv # 列出所有PCI设备# lsusb -tv # 列出所有USB设备# lsmod # 列出加载的内核模块# env # 查看环境变量资源# fre…

关于存储的基本知识:

flash:Flash,是内存(Memory)的一种,但兼有RAM和ROM, 分为NOR Flash 和 NADN Flash两种不同的flash,分别用在不同的场合,不同于SRAM(EEPROM),FLASH的操作是靠一系列指令完成,以扇区sector进行整体…

移植linux内核-映像文件,移植Linux内核-映像文件

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://tigerwang202.blogbus.com/logs/43927976.html首先从Blackfin uClinux网站下载最新版uClinux内核。http://blackfin.uclinux.org/gf/project/uclinux-dist/frs/上述地址除了提供内核源代码外…

linux意想不到题4

101 linux系统调优 答:(1)硬件:大内存、固态硬盘或者RAID阵列、CPU核数、服务器产商 (2)某个服务的编译安装,启用或禁用的一些选项 (3)服务的配置文件,参数的…

linux中用截取一些信息,Linux如何使用cut命令截取文件信息

在Linux众多命令中,cut命令可用来截取文件信息,截取Linux字符串,下面将针对cut命令的用法做个详细介绍,感兴趣的朋友可以来学习下。cut命令有截取的意思,可从linux文件或者标准输入中读取内容并纵向截取所需信息列的一…

l启动进程 linux,《日子》. linux 查看进程启动路径

在linux下查看进程大家都会想到用 ps -ef|grep XXX可是看到的不是全路径,怎么看全路径呢?每个进程启动之后在 /proc下面有一个于pid对应的路径例如:ps -ef|grep python显示:oracle 4431 4366 0 18:56 pts/2 00:00:00 python Serve…

django_4:数据库1——django操作数据库

创建数据库记录(插入) 使用python3 manage.py shell(python3亲测好使) ipython3 manage.py shell(亲测不好使) 方式一、 [rootcentos7 csvt03]# python3 manage.py shell Python 3.5.2 (default, May 9 2017, 23:04:15) Type copyright, credits or li…

linux uuid错误,Linux 错误:fatal error: uuid/uuid.h: No such file or directory

重新configure 后,出现别的错误:fatal error: uuid/uuid.h: No such file or directory这是因为没有uuid库和头文件,需要安装e2fsprogs,试过yum命令安装,问题没解决,需要从源码编译wget http://downloads.sourceforge.…

uva 11054

据说这叫扫描法,,,,感觉就是脑洞啊,,,,,反正从最左端开始,如果有酒就往后运,需要酒就运负的,就是相当于后面有酒了就运回来。。。 #in…

fastdfs集群搭建2

五.在各个存储节点安装nginx 1.上传 fastdfs-nginx-module_v1.16.tar.gz 到/usr/local/src,解压 2.修改 fastdfs-nginx-module 的 config 配置文件 将CORE_INCS"$CORE_INCS /usr/local/include/fastdfs /usr/local/include/fastcommon/" 修改为:CORE_INC…

201521123057 《Java程序设计》第12周学习总结

1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容。 2. 书面作业 1.字符流与文本文件:使用 PrintWriter(写),BufferedReader(读) 将Student对象(属性:int id, String name,int age,doub…

linux 管道文件上机总结,[转载]LINUX 管道 fifo 等总结

Linux进程通信:命名管道FIFO小结Linux下进程之间通信可以用命名管道FIFO完成。命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在。在程序中,我们可以使用两个不同的函数调用来建立管道…

tomcat 下载

点project 关闭防火墙才可以让别人访问自己 转载于:https://www.cnblogs.com/feathe/p/6853491.html