html5做手机网站,网页设计如何制作背景,品牌网上开店加盟网,红铃铛网站建设在linux下#xff0c;假设我们想打开文件/dev/tty#xff0c;我们可以使用系统调用open#xff0c;比如#xff1a;int fd open(/dev/tty, O_RDWR, 0); 本文将从源码角度看下#xff0c;在linux内核中#xff0c;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-rootnd-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。