天天看點

Linux檔案系統(七)---系統調用之open操作(一)

(核心2.4.37)

一、

當我們打開一個檔案的時候,需要獲得檔案的檔案描述符(前面已經說過其實就是檔案數組下标),一般是通過函數open來完成,這個系統調用在<unistd.h>頭檔案中聲明定義,我們看一下源碼:

530 static inline long open(const char * name, int mode, int flags)
531 {
532         return sys_open(name, mode, flags);
533 }
           

Ps:對于這些參數一般我們都很熟悉,經常使用,這裡順便提出記憶一下:

mode:參數可選:

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中定義

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中):

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 }
           

主要看上面注釋表示的四大步驟      

:這步是一個輔助步驟,将filename從使用者态拷貝到核心态變量中。基本步驟涉及一下幾個函數:

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()函數:

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

就是在核心的slab空間中配置設定能夠容納name的空間~~~

看一下do_getname()函數:

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 }
           

:這一步是需要找到一個沒有使用的檔案描述符fd

看一下這個函數get_unused_fd:看這個連結:get_unused_fd

:再回到上面看步驟,到現在為止,我們已經找到了一個可用的檔案描述符fd了,然後我們要做的就是打開指定檔案,然後将這個fd和打開的檔案關聯即可,步驟就是打開我們指定的檔案!

下面會涉及到名字結構nameidata,是以先看看這個結構體:

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:

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結構體指針:

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結構體:

<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 }
           

:最後看一下fd_install函數,這個函數比較簡單,就是将之前申請的檔案描述符fd和打開的檔案file結構體關聯起來:

<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操作完成了~~~

繼續閱讀