目录
1.Linux文件系统操作
Linux文件创建,打开,关闭函数
Linux下文件读写函数
2.C库文件操作
3.Linux文件系统
3.1根目录结构
3.2.VFS
VFS 虚拟文件系统基础概念
Linux文件系统与设备驱动关系:
设备驱动结构体:file,inode
inode结构体
inode之atime,mtime,ctime
file结构体
3.3 sysfs文件系统
3.4 udev规则文件
#文件权限最终由mode&umask决定
int creat (const char *filename,mode_t mode); //文件创建
int umask(int newmask); //修改文件权限
int open (const char *filename,int flags); //文件打开
int open (const char *filename,int flags,mode_t mode); //文件打开
int close (int fd);//fd=open(......)//文件关闭函数
其中 flag可为以下一个或组合:(当flags含O_CREAT时,open函数相当于创建文件函数)
mode为以下一个或组合:
当然mode也可以用数字表示,方法如下:
权限数字含义:
数字含义 | 1 | 2 | 4 | 0 |
执行权限 | 写权限 | 读权限 | 无权限 |
文件权限各位含义(10705为例):
位数 | 第一位 | 第二位 | 第三位 | 第四位 | 第五位 |
作用 | 设置用户ID | 设置组ID | 用户权限 | 组权限 | 其他人权限 |
1 | 0 | 7(1+2+4) | 0 | 5(1+4) | |
设置 | 不设置 | 执行;读;写 | 无权限 | 读;执行 |
因此10705等价与S_IRWXU|S_IROTH|SIXOTH|S_ISUID
文件打开后,就要对文件进行读写
int read(int fd,const void *buf,size_t length);
int write(int fd,const void *buf,size_t length);
指定位置读写:
nt lseek(int fd,offset_t offset,int whence);
lseek()将文件读写指针相对whence移动offset个字节,函数返回文件指针相对头问价的偏移量
SEEK_SET | 相对文件开头 |
SEEK_CUR | 相对文件读写指针的当前位置 |
SEEK_END | 相对文件末尾 |
利用标准C语言库进行文件读写
目录 |
应放置档案内容 |
/bin |
系统有很多放置执行档的目录,但/bin用于存放基本命令,单用户模式下也能运行. |
/boot |
主要放置开机会使用到的档案,包括Linux核心档案以及开机选单与开机所需设定档等等。Linux kernel常用的档名为:vmlinuz ,如果使用的是grub这个开机管理程式,则还会存在/boot/grub/这个目录。 |
/dev |
设备文件存储目录,应用程序通过对此目录下文件读写和控制以访问实际设备 |
/etc |
系统配置文件所在地,一些服务器的配置文件也在这里,如用户账户及密码配置文件 |
/home |
这是系统预设的使用者家目录(home directory)。 在你新增一个一般使用者帐号时,预设的使用者家目录都会规范到这里来。 |
/lib |
系统库文件存放目录,其下的/lib/modules/目录,放置核心相关的模组(驱动程式)。 |
/media |
media是媒体的英文,这个/media底下放置的就是可移除的装置。 包括软碟、光碟、DVD等等装置都暂时挂载于此 |
/mnt |
挂载储存设备的目录 |
/opt |
可选的意思,有些软件被安装在这里 |
/root |
系统管理员(root)的家目录。 因此即使单用户模式下仅仅挂在根目录,也能访问root目录 |
/sbin |
Linux有非常多指令是用来设定系统环境的,这些指令只有root才能够利用来设定系统,其他使用者最多只能用来查询而已。放在/sbin底下的为开机过程中所需要的,里面包括了开机、修复、还原系统所需要的指令。至于某些伺服器软体程式,一般则放置到/usr/sbin/当中。至于本机自行安装的软体所产生的系统执行档(system binary),则放置到/usr/local/sbin/当中了。常见的指令包括:fdisk, fsck, ifconfig, init, mkfs等等。 |
/srv |
srv可以视为service的缩写,是一些网路服务启动之后,这些服务所需要取用的资料目录。 常见的服务例如WWW, FTP等等。 举例来说,WWW伺服器需要的网页资料就可以放置在/srv/www/里面。呵呵,看来平时我们编写的代码应该放到这里了。 |
/tmp |
这是让一般使用者或者是正在执行的程序暂时放置档案的地方。这个目录是任何人都能够存取的,所以你需要定期的清理一下。当然,重要资料不可放置在此目录啊。 因为FHS甚至建议在开机时,应该要将/tmp下的资料都删除。 |
/proc |
这个目录本身是一个虚拟文件系统(virtual filesystem),它存在于内存中。 操作系统运行时,进程以及内核信息(比如CPU,硬盘分区,内存信息等)存放在这里.他放置的资料都是在内存当中, |
/sys | 2.6以后内核所支持的sysfs文件系统被映射在此处 |
/tmp |
用户运行时产生的临时文件,/tmp用来用来存储临时文件 |
/usr | 系统存放程序的目录,比如用户命令,用户库 |
/var | 这个目录的内容经常变动,比如/var/log 存放系统日志 |
Linux 允许众多不同的文件系统共存,并支持跨文件系统的文件操作,这是因为有虚拟文件系统的存在。虚拟文件系统,即VFS(Virtual File System)是 Linux 内核中的一个软件抽象层。它通过一些数据结构及其方法向实际的文件系统如 ext2,vfat 提供接口机制。
可以看到
字符设备上层没有类似ext2的文件系统,所以访问字符设备的file_operations成员函数直接由字符设备驱动提供
块设备访问方法有两种:
Linux字符设备中的两个重要结构体(file、inode),含源代码
Linux inode理解
文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是4KB,即连续八个 sector组成一个 block。
文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。
inodo中包括的内容如下:(总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。)
用stat命令查看文件的inode信息,如下:
时间戳 | 解释 | 备注 |
Access (atime) |
在终端上用cat、more 、less、grep、sed、 cp 、file 一个文件时,此文件的Access的时间记录都会被更新(空文件例外),纯粹的access是不会影响modify和change,但会受到modify行为的影响。 | ls等命令访问后时间也更新 |
Modify (mtime) |
当更改了一个文件的内容的时候,此文件的modify的时间记录会被更新。用ls -l看到的文件时间是最近一次modify的时间。modify的行为是三个行为中最有影响力的行为,它发生以后,会使文件的access记录与change记录也同时得到更新。对于目录也是如此。 | 更改数据后更新,但是拷贝,剪切等操作不会更新该时间 |
change (ctime) |
对一个文件或者目录作mv、chown、chgrp操作后,它的Change时间记录被更新,change时间会受到modify行为的影响。用ls -lc看到的文件时间是最近一次change的时间。 | 拷贝等操作后更新,mtime,atime更新后,change也会更新 |
在设备驱动中,这也是个非常重要的数据结构,必须要注意一点,这里的file与用户空间程序中的FILE指针是不同的,用户空间FILE是定义在C库中,从来不会出现在内核中。而struct file,却是内核当中的数据结构,因此,它也不会出现在用户层程序中。
file结构体指示一个已经打开的文件(设备对应于设备文件),其实系统中的每个打开的文件在内核空间都有一个相应的struct file结构体,它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数,直至文件被关闭。如果文件被关闭,内核就会释放相应的数据结构。
在内核源码中,struct file要么表示为file,或者为filp(意指“file pointer”), 注意区分一点,file指的是struct file本身,而filp是指向这个结构体的指针。
打开终端,在/sys目录下,用命令tree打印树形目录
可以发现/sys结构可以描述如下:
设备存储在/sys/devices目录下,而诸如总线(bus),类(class)下的设备,实际只是链接,最终 链接到/sysy/devices下的设备
udev规则采用行为单位,每一行代表一个规则,每个规则分为一个或多个匹配部分和赋值部分.
匹配关键词包括:
ACTION: | 事件行为,例如:add remove |
KERNEL: | 内核设备名,例如:sda,ttyUSB* |
DEVPATH: | 设备的devpath路径, |
SUBSYSTEM: | 设备的子系统名称,例如sda的子系统为block |
BUS: | 设备在devpath中的总线名称,例如:USB |
DRIVER: |
设备在devpath中的设备驱动名称,例如ide-cdrom |
SYSFS{filename} | 设备的devpath路径下,设备的属性文件"filename"中的内容 |
ENV{key}: | 环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键。 |
PROGRAM: | 调用外部命令。 |
RESULT: | 外部命令 PROGRAM 的返回结果。 |
ATTR: | ATTRS - 匹配设备的sysfs属性,或任何双亲设备的sysfs属性 ,例如:ATTR{size},ATTR{address} |
注意:
devpath:devpath是指一个设备在sysfs文件系统(/sys)下的相对路径,该路径包含了指定设备的属性文件。udev里的多数命令都是针对devpath操作的。例如:sda的devpath是/block/sda,sda2的devpath是/block/sda/sda2
udev的重要赋值键:
NAME: | 在 /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。 |
SYMLINK: | 为 /dev/下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。 |
OWNER, GROUP, MODE: | 为设备设定权限。 |
udev 的值和可调用的替换操作符
Linux 用户可以随意地定制 udev 规则文件的值。例如:my_root_disk, my_printer。同时也可以引用下面的替换操作符:
$kernel, %k:设备的内核设备名称,例如:sda、cdrom。
$number, %n:设备的内核号码,例如:sda3 的内核号码是 3。
$devpath, %p:设备的 devpath路径。
$id, %b:设备在 devpath里的 ID 号。
$sysfs{file}, %s{file}:设备的 sysfs里 file 的内容。其实就是设备的属性值。
$env{key}, %E{key}:一个环境变量的值。
$major, %M:设备的 major 号。
$minor %m:设备的 minor 号。
$result, %c:PROGRAM 返回的结果。
$parent, %P:父设备的设备文件名。
$root, %r:udev_root的值,默认是 /dev/。
$tempnode, %N:临时设备名。
%%:符号 % 本身。
$$:符号 $ 本身。
例子:
添加串口线,根据芯片自动绑定对应串口别名,参考博客USB hub 多usb接口重映射:udev 规则