您的位置:首页 > 运维架构 > Linux

Linux文件系统(七)---系统调用之open操作(一)

2015-11-17 09:49 676 查看
(内核2.4.37)

一、

当我们打开一个文件的时候,需要获得文件的文件描述符(前面已经说过其实就是文件数组下标),一般是通过函数open来完成,这个系统调用在<unistd.h>头文件中声明定义,我们看一下源码:

[cpp] view
plaincopyprint?

530 static inline long open(const char * name, int mode, int flags)

531 {

532 return sys_open(name, mode, flags);

533 }

Ps:对于这些参数一般我们都很熟悉,经常使用,这里顺便提出记忆一下:

mode:参数可选:

[cpp] view
plaincopyprint?

32 #define S_IRWXU 00700 文件所有者可读可写可执行

33 #define S_IRUSR 00400 文件所有者可读

34 #define S_IWUSR 00200 文件所有者可写

35 #define S_IXUSR 00100 文件所有者可执行

36

37 #define S_IRWXG 00070 文件用户组可写可读可执行

38 #define S_IRGRP 00040 文件用户组可读

39 #define S_IWGRP 00020 文件用户组可写

40 #define S_IXGRP 00010 文件用户组可执行

41

42 #define S_IRWXO 00007 其他用户可写可读可执行

43 #define S_IROTH 00004 其他用户可读

44 #define S_IWOTH 00002 其他用户可写

45 #define S_IXOTH 00001 其他用户可执行

flags:在fcntl.h中定义

[cpp] view
plaincopyprint?

7 #define O_RDONLY 00

8 #define O_WRONLY 01

9 #define O_RDWR 02

10 #define O_CREAT 0100 /* not fcntl */

11 #define O_EXCL 0200 /* not fcntl */

12 #define O_NOCTTY 0400 /* not fcntl */

13 #define O_TRUNC 01000 /* not fcntl */

14 #define O_APPEND 02000

15 #define O_NONBLOCK 04000

16 #define O_NDELAY O_NONBLOCK

17 #define O_SYNC 010000

18 #define FASYNC 020000 /* fcntl, for BSD compatibility */

19 #define O_DIRECT 040000 /* direct disk access hint */

20 #define O_LARGEFILE 0100000

21 #define O_DIRECTORY 0200000 /* must be a directory */

22 #define O_NOFOLLOW 0400000 /* don't follow links */

O_RDONLY 以只读方式打开文件

O_WRONLY 以只写方式打开文件

O_RDWR 以读和写的方式打开文件

上面三个只能选择一个,下面的可以合理的任意组合:

O_CREAT 打开文件,如果文件不存在则建立文件

O_EXCL 如果已经置O_CREAT且文件存在,则强制open()失败

O_TRUNC 将文件的长度截为0

O_APPEND 强制write()从文件尾开始

对于终端文件,上面四个是无效,提供了两个新的标志:

O_NOCTTY 停止这个终端作为控制终端

O_NONBLOCK 使open()、read()、write()不被阻塞。

我们可以看到,里面实际调用的是sys_open这个系统调用,其实想想也很正常,对于我的一个系统而言,可以存在很多组不同的文件系统,对于不同的文件系统,打开文件的方式肯定是不一样的,所有内核需要根据具体的文件系统的类型去调用不同的函数进行执行。现在看看sys_open函数(fs/open.c中):

[cpp] view
plaincopyprint?

800 asmlinkage long sys_open(const char * filename, int flags, int mode)

801 {

802 char * tmp;

803 int fd, error;

804

805 #if BITS_PER_LONG != 32

806 flags |= O_LARGEFILE;

807 #endif

808 tmp = getname(filename); /* 1 */

809 fd = PTR_ERR(tmp);

810 if (!IS_ERR(tmp)) {

811 fd = get_unused_fd(); /* 2 */

812 if (fd >= 0) {

813 struct file *f = filp_open(tmp, flags, mode); /* 3 */

814 error = PTR_ERR(f);

815 if (IS_ERR(f))

816 goto out_error;

817 fd_install(fd, f); /* 4 */

818 }

819 out:

820 putname(tmp);

821 }

822 return fd;

823

824 out_error:

825 put_unused_fd(fd);

826 fd = error;

827 goto out;

828 }

主要看上面注释表示的四大步骤/* 1 */ /* 2 */ /* 3 */ /* 4 */

/* 1 */:这步是一个辅助步骤,将filename从用户态拷贝到内核态变量中。基本步骤涉及一下几个函数:

[cpp] view
plaincopyprint?

125 char * getname(const char * filename)

126 {

127 char *tmp, *result;

128

129 result = ERR_PTR(-ENOMEM);

130 tmp = __getname(); /* 这玩意吧其实是分配内核中空间用户装name */

131 if (tmp) {

132 int retval = do_getname(filename, tmp); /* 其实就是将filename拷贝到tmp中 */

133

134 result = tmp;

135 if (retval < 0) {

136 putname(tmp);

137 result = ERR_PTR(retval);

138 }

139 }

140 return result;

141 }

看一下__getname()函数:

[cpp] view
plaincopyprint?

1099 #define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL)

就是在内核的slab空间中分配能够容纳name的空间~~~

看一下do_getname()函数:

[cpp] view
plaincopyprint?

104 static inline int do_getname(const char *filename, char *page)

105 {

106 int retval;

107 unsigned long len = PATH_MAX;

108

109 if ((unsigned long) filename >= TASK_SIZE) {

110 if (!segment_eq(get_fs(), KERNEL_DS))

111 return -EFAULT;

112 } else if (TASK_SIZE - (unsigned long) filename < PATH_MAX)

113 len = TASK_SIZE - (unsigned long) filename;

114

115 retval = strncpy_from_user((char *)page, filename, len);/* 核心的一个步骤,其实就是将filename拷贝到刚刚在内核中分配的空间中 */

116 if (retval > 0) {

117 if (retval < len)

118 return 0;

119 return -ENAMETOOLONG;

120 } else if (!retval)

121 retval = -ENOENT;

122 return retval;

123 }

/* 2 */:这一步是需要找到一个没有使用的文件描述符fd

看一下这个函数get_unused_fd:看这个链接:get_unused_fd

/* 3 */:再回到上面看/* 3 */步骤,到现在为止,我们已经找到了一个可用的文件描述符fd了,然后我们要做的就是打开指定文件,然后将这个fd和打开的文件关联即可,/* 3 */步骤就是打开我们指定的文件!

下面会涉及到名字结构nameidata,所以先看看这个结构体:

[cpp] view
plaincopyprint?

700 struct nameidata {

701 struct dentry *dentry; /* 当前目录项对象 */

702 struct vfsmount *mnt; /* 已安装的文件系统挂载点 */

703 struct qstr last; /* 路径名称最后一部分 */

704 unsigned int flags; /* 查询标识 */

705 int last_type; /* 路径名称最后一部分类型 */

706 };

看这个函数filp_open:

[cpp] view
plaincopyprint?

644 /*

645 * Note that while the flag value (low two bits) for sys_open means:

646 * 00 - read-only

647 * 01 - write-only

648 * 10 - read-write

649 * 11 - special

650 * it is changed into

651 * 00 - no permissions needed

652 * 01 - read-permission

653 * 10 - write-permission

654 * 11 - read-write

655 * for the internal routines (ie open_namei()/follow_link() etc). 00 is

656 * used by symlinks.

657 */

658 struct file *filp_open(const char * filename, int flags, int mode)

659 {

660 int namei_flags, error;

661 struct nameidata nd;

662

663 namei_flags = flags;

664 if ((namei_flags+1) & O_ACCMODE)

665 namei_flags++;

666 if (namei_flags & O_TRUNC)

667 namei_flags |= 2;

668 /* 根据文件名打开文件 */

669 error = open_namei(filename, namei_flags, mode, &nd);

670 if (!error) /* 下面打开这个文件,这个函数返回的是file结构体指针!!! */

671 return dentry_open(nd.dentry, nd.mnt, flags);

672

673 return ERR_PTR(error);

674 }

这个函数比较复杂,请看这个链接:open_namei

回头再看这个函数,dentry_open:这个函数返回的file结构体指针:

[cpp] view
plaincopyprint?

676 struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)

677 {

678 struct file * f;

679 struct inode *inode;

680 static LIST_HEAD(kill_list);

681 int error;

682

683 error = -ENFILE;

684 f = get_empty_filp(); /* 得到一个空的file结构体,如果出错或者内存不足,返回1error */

685 if (!f)

686 goto cleanup_dentry;

687 f->f_flags = flags; /* 一些赋值操作 */

688 f->f_mode = (flags+1) & O_ACCMODE;

689 inode = dentry->d_inode; /* 获得文件inode */

690 if (f->f_mode & FMODE_WRITE) {

691 error = get_write_access(inode);

692 if (error)

693 goto cleanup_file;

694 }

695 /* 一些赋值操作 */

696 f->f_dentry = dentry; /* 目录项 */

697 f->f_vfsmnt = mnt; /* 挂载点 */

698 f->f_pos = 0; /* 文件相对开头偏移 */

699 f->f_reada = 0; /* 预读标志 */

700 f->f_op = fops_get(inode->i_fop); /* 文件操作函数 */

701 file_move(f, &inode->i_sb->s_files);/* 将新建的file链接进入inode对应的超级块的file链表中 */

702

703 /* preallocate kiobuf for O_DIRECT */

704 f->f_iobuf = NULL;

705 f->f_iobuf_lock = 0;

706 if (f->f_flags & O_DIRECT) {

707 error = alloc_kiovec(1, &f->f_iobuf); /* 分配io buffer */

708 if (error)

709 goto cleanup_all;

710 }

711 <span style="white-space:pre"> </span> /* 下面尝试打开文件,保证能够正常打开这个文件 */

712 if (f->f_op && f->f_op->open) {

713 error = f->f_op->open(inode,f);

714 if (error)

715 goto cleanup_all;

716 }

717 f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

718

719 return f; /* 返回创建好的file */

720 /* 下面都是出错处理 */

721 cleanup_all:

722 if (f->f_iobuf)

723 free_kiovec(1, &f->f_iobuf);

724 fops_put(f->f_op);

725 if (f->f_mode & FMODE_WRITE)

726 put_write_access(inode);

727 file_move(f, &kill_list); /* out of the way.. */

728 f->f_dentry = NULL;

729 f->f_vfsmnt = NULL;

730 cleanup_file:

731 put_filp(f);

732 cleanup_dentry:

733 dput(dentry);

734 mntput(mnt);

735 return ERR_PTR(error);

736 }

737

看一下这个函数get_empty_filp,得到一个空的file结构体:

[cpp] view
plaincopyprint?

<span style="font-size:14px;"> </span>26 /* Find an unused file structure and return a pointer to it.

27 * Returns NULL, if there are no more free file structures or

28 * we run out of memory.

29 *

30 * SMP-safe.

31 */

32 struct file * get_empty_filp(void)

33 {

34 static int old_max = 0;

35 struct file * f;

36

37 file_list_lock();

38 if (files_stat.nr_free_files > NR_RESERVED_FILES) { /* 如果允许打开的数量已经超过系统允许的 */

39 used_one:

40 f = list_entry(free_list.next, struct file, f_list); /* 在free_list中删除一个,留下了给新的file使用 */

41 list_del(&f->f_list);

42 files_stat.nr_free_files--;

43 new_one: /* 下面创建一个新的file结构体 */

44 memset(f, 0, sizeof(*f));

45 atomic_set(&f->f_count,1);

46 f->f_version = ++event;

47 f->f_uid = current->fsuid;

48 f->f_gid = current->fsgid;

49 f->f_maxcount = INT_MAX;

50 list_add(&f->f_list, &anon_list);

51 file_list_unlock();

52 return f; /* 返回file */

53 }

54 /*

55 * Use a reserved one if we're the superuser

56 */

57 if (files_stat.nr_free_files && !current->euid)

58 goto used_one;

59 /*

60 * Allocate a new one if we're below the limit. 如果还可以创建file结构体,那么创建一个新的就OK

61 */

62 if (files_stat.nr_files < files_stat.max_files) {

63 file_list_unlock();

64 f = kmem_cache_alloc(filp_cachep, SLAB_KERNEL); /* 在slab中分配一个新的file缓存 */

65 file_list_lock();

66 if (f) {

67 files_stat.nr_files++; /* 数量++ */

68 goto new_one; /* 初始化这个新的值 */

69 }

70 /* Big problems... */

71 printk(KERN_WARNING "VFS: filp allocation failed\n");

72

73 } else if (files_stat.max_files > old_max) {

74 printk(KERN_INFO "VFS: file-max limit %d reached\n", files_stat.max_files);

75 old_max = files_stat.max_files;

76 }

77 file_list_unlock();

78 return NULL;

79 }

/* 4 */:最后看一下fd_install函数,这个函数比较简单,就是将之前申请的文件描述符fd和打开的文件file结构体关联起来:

[cpp] view
plaincopyprint?

<span style="font-size:14px;"> </span>74 /*

75 * Install a file pointer in the fd array.

76 *

77 * The VFS is full of places where we drop the files lock between

78 * setting the open_fds bitmap and installing the file in the file

79 * array. At any such point, we are vulnerable to a dup2() race

80 * installing a file in the array before us. We need to detect this and

81 * fput() the struct file we are about to overwrite in this case.

82 *

83 * It should never happen - if we allow dup2() do it, _really_ bad things

84 * will follow.

85 */

86

87 void fd_install(unsigned int fd, struct file * file)

88 {

89 struct files_struct *files = current->files; /* 获得当前进程文件打开表 */

90

91 write_lock(&files->file_lock);

92 if (files->fd[fd]) /* 如果这个fd下已经存在文件了,那么error! */

93 BUG();

94 files->fd[fd] = file;/* 关联这个fd和新打开的文件 */

95 write_unlock(&files->file_lock);

96 }

至此,文件open操作完成了~~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux fs