zl程序教程

您现在的位置是:首页 >  系统

当前栏目

第十八章 linux-应用层open->软中断指令->底层驱动xxx_open

Linux驱动 指令 open 底层 中断 xxx 应用层
2023-09-14 09:10:01 时间

第十八章 linux-应用层open->软中断指令->底层驱动xxx_open



前言

驱动程序中实现file_operations中的open函数,在应用层中使用open来调用到内核中实现的file_operations中的open函数,那么如何实现的呢?


inode

内核中字符设备相应文件实现file_operations后生成ko文件,每个文件有一个特定的inode号,每个文件会对应一个inode结构体

struct inode {
    umode_t i_mode;  // 打开方式
    kuid_t i_uid;  // 用户id
    kgid_t i_gid;  // 组id
    unsigned int i_flags;  // 权限标志位,阻塞,非阻塞... 标志位信息


    const struct inode_operations*i_op;  // inode 操作方法集
    struct super_block*i_sb;
    struct address_space*i_mapping;


    unsigned longi_ino;  // inode 号码
    union {
         const unsigned int i_nlink;
         unsigned int __i_nlink;   // 链接数
    };
    dev_ti_rdev;   // 设备号
    struct timespeci_atime;  // 访问时间
    struct timespeci_mtime;  // 修改时间
    struct timespeci_ctime;   // 创建时间

    // 存储信息
    unsigned short          i_bytes;
    unsigned int i_blkbits;
    blkcnt_t i_blocks;


    const struct file_operations*i_fop; /* 文件操作方法集合 */
    struct list_headi_devices;  // 内核循环双链表节点
    union {
         struct pipe_inode_info*i_pipe;
         struct block_device*i_bdev;
         struct cdev *i_cdev;   // 字符设备对象指针
       };
...
};

inode结构体包含很多信息,如文件的权限信息,文件的时间信息等等。在驱动文件中,我们主要关注标红的信息,inode号,设备号,文件操作集合。使用mknod创建设备节点时,会将inode号和设备号关联起来。若此时内核中已经注册了该设备号的对象,此时会将设备对象的操作方法集合赋值到此处。

应用层open->底层驱动xxx_open

文件操作集合i_fop指向了def_chr_fops,这个结构中的open函数指向了chrdev_open。chrdev_open完成的主要工作是:首先根据设备号找到添加在内核中代表字符设备的cdev。找到对应的cedv对象后,用cedv关联的操作方法集和赋值给新建的file结构体中的文件操作集合。并调用操作集合中的open函数,完成真正的打开操作。

一个驱动可能会被多应用打开,那多次打开时是生成多个inode号吗?答案是否定的。inode号是唯一的,那如何表示多次打开一个驱动文件呢?

系统中每个打开的文件在内核空间都有一个对应的file结构体。

struct file {
 struct path f_path;  // 路径名称
 struct inode *f_inode; /*inode 指针*/
 const struct file_operations *f_op;  // 文件操作集合
 unsigned int  f_flags;  // 标志位信息
 fmode_t f_mode;  // 操作方式
 loff_t f_pos;
 struct fown_struct f_owner;  // 异步通知 ,需要用到此结构体
 void *private_data;
 ...
 };

请添加图片描述
应用层open->底层驱动xxx_open

应用层open->软中断指令->底层驱动xxx_open

请添加图片描述

  • 应用层中使用open时,触发软中断(int $0x80)
int open(const char *pathname, int flags, mode_t mode);
  • 触发软中断后更根据参数的数量触发不同的函数,利用0x80中断劫持system_call->sys_call_table(系统调用表)
static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
{
	long ret;

	__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
			"c" (arg2), "d" (arg3));

	return ret;
}

sys_call_table(系统调用表)

#include <linux/syscalls.h>
#include <linux/signal.h>
#include <linux/unistd.h>

#include <asm/syscall.h>

#undef __SYSCALL
#define __SYSCALL(nr, call) [nr] = (call),

void *sys_call_table[__NR_syscalls] = {
#include <asm/unistd.h>
};

/*
 * arch/sh/kernel/syscalls_64.S
 *
 * Copyright (C) 2000, 2001  Paolo Alberelli
 * Copyright (C) 2004 - 2007  Paul Mundt
 * Copyright (C) 2003, 2004 Richard Curnow
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#include <linux/sys.h>

	.section .data, "aw"
	.balign 32

/*
 * System calls jump table
 */
	.globl  sys_call_table[]
sys_call_table:
	.long sys_restart_syscall	/* 0  -  old "setup()" system call  */
	.long sys_exit
	.long sys_fork
	.long sys_read
	.long sys_write
	.long sys_open			/* 5 */
	.long sys_close
	.long sys_waitpid
	.long sys_creat
	.long sys_link
	.long sys_unlink		/* 10 */
	.long sys_execve
	.long sys_chdir
	.long sys_time
	.long sys_mknod
	.long sys_chmod			/* 15 */
	.long sys_lchown16
	.long sys_ni_syscall	/* old break syscall holder */
	.long sys_stat
	.long sys_lseek
	.long sys_getpid		/* 20 */
	.long sys_mount
	.long sys_oldumount
	.long sys_setuid16
	.long sys_getuid16
	.long sys_stime			/* 25 */
	.long sh64_ptrace
	.long sys_alarm
	.long sys_fstat
	.long sys_pause
	.long sys_utime			/* 30 */
	.long sys_ni_syscall	/* old stty syscall holder */
	.long sys_ni_syscall	/* old gtty syscall holder */
	.long sys_access
	.long sys_nice
	.long sys_ni_syscall		/* 35 */ /* old ftime syscall holder */
	.long sys_sync
	.long sys_kill
	.long sys_rename
	.long sys_mkdir
	.long sys_rmdir			/* 40 */
	.long sys_dup
	.long sys_pipe
	.long sys_times
	.long sys_ni_syscall	/* old prof syscall holder */
	.long sys_brk			/* 45 */
	.long sys_setgid16
	.long sys_getgid16
	.long sys_signal
	.long sys_geteuid16
	.long sys_getegid16		/* 50 */
	.long sys_acct
	.long sys_umount		/* recycled never used phys( */
	.long sys_ni_syscall	/* old lock syscall holder */
	.long sys_ioctl
	.long sys_fcntl			/* 55 */
	.long sys_ni_syscall	/* old mpx syscall holder */
	.long sys_setpgid
	.long sys_ni_syscall	/* old ulimit syscall holder */
	.long sys_ni_syscall	/* sys_olduname */
	.long sys_umask			/* 60 */
	.long sys_chroot
	.long sys_ustat
	.long sys_dup2
	.long sys_getppid
	.long sys_getpgrp		/* 65 */
	.long sys_setsid
	.long sys_sigaction
	.long sys_sgetmask
	.long sys_ssetmask
	.long sys_setreuid16		/* 70 */
	.long sys_setregid16
	.long sys_sigsuspend
	.long sys_sigpending
	.long sys_sethostname
	.long sys_setrlimit		/* 75 */
	.long sys_old_getrlimit
	.long sys_getrusage
	.long sys_gettimeofday
	.long sys_settimeofday
	.long sys_getgroups16		/* 80 */
	.long sys_setgroups16
	.long sys_ni_syscall	/* sys_oldselect */
	.long sys_symlink
	.long sys_lstat
	.long sys_readlink		/* 85 */
	.long sys_uselib
	.long sys_swapon
	.long sys_reboot
	.long sys_old_readdir
	.long old_mmap			/* 90 */
	.long sys_munmap
	.long sys_truncate
	.long sys_ftruncate
	.long sys_fchmod
	.long sys_fchown16		/* 95 */
	.long sys_getpriority
	.long sys_setpriority
	.long sys_ni_syscall	/* old profil syscall holder */
	.long sys_statfs
	.long sys_fstatfs		/* 100 */
	.long sys_ni_syscall	/* ioperm */
	.long sys_socketcall	/* Obsolete implementation of socket syscall */
	.long sys_syslog
	.long sys_setitimer
	.long sys_getitimer		/* 105 */
	.long sys_newstat
	.long sys_newlstat
	.long sys_newfstat
	.long sys_uname
	.long sys_ni_syscall		/* 110 */ /* iopl */
	.long sys_vhangup
	.long sys_ni_syscall	/* idle */
	.long sys_ni_syscall	/* vm86old */
	.long sys_wait4
	.long sys_swapoff		/* 115 */
	.long sys_sysinfo
	.long sys_ipc		/* Obsolete ipc syscall implementation */
	.long sys_fsync
	.long sys_sigreturn
	.long sys_clone			/* 120 */
	.long sys_setdomainname
	.long sys_newuname
	.long sys_cacheflush	/* x86: sys_modify_ldt */
	.long sys_adjtimex
	.long sys_mprotect		/* 125 */
	.long sys_sigprocmask
	.long sys_ni_syscall		/* old "create_module" */
	.long sys_init_module
	.long sys_delete_module
	.long sys_ni_syscall		/* 130: old "get_kernel_syms" */
	.long sys_quotactl
	.long sys_getpgid
	.long sys_fchdir
	.long sys_bdflush
	.long sys_sysfs			/* 135 */
	.long sys_personality
	.long sys_ni_syscall	/* for afs_syscall */
	.long sys_setfsuid16
	.long sys_setfsgid16
	.long sys_llseek		/* 140 */
	.long sys_getdents
	.long sys_select
	.long sys_flock
	.long sys_msync
	.long sys_readv			/* 145 */
	.long sys_writev
	.long sys_getsid
	.long sys_fdatasync
	.long sys_sysctl
	.long sys_mlock			/* 150 */
	.long sys_munlock
	.long sys_mlockall
	.long sys_munlockall
	.long sys_sched_setparam
	.long sys_sched_getparam	/* 155 */
	.long sys_sched_setscheduler
	.long sys_sched_getscheduler
	.long sys_sched_yield
	.long sys_sched_get_priority_max
	.long sys_sched_get_priority_min  /* 160 */
	.long sys_sched_rr_get_interval
	.long sys_nanosleep
	.long sys_mremap
	.long sys_setresuid16
	.long sys_getresuid16		/* 165 */
	.long sys_ni_syscall	/* vm86 */
	.long sys_ni_syscall	/* old "query_module" */
	.long sys_poll
	.long sys_ni_syscall	/* was nfsservctl */
	.long sys_setresgid16		/* 170 */
	.long sys_getresgid16
	.long sys_prctl
	.long sys_rt_sigreturn
	.long sys_rt_sigaction
	.long sys_rt_sigprocmask	/* 175 */
	.long sys_rt_sigpending
	.long sys_rt_sigtimedwait
	.long sys_rt_sigqueueinfo
	.long sys_rt_sigsuspend
	.long sys_pread64		/* 180 */
	.long sys_pwrite64
	.long sys_chown16
	.long sys_getcwd
	.long sys_capget
	.long sys_capset		/* 185 */
	.long sys_sigaltstack
	.long sys_sendfile
	.long sys_ni_syscall	/* getpmsg */
	.long sys_ni_syscall	/* putpmsg */
	.long sys_vfork			/* 190 */
	.long sys_getrlimit
	.long sys_mmap2
	.long sys_truncate64
	.long sys_ftruncate64
	.long sys_stat64		/* 195 */
	.long sys_lstat64
	.long sys_fstat64
	.long sys_lchown
	.long sys_getuid
	.long sys_getgid		/* 200 */
	.long sys_geteuid
	.long sys_getegid
	.long sys_setreuid
	.long sys_setregid
	.long sys_getgroups		/* 205 */
	.long sys_setgroups
	.long sys_fchown
	.long sys_setresuid
	.long sys_getresuid
	.long sys_setresgid		/* 210 */
	.long sys_getresgid
	.long sys_chown
	.long sys_setuid
	.long sys_setgid
	.long sys_setfsuid		/* 215 */
	.long sys_setfsgid
	.long sys_pivot_root
	.long sys_mincore
	.long sys_madvise
	/* Broken-out socket family (maintain backwards compatibility in syscall
	   numbering with 2.4) */
	.long sys_socket		/* 220 */
	.long sys_bind
	.long sys_connect
	.long sys_listen
	.long sys_accept
	.long sys_getsockname		/* 225 */
	.long sys_getpeername
	.long sys_socketpair
	.long sys_send
	.long sys_sendto
	.long sys_recv			/* 230*/
	.long sys_recvfrom
	.long sys_shutdown
	.long sys_setsockopt
	.long sys_getsockopt
	.long sys_sendmsg		/* 235 */
	.long sys_recvmsg
	/* Broken-out IPC family (maintain backwards compatibility in syscall
	   numbering with 2.4) */
	.long sys_semop
	.long sys_semget
	.long sys_semctl
	.long sys_msgsnd		/* 240 */
	.long sys_msgrcv
	.long sys_msgget
	.long sys_msgctl
	.long sys_shmat
	.long sys_shmdt			/* 245 */
	.long sys_shmget
	.long sys_shmctl
	/* Rest of syscalls listed in 2.4 i386 unistd.h */
	.long sys_getdents64
	.long sys_fcntl64
	.long sys_ni_syscall		/* 250 reserved for TUX */
	.long sys_ni_syscall		/* Reserved for Security */
	.long sys_gettid
	.long sys_readahead
	.long sys_setxattr
	.long sys_lsetxattr		/* 255 */
	.long sys_fsetxattr
	.long sys_getxattr
	.long sys_lgetxattr
	.long sys_fgetxattr
	.long sys_listxattr		/* 260 */
	.long sys_llistxattr
	.long sys_flistxattr
	.long sys_removexattr
	.long sys_lremovexattr
	.long sys_fremovexattr  	/* 265 */
	.long sys_tkill
	.long sys_sendfile64
	.long sys_futex
	.long sys_sched_setaffinity
	.long sys_sched_getaffinity	/* 270 */
	.long sys_ni_syscall		/* reserved for set_thread_area */
	.long sys_ni_syscall		/* reserved for get_thread_area */
	.long sys_io_setup
	.long sys_io_destroy
	.long sys_io_getevents		/* 275 */
	.long sys_io_submit
	.long sys_io_cancel
	.long sys_fadvise64
	.long sys_ni_syscall
	.long sys_exit_group		/* 280 */
	/* Rest of new 2.6 syscalls */
	.long sys_lookup_dcookie
	.long sys_epoll_create
	.long sys_epoll_ctl
	.long sys_epoll_wait
 	.long sys_remap_file_pages	/* 285 */
 	.long sys_set_tid_address
 	.long sys_timer_create
 	.long sys_timer_settime
 	.long sys_timer_gettime
 	.long sys_timer_getoverrun	/* 290 */
 	.long sys_timer_delete
 	.long sys_clock_settime
 	.long sys_clock_gettime
 	.long sys_clock_getres
 	.long sys_clock_nanosleep	/* 295 */
	.long sys_statfs64
	.long sys_fstatfs64
	.long sys_tgkill
	.long sys_utimes
 	.long sys_fadvise64_64		/* 300 */
	.long sys_ni_syscall	/* Reserved for vserver */
	.long sys_ni_syscall	/* Reserved for mbind */
	.long sys_ni_syscall	/* get_mempolicy */
	.long sys_ni_syscall	/* set_mempolicy */
	.long sys_mq_open		/* 305 */
	.long sys_mq_unlink
	.long sys_mq_timedsend
	.long sys_mq_timedreceive
	.long sys_mq_notify
	.long sys_mq_getsetattr		/* 310 */
	.long sys_ni_syscall	/* Reserved for kexec */
	.long sys_waitid
	.long sys_add_key
	.long sys_request_key
	.long sys_keyctl		/* 315 */
	.long sys_ioprio_set
	.long sys_ioprio_get
	.long sys_inotify_init
	.long sys_inotify_add_watch
	.long sys_inotify_rm_watch	/* 320 */
	.long sys_ni_syscall
	.long sys_migrate_pages
	.long sys_openat
	.long sys_mkdirat
	.long sys_mknodat		/* 325 */
	.long sys_fchownat
	.long sys_futimesat
	.long sys_fstatat64
	.long sys_unlinkat
	.long sys_renameat		/* 330 */
	.long sys_linkat
	.long sys_symlinkat
	.long sys_readlinkat
	.long sys_fchmodat
	.long sys_faccessat		/* 335 */
	.long sys_pselect6
	.long sys_ppoll
	.long sys_unshare
	.long sys_set_robust_list
	.long sys_get_robust_list	/* 340 */
	.long sys_splice
	.long sys_sync_file_range
	.long sys_tee
	.long sys_vmsplice
	.long sys_move_pages		/* 345 */
	.long sys_getcpu
	.long sys_epoll_pwait
	.long sys_utimensat
	.long sys_signalfd
	.long sys_timerfd_create	/* 350 */
	.long sys_eventfd
	.long sys_fallocate
	.long sys_timerfd_settime
	.long sys_timerfd_gettime
	.long sys_signalfd4		/* 355 */
	.long sys_eventfd2
	.long sys_epoll_create1
	.long sys_dup3
	.long sys_pipe2
	.long sys_inotify_init1		/* 360 */
	.long sys_preadv
	.long sys_pwritev
	.long sys_rt_tgsigqueueinfo
	.long sys_perf_event_open
	.long sys_recvmmsg		/* 365 */
	.long sys_accept4
	.long sys_fanotify_init
	.long sys_fanotify_mark
	.long sys_prlimit64
	.long sys_name_to_handle_at	/* 370 */
	.long sys_open_by_handle_at
	.long sys_clock_adjtime
	.long sys_syncfs
	.long sys_sendmmsg
	.long sys_setns			/* 375 */
	.long sys_process_vm_readv
	.long sys_process_vm_writev
	.long sys_kcmp
	.long sys_finit_module

  • 然后执行汇编指令跳转到内核态,最终调用到do_sys_open函数

  • 在do_sys_open函数中,经过一系列的调用最终会打开对应驱动,调用到对应驱动中的xxx_open函数

do_sys_open

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
	/*把用户空间的存在内存的文件名字符串复制到内核空间*/
	char *tmp = getname(filename);
	int fd = PTR_ERR(tmp);
 
 
	if (!IS_ERR(tmp)) {
		/*寻找一个暂时没用的文件描述符,或者没有的话返回错误*/
		fd = get_unused_fd();
		if (fd >= 0) {
			/*如果找到的话,就执行打开操作*/
			struct file *f = do_filp_open(dfd, tmp, flags, mode);
			if (IS_ERR(f)) {
				/*打开失败,就释放文件描述符*/
				put_unused_fd(fd);
				fd = PTR_ERR(f);
			} else {
				fsnotify_open(f->f_path.dentry);
				fd_install(fd, f);
			}
		}
		putname(tmp);
	}
	return fd;
}

do_filp_open

/*
 * for the internal routines (ie open_namei()/follow_link() etc). 00 is
 * used by symlinks.
 */
static struct file *do_filp_open(int dfd, const char *filename, int flags,
				 int mode)
{
	int namei_flags, error;
	struct nameidata nd;
	/*内外标记不一样*/
	namei_flags = flags;
	if ((namei_flags+1) & O_ACCMODE)
		namei_flags++;
	/*顺着文件名打开操作*/
	error = open_namei(dfd, filename, namei_flags, mode, &nd);
	if (!error)/*根据得到的文件数据,填充file结构体*/
		return nameidata_to_filp(&nd, flags);
 
	return ERR_PTR(error);
}

open_namei

int open_namei(int dfd, const char *pathname, int flag,
		int mode, struct nameidata *nd)
{
	int acc_mode, error;
	struct path path;
	struct dentry *dir;
	int count = 0;
	/*从flags得到打开模式*/
	acc_mode = ACC_MODE(flag);
 
 
	/* O_TRUNC表示我们需要检查写的权限 */
	if (flag & O_TRUNC)
		acc_mode |= MAY_WRITE;
 
 
	/*O_APPEND需要MAY_APPEND权限  */
	if (flag & O_APPEND)
		acc_mode |= MAY_APPEND;
 
 
	/*如果是不是创建文件的话,就调用path_lookup_open函数获得文件的dentry和vfsmount填充nameidata结构体,找到后就直接返回*/
	if (!(flag & O_CREAT)) {
		error = path_lookup_open(dfd, pathname, lookup_flags(flag),
					 nd, flag);
		/*获得后就直接返回函数*/
		if (error)
			return error;
		goto ok;
	}
 
 
	/*如果是要创建文件,我们需要知道父目录,所以加上LOOKUP_PARENT的flag*/
	error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
	if (error)
		return error;
 
 
	/*已经拥有了父目录,我们检查下返回结果,如果最后找到的是目录就返回错误,我们只创建文件不创建目录 */
	error = -EISDIR;
	if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
		goto exit;
	/**/
	dir = nd->dentry;
	nd->flags &= ~LOOKUP_PARENT;
	mutex_lock(&dir->d_inode->i_mutex);
	/*填充path结构体*/
	path.dentry = lookup_hash(nd);
	path.mnt = nd->mnt;
 
 
do_last:
	/*如果获得的参数有问题就返回错误*/
	error = PTR_ERR(path.dentry);
	if (IS_ERR(path.dentry)) {
		mutex_unlock(&dir->d_inode->i_mutex);
		goto exit;
	}
 
 
	if (IS_ERR(nd->intent.open.file)) {
		mutex_unlock(&dir->d_inode->i_mutex);
		error = PTR_ERR(nd->intent.open.file);
		goto exit_dput;
	}
 
 
	/* 错误的dentry,直接创建文件 */
	if (!path.dentry->d_inode) {
		error = open_namei_create(nd, &path, flag, mode);
		if (error)
			goto exit;
		return 0;
	}
 
 
	/*
	 * 说明文件已经存在
	 */
	mutex_unlock(&dir->d_inode->i_mutex);
	audit_inode(pathname, path.dentry->d_inode);
	/*O_EXCL标志代表如果文件存在就退出*/
	error = -EEXIST;
	if (flag & O_EXCL)
		goto exit_dput;
	/*找到真正的挂载点*/
	if (__follow_mount(&path)) {
		error = -ELOOP;
		if (flag & O_NOFOLLOW)
			goto exit_dput;
	}
 
 
	error = -ENOENT;
	/*说明创建失败*/
	if (!path.dentry->d_inode)
		goto exit_dput;
	/*处理连接*/
	if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
		goto do_link;
	/*path结构体转成nameidata结构体*/
	path_to_nameidata(&path, nd);
	error = -EISDIR;
	/*再次检查*/
	if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
		goto exit;
ok:
	/*写之前的一些检查和准备工作*/
	error = may_open(nd, acc_mode, flag);
	if (error)
		goto exit;
	return 0;
 
 
exit_dput:
	/*退出前释放path*/
	dput_path(&path, nd);
exit:
	if (!IS_ERR(nd->intent.open.file))
		release_open_intent(nd);
	path_release(nd);
	return error;
 
 
do_link:
	/*如果flag有O_NOFOLLOW,说明禁止链接,直接返回错误*/
	error = -ELOOP;
	if (flag & O_NOFOLLOW)
		goto exit_dput;
	/*首先找到父目录*/
	nd->flags |= LOOKUP_PARENT;
	/*安全操作,暂时不管*/
	error = security_inode_follow_link(path.dentry, nd);
	if (error)
		goto exit_dput;
	/*搜寻,并创建*/
	error = __do_follow_link(&path, nd);
	if (error) {
		release_open_intent(nd);
		return error;
	}
	nd->flags &= ~LOOKUP_PARENT;
	/*检验返回结果*/
	if (nd->last_type == LAST_BIND)
		goto ok;
	error = -EISDIR;
	if (nd->last_type != LAST_NORM)
		goto exit;
	if (nd->last.name[nd->last.len]) {
		__putname(nd->last.name);
		goto exit;
	}
	error = -ELOOP;
	/*超出循环最大次数*/
	if (count++==32) {
		__putname(nd->last.name);
		goto exit;
	}
	/*填充返回结果*/
	dir = nd->dentry;
	mutex_lock(&dir->d_inode->i_mutex);
	path.dentry = lookup_hash(nd);
	path.mnt = nd->mnt;
	__putname(nd->last.name);
	goto do_last;
}

open_namei_create

static int open_namei_create(struct nameidata *nd, struct path *path,
				int flag, int mode)
{
	int error;
	struct dentry *dir = nd->dentry;
	/*POSIX权限标准检查*/
	if (!IS_POSIXACL(dir->d_inode))
		mode &= ~current->fs->umask;
	/*vfs层调用文件创造*/
	error = vfs_create(dir->d_inode, path->dentry, mode, nd);
	mutex_unlock(&dir->d_inode->i_mutex);
	dput(nd->dentry);
	/*创建后放到nameidata里*/
	nd->dentry = path->dentry;
	if (error)
		return error;
	/*权限检查,不检查写权限和truncate权限*/
	return may_open(nd, 0, flag & ~O_TRUNC);
}

vfs_create

int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	/*打开前的权限检查*/
	int error = may_create(dir, dentry, nd);
 
 
	if (error)
		return error;
	/*inode的inode_operation函数表的create函数*/
	if (!dir->i_op || !dir->i_op->create)
		return -EACCES;	/* shouldn't it be ENOSYS? */
	mode &= S_IALLUGO;
	mode |= S_IFREG;
	/*安全检查操作*/
	error = security_inode_create(dir, dentry, mode);
	if (error)
		return error;
	DQUOT_INIT(dir);
	/*创建*/
	error = dir->i_op->create(dir, dentry, mode, nd);
	if (!error)
		fsnotify_create(dir, dentry);
	return error;
}