sys_open()代码分析
应用程序在操作任何一个文件之前,必须先调用open()来打开该文件,即通知内核新建一个代表该文件的结构,并且返回该文件的描述符(一个整数),该描述符在进程内唯一。open()的格式如下:
int open(const char * pathname,int oflag, mode_t mode ) pathname:代表需要打开的文件的文件名;
oflag:表示打开的标识,具体的内核支持如下标记位:
O_ACCMODE<0003>: 读写文件操作时,用于取出flag的低2位。
O_RDONLY<00>: 只读打开
O_WRONLY<01>: 只写打开
O_RDWR<02>: 读写打开
O_CREAT<0100>: 文件不存在则创建,需要mode_t
O_EXCL<0200>: 如果同时指定了O_CREAT,而文件已经存在,则出错
O_NOCTTY<0400>: 如果pathname代表终端设备,则不将此设备分配作为此进程的控制终端
O_TRUNC<01000>: 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0
O_APPEND<02000>: 每次写时都加到文件的尾端
O_NONBLOCK<04000>: 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。
O_NDELAY
O_SYNC<010000>: 使每次write都等到物理I/O操作完成。
FASYNC<020000>: 兼容BSD的fcntl同步操作
O_DIRECT<040000>: 直接磁盘操作标识,每次读写都不使用内核提供的缓存,直接读写磁盘设备
O_LARGEFILE<0100000>: 大文件标识
O_DIRECTORY<0200000>: 必须是目录
O_NOFOLLOW<0400000>: 不获取连接文件
O_NOATIME<01000000>: 暂无
当新创建一个文件时,需要指定mode参数,以下说明的格式如宏定义名称<实际常数值>: 描述如下:
S_IRWXU<00700>:文件拥有者有读写执行权限
S_IRUSR (S_IREAD)<00400>:文件拥有者仅有读权限
S_IWUSR (S_IWRITE)<00200>:文件拥有者仅有写权限
S_IXUSR (S_IEXEC)<00100>:文件拥有者仅有执行权限
S_IRWXG<00070>:组用户有读写执行权限
S_IRGRP<00040>:组用户仅有读权限
S_IWGRP<00020>:组用户仅有写权限
S_IXGRP<00010>:组用户仅有执行权限
S_IRWXO<00007>:其他用户有读写执行权限
S_IROTH<00004>:其他用户仅有读权限
S_IWOTH<00002>:其他用户仅有写权限
S_IXOTH<00001>:其他用户仅有执行权限
当open()系统调用进入内核时候,最终调用的函数为
SYSCALL_DEFINE3(open, const char __user*, filename, int, flags, int,mode)
实际相当于调用:
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
该函数位于fs/open.c中,下面将会分析其具体的实现过程。
1. 2. 3. 4. 5. 6. 7. 8. 9.
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) {
long ret;
//判断系统是否支持大文件,即判断long的位数,如果64则表示支持大文件; if (force_o_largefile()) flags |= O_LARGEFILE;
//完成主要的open工作,AT_FDCWD表示从当前目录开始查找 ret = do_sys_open(AT_FDCWD, filename, flags, mode);
10. /* avoid REGPARM breakage on x86: */
11. asmlinkage_protect(3, ret, filename, flags, mode); 12. return ret; 13. }
该函数主要调用do_sys_open()来完成打开工作,do_sys_open()的代码分析如下。
1. long do_sys_open(int dfd, const char__user *filename, int flags, int mode) 2. {
3. //将欲打开的文件名拷贝到内核中,该函数的分析见下文; 4. char *tmp = getname(filename); 5. int fd = PTR_ERR(tmp); 6.
7. if (!IS_ERR(tmp)) {
8. //从进程的文件表中找到一个空闲的文件表指针,如果出错,则返回,见下文说明; 9. fd = get_unused_fd_flags(flags); 10. if (fd >= 0) {
11. //执行打开操作,见下文说明,dfd=AT_FDCWD;
12. struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); 13. if (IS_ERR(f)) { 14. put_unused_fd(fd);
15. fd = PTR_ERR(f); 16. } else {
17. fsnotify_open(f);
18. //添加打开的文件表f到当前进程的文件表数组中,见下文说明; 19. fd_install(fd, f); 20. } 21. }
22. putname(tmp); 23. }
24. return fd; 25. }
1. 该函数主要分为如下几个步骤来完成打开文件的操作: 2. 1.将文件名参数从用户态拷贝至内核,调用函数get_name();
3. 2.从进程的文件表中找到一个空闲的文件表指针,调用了函数get_unused_fd_flgas(); 4. 3.完成真正的打开操作,调用函数do_filp_open();
5. 4.将打开的文件添加到进程的文件表数组中,调用函数fd_install(); 6. char * getname(const char __user * filename) 7. {
8. char *tmp, *result; 9.
10. result = ERR_PTR(-ENOMEM); 11. //从内核缓存中分配空间; 12. tmp = __getname(); 13. if (tmp) {
14. //将文件名从用户态拷贝至内核态;
15. int retval = do_getname(filename, tmp); 16. result = tmp;
17. //如果拷贝失败,则调用__putname()释放__getname()中申请的空间; 18. if (retval < 0) { 19. __putname(tmp);
20. result = ERR_PTR(retval); 21. } 22. }
23. audit_getname(result); 24. return result;
25. }
26. 该函数主要的任务是将文件名filename从用户态拷贝至内核态: 27. 1.首先调用__getname()来从内核缓存中分配空间;
28. 2.调用do_getname()将文件名filename从用户态拷贝至内核态tmp中,如果成功,则返回保存用户名
的内核缓冲区的起始地址,否则,调用__putname()释放1中申请的内核缓冲区; 29. get_unused_fd_flags(flags)
30. #define get_unused_fd_flags(flags) alloc_fd(0, (flags)) 31. int alloc_fd(unsigned start, unsigned flags) 32. {
33. //获得当前进程的files_struct 结构;
34. struct files_struct *files = current->files; 35. unsigned int fd; 36. int error;
37. struct fdtable *fdt; 38.
39. spin_lock(&files->file_lock); 40. repeat:
41. fdt = files_fdtable(files); 42. fd = start;
43. //从上一次打开的fd的下一个fd开始搜索空闲的fd 44. if (fd < files->next_fd) 45. fd = files->next_fd; 46. //寻找空闲的fd,返回值为空闲的fd 47. if (fd < fdt->max_fds)
48. fd = find_next_zero_bit(fdt->open_fds->fds_bits, 49. fdt->max_fds, fd);
50. //如果有必要,即打开的fd超过max_fds,则需要expand当前进程的fd表;
51. //返回值error<0表示出错,error=0表示无需expand,error=1表示进行了expand; 52. error = expand_files(files, fd); 53. if (error < 0) 54. goto out; 55. 56. /*
57. * If we needed to expand the fs array we 58. * might have blocked - try again.