涉及到创建打开关闭等操作
int creat(const char *filename, mode_t mode)
其中参数filename指的是文件名,mode指的是文件的权限,它同umask决定了文件的最终权限,umask代表了文件在创建时要去除的一些存取权限
int umask(int newmask)
将umask设置为新的mask,然后返回旧的umask,只影响读写执行的权限
int open(const char *pathname, int flags, mode_t mode);
int open(const char *pathname, int flags);
pathname默认的是当前文件夹的下面
文件的打开标志:
标志 | 含义 |
---|---|
O_RDONLY | |
O_WRONLY | |
O_RDWR | rdonly,wronly和rdwr这三个标志只能选取其中一个,不能同时存在 |
O_APPEND | |
O_CREAT | |
O_EXEC | 如果使用了O_CREAT而且文件已经存在,就会发生一个错误 |
O_NOBLOCK | 以非阻塞的方式打开一个文件 |
O_TRUNC | 如果文件已经存在,则删除文件的内容 |
如果使用了mode_t,表示打开时有文件的访问权限:
标志 | 含义 |
---|---|
S_IRUSR | 用户可以读 |
S_IWUSR | |
S_IXUSR | |
S_IRGRP | 组可以读 |
S_IWGRP | |
S_IXGRP | |
S_IROTH | 其他人可以读 |
S_IWOTH | |
S_IXOTH | |
S_IRWXO | 其他人可以读写执行 |
S_ISUID | 设置用户执行ID(set UID) |
S_ISGID | 设置组执行ID |
除了上述宏来表示的产生标志之外,我们自己也可以用数字来表示
文件权限共5位:从左往右数第一位用户ID、第二位组ID、第三位自己的权限、第四位组权限、第五位其他人的权限;
数字表示1(执行权限)、2(写权限)、4(读权限)、0(无权限)
例如要创建一个用户可读、可写、可执行、但是组没有权限,其他人可以读、可以执行的文件,并设置用户ID。
open(filename, O_CREAT, 10 705);
open(filename, O_CREAT, S_IRWXU|S_IROTH|S_XOTH|S_ISUID);
如果文件打开成功会返回一个文件描述符,以后对于文件的所有操作都可以通过对这个文件描述符进行操作来实现。
int read(int fd, const void *buf, size_t length);
buf为缓冲区名称,length为缓冲区的大小,单位为字节,表示在fd中读取length个字节写入buf中,返回的是实际读取的字节数。
int write(int fd, const void *buf, size_t length);
表示从buf中读取length字节数写入fd中,返回实际写入的字节数
int lseek(int fd, offset_t offset, int whence);
lseek将文件的读写指针相对whence移动了offset个字节,返回文件指针相对于文件开发的位置
SEEK_SET | 相对文件开头 |
---|---|
SEEK_CUR | 相对当前位置 |
SEEK_END | 相对文件末尾 |
lseek(fd, 0, SEEK_END)
表示文件的大小
也就是文件指针是从wherece的位置处开始移动,结果移动了0个位置,指针就停在了whence的位置,而函数返回值是文件指针相对于文件开头的位置,因此这个返回值就是文件的长度。
int close(int fd);
例如,编写一个程序,在当前目录下面创建用户可读写文件hello.txt,在其中写入“hello,software weekly”,关闭该文件,再次打开该文件,读取其中的内容输出在屏幕上
#include
#include
#include
#include
#include
#include
#define BUFF_SIZE 100
int man()
{
int ret;
char buff[BUFF_SIZE];
int fd = open("hello.txt", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if(fd){
ret = write(fd, "hello,software weekly", strlen(hello,software weekly));
if(ret < 0)
printf("write failed\n");
close(fd);
}
else{
printf("creat failed\n");
}
fd = open("hello.txt", O_RDONLY);
ret = read(fd, buff, sizeof(buff));
if(ret){
printf("read failed\n");
close(fd);
}
buff[ret] = "\0";
printf("%s\n", buff);
close(fd);
}
FILE *fopen(const char *path, const char *mode);
mode为C库函数打开的标志:
标志 | 含义 |
---|---|
r、rb | 读 |
r+、r+b | 读写 |
w | 写,不存在则创建 |
w+、w+b | 读写,不存在则创建 |
a | 追加,不存在则创建 |
a+、a+b | 读写且追加,不存在则创建 |
b为二进制文件,在window下面是有区分的,但是Linux下面则没有
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
fread表示从stream流中读取n个字段,每个字段大小为size个字节,将读取到的数据存到ptr中,返回已经读取的字段数。当读取的字段数小于n时,可能是在函数调用时出现了错误,也可能是读到了文件的末尾。因此要通过调用feof()和ferror()来判断。
另外C库还提供了定位函数:
int fseek(FILE *stream, long offset, int whence);
int fgetpos(FILE *stream, fpos_t *pos);
int fsetpos(FILE *stream, const fpos_t *pos);
int fclose(FILE *stream);
例如,编写一个程序,在当前目录下面创建用户可读写文件hello.txt,在其中写入“hello,software weekly”,关闭该文件,再次打开该文件,读取其中的内容输出在屏幕上
#include
#define BUFF_SIZE 100
int main()
{
char buff[BUFF_SIZE];
FILE *fd = fopen("hello.txt", w+);
if (fd){
fputs("hello,software weekly", fd);
fclose(fd);
}
fd = fopen("hello.txt", r+);
if(fd){
fgets(buff, sizeof(buff), fd);
printf("%s\n", buff); //fgets函数会默认在字符串后面补"\0"
fclose(fd);
}
}
linux文件系统目录结构
应用程序与文件系统直接的接口是系统调用,文件系统与设备文件之间的接口是file_operations结构体成员函数。
由于字符设备的上层没有类似于文件系统,所以字符设备的file_operations成员函数就直接由设备驱动提供了
块设备有两种访问方法:
一种是不通过文件系统直接访问裸设备,在Linux内核实现了统一的def_blk_fops这一file_operations
类似于”dd if=/dev/sdb1 of=sdb1.img”这一指令是不通过文件系统,直接访问的驱动设备文件
第二种是通过文件系统访问块设备,file_operations的实现则位于文件系统内,文件系统会把针对文件的读写转换为针对块设备原始扇区的读写
在设备驱动程序的设计种,一般而言,会关心file和inode这两个结构体
系统中每个打开的文件在内核空间都有一个关联的struct file,在内核和驱动源代码中,struct file的指针通常被命名为file或者filep
文件结构体struct file中读写模式mode、标志f_flags都是设备驱动关心得内容,而私有指针private_data在设备驱动中被广泛使用大多数指向设备驱动自定义以**用于描述设备的结构体,**是更高级的文件描述,依赖于低层的struct inode数据结构
VFS inode是Linux管理文件系统的最基本的单位,也是文件系统连接任何子目录,文件的桥梁,只与操作系统相关,用于保存文件或者目录信息。
表示设备文件的inode结构,i_rdev字段包含设备编号。Linux的设备编号分为主设备编号和次设备编号,前者为dev_t的高12位,后者位dev_t的低20位
unsigned int imajor(struct inode *inode);
获取主设备号,主设备号是驱动对应的概念,同一类设备一般使用相同的主设备号,序号一般从0开始
unsigned int imanor(struct inode *inode);
获取次设备号
查看 /proc/devices
可以获知系统中所注册的设备
struct inode和struct file的区别在于inode只关心操作系统找出底层文件结构的内容(例如什么设备文件),而不去具体的跟踪文件的当前位置和当前模式。struct file是个基本结构,实际上持有一个struct inode的指针,他代表打开的文件,并且提供一组函数,他们与底层文件结构执行方法有关
也就是说struct inode只代表内核中的文件,而struct file表示实际打开的文件,同一个文件被打开时可能有多个文件描述符,但他们都指向同一个inode。
devfs(设备文件系统)是由Linux 2.4内核出现的,使得设备驱动程序能够自主地管理自己的设备文件
register_chrdev()
传递0主设备号以获得可用的主设备号,并在 devfs_register()
中指定次设备号udev和sysfs都是Linux系统的一部分,但它们有不同的功能和用途。sysfs是一个虚拟文件系统,用于从用户空间访问内核对象的属性。它将内核对象的属性表示为文件和目录,使得用户空间的程序能够通过标准的文件系统接口查询和更改内核对象的属性。包括展示设备驱动模型中各组件的层次关系。
在/sys/目录下的顶级目录:
block | 包含所有的块设备 |
---|---|
devices | 包含系统所有的设备 |
bus | 包含所有的总线类型 |
class | 包含系统中的设备类型 |
他们实际上都会被认为是kobject的派生类,一个kobject对应sysfs中的一个目录。
在/sys/bus/pci的目录下又会出现devices和drivers,而这个devices目录下的文件是对/sys/devices下的文件的符号链接
与此不同,udev是一个设备管理器,它管理/dev目录下的设备节点。当内核检测到新设备时,它将发送一个uevent,udev将接收到这个事件并对其进行处理。这可能包括加载设备驱动,创建或删除设备节点,甚至更改设备的权限和所有权。udev的工作依赖于sysfs,因为它使用sysfs提供的信息来确定如何处理设备。
总的来说,sysfs是一个提供有关内核对象的信息的接口,而udev则使用这些信息来管理设备。就好比udev是做饭的方法,而sys是菜和米饭
在Linux内核中,分别使用bus_type、device_driver和device来描述总线、驱动和设备,这三个结构体定义在include/linux/device.h中
#define DRIVER_ATTR(_name, _mode, _show, _store) \
struct driver_attribute driver_attr_##_name = __ATTR(_name, _
mode, _show, _store)
#define DRIVER_ATTR_RW(_name) \
struct driver_attribute driver_attr_##_name = __ATTR_RW(_name)
#define DRIVER_ATTR_RO(_name) \
struct driver_attribute driver_attr_##_name = __ATTR_RO(_name)
#define DRIVER_ATTR_WO(_name) \
struct driver_attribute driver_attr_##_name = __ATTR_WO(_name)
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _
store)
#define BUS_ATTR_RW(_name) \
struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)
#define BUS_ATTR_RO(_name) \
struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
#define DEVICE_ULONG_ATTR(_name, _mode, _var) \
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
#define DEVICE_INT_ATTR(_name, _mode, _var) \
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) }
#define DEVICE_BOOL_ATTR(_name, _mode, _var) \
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) }
#define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = \
__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
撰写不易,留下您的关注和点赞,我们一起进步!