之前做应用程序的开发,就知道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 等系统调用进行读写 |