sys - open 下载本文

内容发布更新时间 : 2024/4/26 4:37:18星期一 下面是文章的全部内容请认真阅读。

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.