Linux设备驱动开发详解【五】_Linux⽂件系统和设备⽂件系
本⽂简介
由于字符设备和块设备都很好地体现了“⼀切都是⽂件”的设计思想,掌握Linux⽂件系统、设备⽂件系统的知识⾮常重要。
⾸先,设备驱动最终通过操作系统的⽂件系统调⽤或C库函数(本质也基于系统调⽤)被访问。
其次,驱动⼯程师在设备驱动中不可避免地会与设备⽂件系统打交道,如Linux2.4内核的devfs⽂件系统和Linux2.6内核的基于sysfs的udev⽂件系统。
5.1节讲解了通过Linux API和C库函数在⽤户空间进⾏Linux⽂件操作的编程⽅法。
5.2节分析了Linux⽂件系统的⽬录结构,简单介绍了Linux内核中⽂件系统的实现,并给出了⽂件系统和设备驱动的关系。
5.3节和5.4节分别讲解了Linux2.4内核的devfs和Linux2.6内核所采⽤的udev设备⽂件系统,并分析了两者的区别。
5.1 Linux⽂件操作
⼀、⽂件操作的相关系统调⽤
Linux的⽂件操作系统调⽤(在Windows编程领域,习惯称操作系统提供的接⼝为API)涉及创建、打开、读写和关闭⽂件。
1、创建
东南大学matlab正版化int creat(const char *filename, mode_t mode);
参数 mode 指定新建⽂件的存取权限,它同 umask ⼀起决定⽂件的最终权限(mode&umask),其中 umask
代表了⽂件在创建时需要去掉的⼀些存取权限。 umask 可通过系统调⽤ umask()来改变,如下所⽰:
int umask(int newmask);
该调⽤将umask设置为newmask,然后返回旧的umask,它只影响读、写和执⾏权限。
2、打开
1int open(const char *pathname, int flags);
2int open(const char *pathname, int flags, mode_t mode);
open()函数有两个形式,其中pathname是我们要打开的⽂件名(包含路径名称,默认是在当前路径下⾯),flags可以是如表5.1所⽰的⼀个值或者⼏个值的组合。
表5.1 ⽂件打开标志
标志含义
O_RDONLY以只读的⽅式打开⽂件
O_WRONLY以只写的⽅式打开⽂件
O_RDWR以读写的⽅式打开⽂件
O_APPEND以追加的⽅式打开⽂件
O_CREAT 创建⼀个⽂件
O_EXEC 如果使⽤了O_CREAT⽽且⽂件已经
存在,
就会发⽣⼀个错误
O_NONBLOCK
以⾮阻塞的⽅式打开⼀个⽂件O_TRUNC 如果⽂件已经存在,则删除⽂件的内容
如果使⽤了O_CREAT标志,则使⽤的函数是int open(const char *pathname, int flags, mode_t mode),这个时候还要指定mode标志,⽤来表⽰⽂件的访问权限。mode可以是如表5.2所⽰值的组合。
表5.2 ⽂件访问权限
标志
含义S_IRUSR
⽤户可以读S_IWUSR
⽤户可以写S_IXUSR
⽤户可以执⾏S_IRWXU
py是什么意思⽤户可以读、写、执⾏S_IRGRP
组可以读S_IWGRP
组可以写S_IXGRP
组可以执⾏S_IRWXG
组可以读、写、执⾏S_IROTH
其他⼈可以读S_IWOTH
其他⼈可以写S_IXOTH
其他⼈可以执⾏S_IRWXO
其他⼈可以读、写、执⾏S_ISUID
设置⽤户的执⾏ID S_ISGID 设置组的执⾏ID
除了可以通过上述宏进⾏“或”逻辑产⽣标志以外,我们还可以⾃⼰⽤数字来表⽰,Linux总共可以⽤5个数字来表⽰⽂件的各种权限:第⼀位表⽰设置⽤户ID,第⼆位表⽰设置组ID,第三位表⽰⽤户⾃⼰的权限位,第四位表⽰组的权限,第五位表⽰其他⼈的权限。每个数字可以取1(执⾏权限)、2(写权限)、4(读权限)、0(⽆)或者这些值的和。
例如,如果要创建⼀个⽤户可读、可写、可执⾏,但是组没有权限,其他⼈可以读、可以执⾏的⽂件,并设置⽤户ID位。那么,应该使⽤的模式是1(设置⽤户组ID)、0(不设置组ID)、7(1+2+4,读、写、执⾏)、0(没有权限)、5(1+4,读、执⾏),即10705,如下所⽰:
上述语句等价于:
如果⽂件打开成功,open函数会返回⼀个⽂件描述符,以后对该⽂件的所有操作就可以通过对这个⽂件描述符进⾏操作来实现。
3、读写
open("test", O_CREAT, 10705);
open("test", O_CREAT, S_IRWXU | S_IROTH | S_IXOTH | S_ISUID);
在⽂件打开后,我们才可以对⽂件进⾏读写,Linux系统中提供⽂件读写的系统调⽤是read、write函数,如下所⽰:
1int read(int fd,const void *buf, size_t length);
2int write(int fd,const void *buf, size_t length);
其中参数buf为指向缓冲区的指针,length为缓冲区的⼤⼩(以字节为单位)。函数read()实现从⽂件描述符fd所指定的⽂件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数。函数write实现把length个字节从buf指向的缓冲区中写到⽂件描述符fd所指向的⽂件中,返回值为实际写⼊的字节数。
以O_CREAT为标志的open函数实际上实现了⽂件创建的功能,因此,下⾯的函数等同于create()函数:
int open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
4、定位
对于随机⽂件,我们可以随机地指定位置读写,使⽤如下函数进⾏定位:
int lseek(int fd, offset_t offset, int whence);
lseek()将⽂件读写指针相对whence移动offset个字节。操作成功时,返回⽂件指针相对于⽂件头的位置。参数whence可以使⽤如下值。
SEEK_SET:相对⽂件开头;
SEEK_CUT:相对⽂件读写指针的当前位置;
SEEK_END:相对⽂件末尾。
正则表达式练习题offset可取负值,例如下述调⽤可将⽂件指针相对当前位置向前移动5个字节。
lseek(fd, -5, SEEK_CUR);
由于lseek函数的返回值为⽂件指针相对于⽂件头的位置,因此下列调⽤的返回值就是⽂件的长度:
lseek(fd, 0, SEEK_END);
5、关闭
当操作完成以后,就要关闭⽂件了,只要调⽤close函数就可以,其中fd是要关闭的⽂件描述符。
int close(int fd);
例程:编写⼀个程序,在当前⽬录下创建⽤户可读写⽂件“”,在其中写⼊“Hello World”,关闭该⽂件。再次打开该⽂件,读取其中的内容并输出在屏幕上,如代码清单5.1所⽰。
代码清单5.1 Linux⽂件操作⽤户空间编程(使⽤系统调⽤)
编译并运⾏,执⾏结果为输出“Hello World”。
⼆、C 库函数的⽂件操作
C库函数的⽂件操作实际上是独⽴于具体的操作系统平台的,不管是DOS、Windows、Linux还是在Vxworks中都是这些函数。
1、创建和打开
fopen()实现打开指定⽂件filename,其中的mode为打开模式,C库函数中⽀持的打开模式如表5.3所⽰。
表5.3 C库函数⽂件打开标志
标志
含义r、rb 以只读⽅式打开
w、wb 以只写⽅式打开。如果⽂件不存在,则创建该⽂件,否
则⽂件被截断
a、ab 以追加⽅式打开。如果⽂件不存在,则创建该⽂件
r+、r+b、rb+以读写⽅式打开
w+、w+b、wh+以读写⽅式打开。如果⽂件不存在,则创建该⽂件,否
则⽂件被截断
a+、a+b、ab+
以读和追加⽅式打开。如果⽂件不存在,则创建新⽂件        其中b⽤于区分⼆进制⽂件和⽂本⽂件,这⼀点在DOS、Windows系统中是有区分的,但Linux系统不区分⼆进制⽂件和⽂本⽂件。
jquery开发基础教程
2、读写
C库函数⽀持以字符、字符串等为单位,⽀持按照某种格式进⾏⽂件的读写,这⼀组函数为:
1
#include <sys/types.h>2
#include <sys/stat.h>3
#include <fcntl.h>4
#include <stdio.h>5
#define LENGTH 1006
7
void main(void){8
int fd, len;9
char str[LENGTH];10
11
/*创建并打开⽂件 */12
fd = open("", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); 13
14
if (fd){15
write(fd, "Hello World", strlen("Hello World")); /*写⼊字符串 */16
close(fd);17
}18
19
fd = open("", O_RDWR);20
len = read(fd, str, LENGTH); /* 读取⽂件内容 */21
str[len] = '\0';22
printf("%s\n", str);23    close(fd);24}
FILE *fopen(const char *path, const char *mode);
1int fgetc(FILE *stream);
2int fputc(int c, FILE *stream);
3char *fgets(char *s, int n, FILE *stream);
4int fputs(const char *s, FILE *stream);
5int fprintf(FILE *stream, const char *format, ...);
6int fscanf(FILE *stream, const char *format, ...);
7size_t fread(void *ptr, size_t size, size_t n, FILE *stream);
8size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);
fread()实现从stream中读取n个字段,每个字段为size个字节,并将读取的字段放⼊ptr所指的字符数组中,返回实际已读取的字段数。在读取的字段数⼩于num时,可能是在函数调⽤时出现错误,也可能是读到⽂件的结尾,所以要通过调⽤feof()和ferror()来判断。
write()实现从缓冲区ptr所指的数组中把n个字段写到stream中,每个字段长为size个字节,返回实际写⼊的字段数。
另外,C库函数还提供了读写过程中的定位能⼒,这些函数包括:
1int fgetpos(FILE *stream, fpos_t *pos);
sql语句查询包含2int fsetpos(FILE *stream, const fpos_t *pos);
3int fseek(FILE *stream, long offset, int whence);
3、关闭
利⽤C库函数关闭⽂件依然是很简单的操作,如下所⽰:
int fclose(FILE *stream);
例程:上⼀个例程⽤C库函数来实现,如代码清单5.2所⽰。
代码清单5.2 Linux⽂件操作⽤户空间编程范例(使⽤C库函数)
1#include <stdio.h>
linux建立文件系统的命令
2#define LENGTH 100
3main(){
4    FILE *fd;
5    char str[LENGTH];
6
7    fd = fopen("", "W+");    /*创建并打开⽂件*/
8    if(fd){
9        fputs("Hello World", fd);    /*写⼊字符串*/
10        fclose(fd);
11    }
12
13    fd = fopen("","r");
14    fgets(str, LENGTH, fd);    /*读取⽂件内容*/
15    printf("%s\n", str);
16    fclose(fd);
17}
5.2 Linux⽂件系统
⼀、Linux⽂件系统⽬录结构