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等系统调用进行读写

版权声明:本文为whuer_xiaojie原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。