
上文学到 不管什么文件系统类型,都通过VFS(虚拟文件系统层)读和写等 *** 作文件,写文件的元数据和文件的实际数据到磁盘 。但数据是怎么落地磁盘中的呢?落到磁盘中的都经过什么组件?
以一个写数据到磁盘为例,给出Linux I/O子系统的体系结构。
当磁盘执行写入 *** 作时发生的 基本 *** 作 (假设磁盘上扇区中的文件数据已经被读取到分页缓存)。
1) 一个进程通过write()系统调用 VFS虚拟文件系统 请求写一个文件。
2) 内核更新已映射文件的分页缓存。
3) 内核线程 pdflush/Per-BDI flush将分页缓存刷新到磁盘。
4) 同时 VFS虚拟文件系统层 在一个bio(block input output)结构中放置每个块缓冲,并向块设备层提交写请求。
5) 块设备层 从上层得到请求,并执行一个 I/O电梯 *** 作,将请求放置到I/O 请求队列。
6) 设备驱动器 (比如SCSI 或 其他设备特定的驱动器)将执行写 *** 作。
7) 磁盘设备 固件执行硬件 *** 作,如在盘片扇区上定位磁头,旋转,数据传输。
过去的20年中,处理器性能的改进要超过计算机系统中的其他组件,如处理器缓存、物理内存及磁盘等等。 访问内存和磁盘的速度较慢会限制整个系统的性能 ,怎么解决这个问题呢?引入 磁盘缓存机制 ,在较快的存储器中缓存频繁使用的数据,减少了访问较慢的存储器的次数。
磁盘缓存机制有以下3个地方解决:
引入存储层次结构 ,在CPU和磁盘之间放置L1缓存、L2缓存、物理内存和一些其他缓存减少这种不匹配,从而让进程减少访问较慢的内存和磁盘的次数,避免CPU花费更多的时间等待来自较慢磁盘驱动器的数据。
另外一种解决思路: 在更快的存储器上实现更高的缓存命中率,就可能更快地访问数据 。怎么提高缓存命中率呢?引入 参考局部性(locality of reference) 的技术。这项技术基于以下2个原则:
1) 大多数最近使用过的数据,在不久的将来有较高的几率被再次使用(时间局部性)。
2) 驻留在数据附近的数据有较高的几率被再次使用(空间局部性)。
Linux在许多组件中使用这些原则,比如分页缓存、文件对象缓存(索引节点缓存、目录条目缓存等等)、预读缓冲等。
以进程从磁盘读取数据并将数据复制到内存的过程为例。进程可以从缓存在内存中的数据副本中检索相同的数据,用于读和写。
1) 进程写入新数据
当一个进程试图改变数据时,进程首先在内存中改变数据。此时磁盘上的数据和内存中的数据是不相同的,并且内存中的数据被称为 脏页(dirty page) 。脏页中的数据应该尽快被同步到磁盘上,因为如果系统突然发生崩溃(电源故障)则内存中的数据会丢失。
2) 将内存中的数据刷新到磁盘
同步脏数据缓冲的过程被称为 刷新 。在Linux 2632内核之前(Red Hat Enterprise Linux 5),通过内核线程pdflush将脏页数据刷新到磁盘。在Linux 2632内核中(Red Hat Enterprise Linux 6x)pdflush被Per-BDI flush线程(BDI=Backing Device Interface)取代,Per-BDI flush线程以flush-MAJOR:MINOR的形式出现在进程列表中。当内存中脏页比例超过阀值时,就会发生刷新(flush)。
块层处理所有与块设备 *** 作相关的活动。块层中的关键数据结构是bio(block input output)结构,bio结构是在虚拟文件系统层和块层之间的一个接口。
当执行写的时候,虚拟文件系统层试图写入由块缓冲区构成的页缓存,将连续的块放置在一起构成bio结构,然后将其发送到块层。
块层处理bio请求,并链接这些请求进入一个被称为I/O请求的队列。这个链接的 *** 作被称为 I/O电梯调度(I/O elevator)。问个问题:为啥叫电梯调度呢?
Linux 24内核使用的是一种单一的通用I/O电梯调度方法,26内核提供4种电梯调度算法供用户自己选择。因为Linux *** 作系统适用的场合很广泛,所以I/O设备和工作负载特性都会有明显的变化。
1)CFQ(Complete Fair Queuing,完全公平队列)
CFQ电梯调度为每个进程维护一个I/O队列,从而 对进程实现一个QoS(服务质量)策略 。CFQ电梯调度能够很好地适应存在很多竞争进程的大型多用户系统。它积极地避免进程饿死并具有低延迟特征。从2618内核发行版开始,CFQ电梯调度成为默认I/O调度器。
CFQ为每个进程/线程单独创建一个队列来管理产生的请求,各队列之间用时间片来调度,以保证每个进程都能分配到合适的I/O带宽。I/O调度器每次执行一个进程的4个请求。
2)Deadline
Deadline是一种循环的电梯调度(round robin)方法,Deadline 算法实现了一个近似于实时的I/O子系统。在保持良好的磁盘吞吐量的同时,Deadline电梯调度既提供了出色的块设备扇区的顺序访问,又确保一个进程不会在队列中等待太久导致饿死。
Deadline调度器为了兼顾这两个方面,引入了4个队列,这4个队列可分为两类,每一类都由读和写两种队列组成。一类队列用来对 请求 按 起始扇区序号 进行排序(通过红黑树来组织),称为sort_list;另一类对 请求 按 生成时间进行排序 (由链表来组织),称为fifo_list。每当确定了一个传输方向(读或写),系统都将会从相应的sort_list中将一批连续请求调度到请求队列里,具体的数目由fifo_batch来确定。 只有遇到三种情况才会导致一次批量传输的结束 :1对应的sort_list中已经没有请求了;2下一个请求的扇区不满足递增的要求;3上一个请求已经是批量传输的最后一个请求了。
所有的请求在生成时都会被赋上一个期限值,并且按期限值将它们排序在fifo_list中, 读请求的期限时长默认为500ms,写请求的期限时长默认为5s。 在Deadline调度器定义了一个writes_starved默认值为2,写请求的饥饿线。 内核总是优先处理读请求,当饿死进程的次数超过了writes_starved后,才会去考虑写请求 。 为什么内核会偏袒读请求呢 这是从整体性能上进行考虑的。读请求和应用程序的关系是同步的,因为应用程序要等待读取完毕,方能进行下一步工作所以读请求会阻塞进程,而写请求则不一样。应用程序发出写请求后,内存的内容何时被写入块设备对程序的影响并不大,所以调度器会优先处理读请求。
3) NOOP
一个简单的FIFO 队列,不执行任何数据排序。NOOP 算法简单地合并相邻的数据请求,所以增加了少量的到磁盘I/O的处理器开销。NOOP电梯调度假设一个块设备拥有它自己的电梯算法。当后台存储设备能重新排序和合并请求,并能更好地了解真实的磁盘布局时,通常选择NOOP调度,
4)Anticipatory
Anticipatory本质上与Deadline一样,但Anticipatory电梯调度在处理最后一个请求之后会等待一段很短的时间,约6ms(可调整antic_expire改变该值),如果在此期间产生了新的I/O请求,它会在每个6ms中插入新的I/O *** 作,这样可以将一些小的I/O请求合并成一个大的I/O请求,从而用I/O延时换取最大的I/O吞吐量。
Linux内核使用设备驱动程序得到设备的控制权。 设备驱动程序 通常是一个独立的内核模块,通常针对每个设备(或是设备组)而提供,以便这些设备在Linux *** 作系统上可用。一旦加载了设备驱动程序,将被当作Linux内核的一部分运行,并能控制设备的运行。
SCSI (Small Computer System Interface,小型计算机系统接口)是最常使用的I/O设备技术,尤其在企业级服务器环境中。SCSI在 Linux 内核中实现,可通过设备驱动模块来控制SCSI设备。 SCSI包括以下模块类型 :
1) Upper IeveI drivers(上层驱动程序)。 sd_mod、sr_mod(SCSI-CDROM)、st(SCSI Tape)和sq(SCSI通用设备)等。
2) MiddIe IeveI driver(中层驱动程序) 。如scsi_mod实现了 SCSI 协议和通用SCSI功能。
3) Low IeveI drivers(底层驱动程序) 。提供对每个设备的较低级别访问。底层驱动程序基本上是特定于某一个硬件设备的,可提供给某个设备。
4) Pseudo drive(伪驱动程序) 。如ide-scsi,用于 IDE-SCSI仿真。
通常一个较大的性能影响是文件系统元数据怎样在磁盘上存放 。引入 磁盘条带阵列 (RAID 0、RAID 5和RAID 6)解决这个问题。在一个条带阵列上,磁头在移动到阵列中下一个磁盘之前,单个磁盘上写入的数据称为 CHUNKSIZE ,所有磁盘使用一次它后返回到第一个磁盘。 如果文件系统的布局没有匹配RAID的设计,则有可能会发生一个文件系统元数据块被分散到2个磁盘上,导致对2个磁盘发起请求 。或者 将所有的元数据在一个单独的磁盘上存储,如果该磁盘发生故障则可能导致该磁盘变成热点 。
设计RAID阵列需要考虑以下内容:
1) 文件系统使用的块大小。
2) RAID 阵列使用的CHUNK大小。
3) RAID 阵列中同等磁盘的数量。
块大小 指可以读取/写入到驱动器的最小数据量,对服务器的性能有直接的影响。块的大小由文件系统决定,在联机状态下不能更改,只有重新格式化才能修改。可以使用的块大小有1024B、2048B、4096B,默认为 4096 B。
stride条带 是在一个chunk中文件系统块的数量。如果文件系统块大小为4KB,则chunk大小为64KB,那么stride是64KB/4KB=16块。
stripe-width 是RAID阵列上一个条带中文件系统块的数量。比如 一个3块磁盘的RAID5阵列 。按照定义,在RAID5阵列每个条带中有1个磁盘包含奇偶校验内容。想要得到stripe-width,首先需要知道每个条带中有多少磁盘实际携带了数据块,即3磁盘-1校验磁盘=2数据磁盘。2个磁盘中的stride是chunk中文件系统块的数量。因此能计算 2(磁盘)16(stride)=32(stripe)。
创建文件系统时可以使用mkfs给定数量:mk2fs -t ext4 -b 4096 -E stripe=16,stripe_width=64 /dev/vda
一、Linux device driver 的概念\x0d\\x0d\系统调用是 *** 作系统内核和应用程序之间的接口,设备驱动程序是 *** 作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象 *** 作普通文件一样对硬件设备进行 *** 作。设备驱动程序是内核的一部分,它完成以下的功能:\x0d\\x0d\1、对设备初始化和释放;\x0d\\x0d\2、把数据从内核传送到硬件和从硬件读取数据;\x0d\\x0d\3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;\x0d\\x0d\4、检测和处理设备出现的错误。\x0d\\x0d\在Linux *** 作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O *** 作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。\x0d\\x0d\已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。\x0d\\x0d\最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。\x0d\\x0d\二、实例剖析\x0d\\x0d\我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。\x0d\\x0d\由于用户进程是通过设备文件同硬件打交道,对设备文件的 *** 作方式不外乎就是一些系统调用,如 open,read,write,close, 注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢这需要了解一个非常关键的数据结构:\x0d\\x0d\STruct file_operatiONs {\x0d\\x0d\int (seek) (struct inode ,struct file , off_t ,int);\x0d\\x0d\int (read) (struct inode ,struct file , char ,int);\x0d\\x0d\int (write) (struct inode ,struct file , off_t ,int);\x0d\\x0d\int (readdir) (struct inode ,struct file , struct dirent ,int);\x0d\\x0d\int (select) (struct inode ,struct file , int ,select_table );\x0d\\x0d\int (ioctl) (struct inode ,struct file , unsined int ,unsigned long);\x0d\\x0d\int (mmap) (struct inode ,struct file , struct vm_area_struct );\x0d\\x0d\int (open) (struct inode ,struct file );\x0d\\x0d\int (release) (struct inode ,struct file );\x0d\\x0d\int (fsync) (struct inode ,struct file );\x0d\\x0d\int (fasync) (struct inode ,struct file ,int);\x0d\\x0d\int (check_media_change) (struct inode ,struct file );\x0d\\x0d\int (revalidate) (dev_t dev);\x0d\\x0d\}\x0d\\x0d\这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write *** 作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。\x0d\\x0d\下面就开始写子程序。\x0d\\x0d\#include 基本的类型定义\x0d\\x0d\#include 文件系统使用相关的头文件\x0d\\x0d\#include \x0d\\x0d\#include \x0d\\x0d\#include \x0d\\x0d\unsigned int test_major = 0;\x0d\\x0d\static int read_test(struct inode inode,struct file file,char buf,int count)\x0d\\x0d\{\x0d\\x0d\int left; 用户空间和内核空间\x0d\\x0d\if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )\x0d\\x0d\return -EFAULT;\x0d\\x0d\for(left = count ; left > 0 ; left--)\x0d\\x0d\{\x0d\\x0d\__put_user(1,buf,1);\x0d\\x0d\buf++;\x0d\\x0d\}\x0d\\x0d\return count;\x0d\\x0d\}\x0d\\x0d\这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf 是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考,在向用户空间拷贝数据之前,必须验证buf是否可用。这就用到函数verify_area。为了验证BUF是否可以用。\x0d\\x0d\static int write_test(struct inode inode,struct file file,const char buf,int count)\x0d\\x0d\{\x0d\\x0d\return count;\x0d\\x0d\}\x0d\\x0d\static int open_test(struct inode inode,struct file file )\x0d\\x0d\{\x0d\\x0d\MOD_INC_USE_COUNT; 模块计数加以,表示当前内核有个设备加载内核当中去\x0d\\x0d\return 0;\x0d\\x0d\}\x0d\\x0d\static void release_test(struct inode inode,struct file file )\x0d\\x0d\{\x0d\\x0d\MOD_DEC_USE_COUNT;\x0d\\x0d\}\x0d\\x0d\这几个函数都是空 *** 作。实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。\x0d\\x0d\struct file_operations test_fops = {\x0d\\x0d\read_test,\x0d\\x0d\write_test,\x0d\\x0d\open_test,\x0d\\x0d\release_test,\x0d\\x0d\};\x0d\\x0d\设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。\x0d\\x0d\int init_module(void)\x0d\\x0d\{\x0d\\x0d\int result;\x0d\\x0d\result = register_chrdev(0, "test", &test_fops); 对设备 *** 作的整个接口\x0d\\x0d\if (result \x0d\\x0d\#include \x0d\\x0d\#include \x0d\\x0d\#include \x0d\\x0d\main()\x0d\\x0d\{\x0d\\x0d\int testdev;\x0d\\x0d\int i;\x0d\\x0d\char buf[10];\x0d\\x0d\testdev = open("/dev/test",O_RDWR);\x0d\\x0d\if ( testdev == -1 )\x0d\\x0d\{\x0d\\x0d\printf("Cann't open file \n");\x0d\\x0d\exit(0);\x0d\\x0d\}\x0d\\x0d\read(testdev,buf,10);\x0d\\x0d\for (i = 0; i 回答于 2022-11-18
所有的硬件设备必须挂载之后才能使用,只不过,有些硬件设备(比如硬盘分区)在每次系统启动时会自动挂载,而有些(比如 U 盘、光盘)则需要手动进行挂载。
mount命令(用来挂载硬盘或镜像等) 用法:mount [-t vfstype] [-o options] device dir -t vfstype 指定文件系统的类型,通常不必指定。 mount 会自动选择正确的类型。
linux如何查看当前路径下一个表的大小
只查看文件夹下的文件个数(当前目录的文件数) ls-l|grep"^-"|wc-l 1 注意:该命令只查看到当前目录下文件的个数,不包含子目录、以及子目录下的文件。 说明: ls -l长列表输出该目录
2
查看文件夹下的文件个数(当前目录的文件和子文件夹的文件数) find/ -type f|wc-l 1 三、linux查看磁盘空间 df -h命令来查看磁盘信息, -h 选项为根据大小适当显示 df
Linux下没有盘符的概念,而是将各分区通过挂载到目录(挂载点)来访问实际的磁盘分区,有时候我们想知道某个文件或目录是在哪个分区上,
有如下几种方法:
1、最简单的,直接 df -h 目录名
比如我要查看工作目录下的baidupan目录在哪个分区
# df -h baidupan
可以看到,baidupan在vda1分区下
2、用df 或 fdisk -l查看分区挂载情况,直接输入mount或者也可以用cat /etc/mtab,然后pwd找最接近的挂载点信息
拓展内容
Linux 的分区规定
1 设备管理
在 Linux 中,每一个硬件设备都映射到一个系统的文件,对于硬盘、光驱等 IDE 或 SCSI 设备也不例外。Linux 把各种 IDE 设备分配了一个由 hd 前缀组成的文件;而对于各种 SCSI 设备,则分配了一个由 sd 前缀组成的文件。
对于ide硬盘,驱动器标识符为“hdx~”,其中“hd”表明分区所在设备的类型,这里是指ide硬盘了。“x”为盘号(a为基本盘,b为基本从属盘,c为辅助主盘,d为辅助从属盘),“~”代表分区,前四个分区用数字1到4表示,它们是主分区或扩展分区,从5开始就是逻辑分区。例,hda3表示为第一个ide硬盘上的第三个主分区或扩展分区,hdb2表示为第二个ide硬盘上的第二个主分区或扩展分区。对于scsi硬盘则标识为“sdx~”,scsi硬盘是用“sd”来表示分区所在设备的类型的,其余则和ide硬盘的表示方法一样,不在多说。
例如,第一个 IDE 设备,Linux 就定义为 hda;第二个 IDE 设备就定义为 hdb;下面以此类推。而 SCSI 设备就应该是sda、sdb、sdc 等。
2 分区数量
要进行分区就必须针对每一个硬件设备进行 *** 作,这就有可能是一块IDE硬盘或是一块SCSI硬盘。对于每一个硬盘(IDE 或 SCSI)设备,Linux 分配了一个 1 到 16 的序列号码,这就代表了这块硬盘上面的分区号码。例如,第一个 IDE 硬盘的第一个分区,在 Linux 下面映射的就是 hda1,第二个分区就称作是 hda2。对于 SCSI 硬盘则是 sda1、sdb1 等。
3 各分区的作用
在 Linux 中规定,每一个硬盘设备最多能有 4个主分区(其中包含扩展分区)构成,任何一个扩展分区都要占用一个主分区号码,也就是在一个硬盘中,主分区和扩展分区一共最多是 4 个。对于早期的 DOS 和 Windows(Windows 2000 以前的版本),系统只承认一个主分区,可以通过在扩展分区上增加逻辑盘符(逻辑分区)的方法,进一步地细化分区。
主分区的作用就是计算机用来进行启动 *** 作系统的,因此每一个 *** 作系统的启动,或者称作是引导程序,都应该存放在主分区上。
这就是主分区和扩展分区及逻辑分区的最大区别。
我们在指定安装引导 Linux 的 bootloader 的时候,都要指定在主分区上,就是最好的例证。Linux 规定了主分区(或者扩展分区)占用 1 至 16 号码中的前 4 个号码。以第一个 IDE 硬盘为例说明,主分区(或者扩展分区)占用了 hda1、hda2、hda3、hda4,而逻辑分区占用了 hda5 到 hda16 等 12 个号码。因此,Linux 下面每一个硬盘总共最多有 16 个分区。对于逻辑分区,Linux 规定它们必须建立在扩展分区上(在 DOS 和 Windows 系统上也是如此规定),而不是主分区上。因此,我们可以看到扩展分区能够提供更加灵活的分区模式,但不能用来作为 *** 作系统 的引导。 除去上面这些各种分区的差别,我们就可以简单地把它们一视同仁了。
4 分区指标
对于每一个 Linux 分区来讲,分区的大小和分区的类型是最主要的指标。容量的大小读者很容易理解,但是分区的类型就不是那么容易接受了。分区的类型规定了这个分区上面的文件系统的格式。Linux 支持多种的文件系统格式,其中包含了我们熟悉的FAT32、FAT16、NTFS、HP-UX,以及各种 Linux 特有的 Linux Native和 Linux Swap分区类型。在 Linux 系统中,可以通过分区类型号码来区别这些不同类型的分区。各种类型号码在介绍Fdisk的使用方式的时候将会介绍。
姓名:王芷若 学号:19020100180
学院:电子工程学院
嵌牛导读:本篇文章整理Linux知识点—Linux字符型设备驱动初步。
嵌牛鼻子:Linux设备类型,结构体,驱动模块
嵌牛提问:Linux设备有什么类型?关键函数有哪些?
嵌牛内容–linux字符型设备驱动初步
一、Linux字符设备驱动初步
1、Linux设备类型
(1)字符设备:只能一个字节一个字节的读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后顺序进行。字符设备是面向流的设备,常见的字符设备如鼠标、键盘、串口、控制台、LED等。
(2)块设备:是指可以从设备的任意位置读取一定长度的数据设备。块设备如硬盘、磁盘、U盘和SD卡等存储设备。
(3)网络设备:网络设备比较特殊,不在是对文件进行 *** 作,而是由专门的网络接口来实现。应用程序不能直接访问网络设备驱动程序。在/dev目录下也没有文件来表示网络设备。
2、开发流程
在这里插入描述
3、关键函数讲解(以26以下版本内核为例)
(1)驱动模块注册register_chrdev()函数
原型:register_chrdev(unsigned int major, const char name,const struct file_operations fops);
major:主设备号,该值为 0 时,自动运行分配。而实际值不是 0 ;
name:设备名称;
fops: *** 作函数,实现驱动定义的open、read、write、close等内核函数与应用程序调用的open、read、write、close间的映射;
返回值:
major 值为 0 ,正常注册后,返回分配的主设备号。如果分配失败,返回 EBUSY 的负值 ( -EBUSY ) 。major 值若大于 linux/majorh (24内核)中声明的最大值 (#define MAX_CHRDEV 255) ,则返回EINVAL 的负值 (-EINVAL) 。指定 major 值后,若有注册的设备,返回 EBUSY 的负值 (-EBUSY)。若正常注册,则返回 0 值
(2)驱动注销unregister_chrdev()函数
原型:
#include <linuxfsh>
int unregister_chrdev (unsigned int major, const char name)
变量:
major 主设备号
name 设备文件
返回值:
major 值若大于 linux/majorh (24 内核)中声明的最大值 (#define MAX_CHRDEV 255),返回 EINVAL的负值 (-EINVAL)。指定了 major的值后,若将要注销的 major 值并不是注册的设备驱动程序,返回 EINVAL的负值 ( -EINVAL )。正常注销则返回 0值。
(3)File_operation结构体
file_operations结构是建立驱动程序和设备编号的连接,内部是一组函数指针,每个打开的文件,也就是file结构,和一组函数关联,这些 *** 作主要用来实现系统调用的
struct file_operations {
struct module owner;//拥有该结构的模块的指针,一般为THIS_MODULES
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 (aio_read) (struct kiocb , const struct iovec , unsigned long, loff_t);//初始化一个异步的读取 *** 作
ssize_t (aio_write) (struct kiocb , const struct iovec , unsigned long, loff_t);//初始化一个异步的写入 *** 作
int (readdir) (struct file , void , filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (poll) (struct file , struct poll_table_struct ); //轮询函数,判断目前是否可以进行非阻塞的读写或写入
int (ioctl) (struct inode , struct file , unsigned int, unsigned long); //执行设备I/O控制命令
long (unlocked_ioctl) (struct file , unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl
long (compat_ioctl) (struct file , unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替
int (mmap) (struct file , struct vm_area_struct ); //用于请求将设备内存映射到进程地址空间
int (open) (struct inode , struct file ); //打开
int (flush) (struct file , fl_owner_t id);
int (release) (struct inode , struct file ); //关闭
int (fsync) (struct file , struct dentry , int datasync); //刷新待处理的数据
int (aio_fsync) (struct kiocb , int datasync); //异步刷新待处理的数据
int (fasync) (int, struct file , int); //通知设备FASYNC标志发生变化
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 );
};
linux下面的概念是一切皆文件。所以没有像c盘d盘这样的东西,有的只是各种各样的文件夹和文件。要读一个linux下面的文件很简单,命令pwd可以得到当前路径,然后路径接上你打开的文件名就可以知道这个文件的详细路径了。直接open就可以了。
以上就是关于Linux磁盘I/O子系统全部的内容,包括:Linux磁盘I/O子系统、linux驱动程序结构框架及工作原理分别是什么、Linux系统下,如果要访问分过区的磁盘,还要进行什-|||-么 *** 作等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)