Linux 文件系统与驱动

之前做应用程序的开发,就知道Linux系统的一大特点就是一切皆文件,一直以来对所有设备的操作都是使用系统函数open read write close来实现的,就没关心过系统里面是怎么实现对各种设备的区分和控制柜,直到开始看Linux设备驱动方面的知识以后,感觉像发现了新大陆一样的神奇,故把相关知识记录下来,方便以后自己查找。

文件系统调用

文件的打开

int open(const char *pathname, int flags);
int open(const char *pathname, int falgs, mode_t mode);

open函数有两个形式,其中第一个参数是需要打开的文件路径及文件名(默认是当前路径),flags是一下值的一个或者几个的组合:

标志(flag) 含义
O_RDONLY 只读方式
O_WRONLY 只写方式
O_RDWR 可读可写
O_APPEND 追加的方式
O_CREAT 创建一个文件
O_EXEC 如果使用了O_CREAT而且文件已经存在,就会发生一个错误
O_NOBLOCK 以非阻塞的方式
O_TRUNC 如果文件已经存在就删除文件内容

O_RDONLY ,O_WRONLY ,O_RDWR这三个标志位只能使用其中的一个
如果使用了O_CREAT标志,那么调用的函数需要指定第三个参数mode标志,以表示文件的访问权限:

标志(flag) 含义
S_IRUSR 用户可以读
S_IWUSR 用户可以写
S_IXUSR 用户可以执行
S_IRWXU 用户可读,可写,可执行
S_IRGRP 组可读
S_IWGRP 组可写
S_IXGRP 组可执行
S_IRWXG 组可读,可写,可执行
S_IROTH 其他人可以读
S_IWOTH 其他人可写
S_IXOTH 其他人可执行
S_IRWXO 其他人可读,可写,可执行
S_ISUID 设置用户执行ID
S_ISGID 设置组ID

除了通过以上宏第一或运算产生标志位以外,可以自己用数字生产标志位规则如下:

byte位 含义 备注
第一位 (byte 4) 设置用户ID
第二位 (byte 3) 设置组ID
第三位 (byte 2) 用户自己的权限 可取 1(可执行),2 (可写),4(可读),0(无)或者这些值的和
第四位 (byte 1) 组的权限
第五位 (byte 0) 其他人权限
open("test", O_CREAT, 10705);
open("test", O_CREAT, S_IRWXU | S_IROTH | S_IXOTH | S_ISUID);

上面两个函数等价:当前路径下创建一个用户可读可写可执行,组没有权限,其他人可读可执行并设置用户ID名为test的文件

文件的读写

int read(int fd, const void *buf, size_t length);
int write(int fd, const void *buf, size_t length);

参数 fd 为open函数返回的文件描述符,buf为指向缓冲区的指针,length为缓冲区大小;
read()从fd所指的文件里面读取length个字节到buf缓冲区,返回实际读取到的字节数;
write()把length个字节从缓冲区buf中写到fd指向的文件去,返回实际写进去的字节数;

文件定位

int lseek(int fd, offset_t offset, int whence);

将文件指针相对whence移动offset个字节,操作成功时返回文件指针相对于文件头的位置,whence可以取:

  • SEEK_SET:文件头
  • SEEK_CUR:当前位置
  • SEEK_END:文件末尾

lseek(fd, 0, SEEK_END);返回的就是文件的长度
lseek(fd, -5 , SEEK_CUR);将文件指针相对当前位置向前移动5字节

文件关闭

int close(int fd);

文件系统与设备驱动

Linux应用程序和设置之间的关系如下图所示:
在这里插入图片描述
应用程序通过系统调用,操作系统通过虚拟文件系统中的file_operations结构体去调用相应的设备驱动,file_operations结构体定义了一系列函数指针,相应的设备驱动实现这些函数,并把函数赋值给file_operations的函数指针,然后把驱动注册到内核。file_operations结构体定义如下(include/linux/fs.h):

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iopoll)(struct kiocb *kiocb, bool spin);
	int (*iterate) (struct file *, struct dir_context *);
	int (*iterate_shared) (struct file *, struct dir_context *);
	__poll_t (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	unsigned long mmap_supported_flags;
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
#endif
	ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
			loff_t, size_t, unsigned int);
	loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
				   struct file *file_out, loff_t pos_out,
				   loff_t len, unsigned int remap_flags);
	int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;
file_operations内核函数 用户空间函数 备注
llseek() lseek() 用来修改一个文件的当前读写位置,并将新位置返回,出错时返回一个负数
read() read() fread() 从设备中读取数据,成功返回读取的字节数,失败返回负数,0暗示end-of-file
write() write() fwrite() 向设备发送数据,成功返回发送的字节数,失败返回负数,如果此函数未被实现,用户调研write()以后讲收到-EINVAL返回值
unlocked_ioctrl() ioctrl() fcntl() 提供设备相关控制命令调用,成功返回非负值,失败返回负数
mmap() mmap() 帧缓冲被映射到用户空间,应用程序可以直接访问,不需要再内核空间和用户空间进行内存复制,如果此函数未被实现,用户调研mmap()以后讲收到-EINVAL返回值
poll() select() poll() 用于查询设备是否可被非阻塞的进行读写,当查询的调节未触发时,用户空间进行select() 和 poll()查询系统调用时将引起进程的阻塞
aio_read() aio_write 对设备进行异步读写操作,当该函数实现以后,用户空间可以对设备执行SYS_io_setup、SYS_io_submit、SYS_io_getevents、SYS_io_destory等系统调用进行读写
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章