[1]ブロックデバイスブロックデバイスの
特徴:
セクターに応じたアクセス、アクセスの単位は512バイトで、
シーケンシャルまたは順不同でアクセスできます。
块设备的硬件知识:
比如一个移动硬盘:
磁头:有多少个盘面
磁道:一个面内有多少环
扇区:一个环内有多少个扇区,一个扇区512字节
磁盘的数据的读取:
磁盘数据的读取不会按照存储顺序来读取,因为磁头它是机械结构,
通过旋转来访问数据,如果按照数据来访问,需要反复的切换这个
物理结构,比较浪费时间。所以磁盘在访问的时候采用电梯优化的算法
来完成。即一次将一个盘面上的数据全部读取到,然后切换物理结构,
在读取下面的数据。将所有的数据读取完之后,进行数据的排序,排序
之后进行操作。
页 4K 段 :可以包含多个block
block 512字节 1K 2K 4K 扇区 512字节
[2]ブロックデバイス
ユーザーのフレーム構造:
open read write close(すべてがファイル)
------------------ |(io request)-------- --------------
kernel:| VFS:(struct block_device)
| ext2 ext3 ext4 yaffs jiffs
|
|上記のファイルシステムは、ioリクエストをブロックに変換するために使用されます
| request bio(block input出力)、Linuxカーネル
は、物理アドレスに対する連続したbio要求を1つの要求に結合します。
|要求はキューに入れられます。
| -----------------------------------------
|ブロックデバイスドライバー:struct gendisk
| 1.割り当てられたオブジェクト
| 2.オブジェクトの初期化、キューの初期化
| 3.ハードディスクハードウェアの操作
| 4.登録と登録解除
ハードウェア:ハードディスク
[3]ブロックデバイスドライバの構造と機能
1.ブロックデバイス構造の
構造体gendisk { INT主要; //メジャーデバイス番号のint first_minor; //マイナーデバイス番号の値開始INTの未成年者; //番号デバイスのチャーDISK_NAME [DISK_NAME_LEN]; //ブロックデバイスの名前struct disk_part_tbl * part_tbl; //ハードディスクパーティションテーブルstruct hd_struct part0; // ハードディスクのパーティションconst struct block_device_operations * fops; // ブロックデバイスの操作メソッド構造struct request_queue * queue; // Request queue void * private_data; // private data }; 2.パーティション構造struct hd_struct { ector_t start_sect; //セクターの開始ector_t nr_sects; // セクターの数sector_t alignment_offset; //アライメント方法int partno; //パーティション番号};
3.操作方法结构体
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
int (*release) (struct gendisk *, fmode_t);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*getgeo)(struct block_device *, struct hd_geometry *);
//设置磁盘有多少个磁头,有多少个磁道,有多少扇区
};
4.队列的操作的方法
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
功能:初始化一个队列
参数:
@rfn :队列处理函数
typedef void (request_fn_proc) (struct request_queue *q);
//这个是队列处理函数的原型,在这个函数中要进行读写操作
@lock :自旋锁
返回值:成功返回初始化好的队列的指针
失败返回NULL
struct request *blk_fetch_request(struct request_queue *q)
功能:从队列中取出一个请求
参数:
@q :队列
返回值:成功返回request结构体指针
失败返回NULL
void blk_cleanup_queue(struct request_queue *q)
功能:清除队列
参数:
@q:被清除的队列
返回值:无
5.关于request的操作
sector_t blk_rq_pos(const struct request *rq)
功能:从请求中获取本次操作的起始的函数
unsigned int blk_rq_cur_sectors(const struct request *rq)
功能:获取本次想读写的扇区的个数
int blk_rq_cur_bytes(const struct request *rq)
功能:获取本次想读写的字节的个数
rq_data_dir(rq)
功能:从request中获取本次是读还是写
rq_data_dir(rq) == READ 读
rq_data_dir(rq) == WRITE 写
bool __blk_end_request_cur(struct request *rq, 0)
功能:判断request的读写是否处理完了
参数:
@rq :请求队列
返回值:真:表示数据没有处理完
假:数据处理完成了
2.gendisk内存分配并初始化的函数
struct gendisk *alloc_disk(int minors)
功能:分配内存并初始一些内容
参数:
@minors :设备的个数
2.申请块设备的设备号
int register_blkdev(unsigned int major, const char *name)
功能:申请块设备的设备号
参数:
@major :如果填写的是大于0的值静态指定设备号
如果填写的是0表示动态申请设备号
@name :cat /proc/devices
返回值:
major > 0 成功返回0,失败返回错误码
major = 0 成功返回主设备号,失败返回小于等于0的值
void unregister_blkdev(unsigned int major, const char *name)
功能:失败块设备的设备号
参数:
@major :主设备号
@name :名字
返回值:无
3.块设备驱动的注册/注销
void add_disk(struct gendisk *disk)
功能:注册gendisk
参数:
@disk :gendisk对象的地址
void del_gendisk(struct gendisk *disk)
功能:注销gendisk
参数:
@disk :gendisk对象的地址
块设备驱动的测试:
1.安装驱动
sudo insmod mydisk.ko
2.查看
cat /proc/devices
ls /dev/mydisk
sudo fdisk -l
3.分区
sudo fdisk /dev/mydisk
:m --->打印帮助信息
:n --->新建一个分区
:p --->打印分区
:w --->保存退出
:q --->退出
4.格式化
sudo mkfs.ext3 /dev/mydisk1
5.将磁盘挂载到一个目录下
sudo mount /dev/mydisk1 ~/udisk
在udisk中存放文件和目录
6.取消挂载
sudo umount ~/udisk
7.将刚才写进磁盘的数据给读出来
cat /dev/mydisk1 > mydisk_file.bin
8.查看.bin中是否有文件
sudo mount -o loop mydisk_file.bin ~/udisk
在udisk中可以看到刚才写进入的文件和目录
表示块设备驱动是成功的。
メモリ割り当て機能:
仮想メモリ:
1.アドレス
物理アドレス:データシートで見つけることができるアドレスは、物理アドレス、実際のデバイスの操作アドレスと呼ばれます;
仮想アドレス、線形アドレス:オペレーティングシステムプログラマーが操作できるアドレスそれは仮想アドレスです;
論理アドレス:プログラムを分解した後、見ることができるアドレスは論理アドレスと呼ばれます;
2.内存管理
段式管理
页式管理
3.内存映射关系
4g------------------------------------
4k
-----------------------------------
固定内存映射区 4m-4k
-----------------------------------
高端内存映射区 4m(alloc_page)
------------------------------------
NULL 8K(保护)
------------------------------------vmalloc end
vmalloc内存区120m-8m-8k(低端或者高端内存)
------------------------------------vmalloc start
vmalloc offset 8m
------------------------------------
物理内存映射区896M(kmalloc get_free_page低端内存)
3g------------------------------------ 物理内存 3g 偏移 4k + 3g
用户空间
0g------------------------------------
4.linux内存分配函数
void *kmalloc(size_t s, gfp_t gfp)
功能:分配对应的虚拟内存
参数:size:分配内存区的大小(2的次幂,最大是128K,连续)
flags:内存分配标志
GFP_KERNEL:内核可能被休眠,进程上下文,不能用于中断上下文中
GFP_ATOMIC:处理紧急的事务,用在中断上下文
返回值:对应虚拟地址
特点:最大128k , 分配虚拟地址,其虚拟地址空间连续,物理地址空间也是连续
类似函数:kzalloc:kmalloc+memset(buf,0,sizeof(buf));
void kfree(const void *x)
功能:释放对应的虚拟内存
参数:x:虚拟内存的起始地址
返回值:无
void *vmalloc(unsigned long size)
功能:分配对应的虚拟内存
参数:size:分配内存区的大小
返回值:对应虚拟地址
特点:分配虚拟地址,其虚拟地址空间连续,但是物理地址空间不一定连续
void vfree(const void *addr)
功能:释放对应的虚拟内存
参数:addr:虚拟内存区的首地址
返回值:无
unsigned long __get_free_page(gfp_t gfp)
功能:分配一个物理页
参数:
@gfp
GFP_KERNEL:内核可能被休眠,进程上下文,不能用于中断上下文中
GFP_ATOMIC:处理紧急的事务,用在中断上下文
void free_page(unsigned long addr)
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
功能:分配多个物理页
参数:
@gfp_mask:
GFP_KERNEL:内核可能被休眠,进程上下文,不能用于中断上下文中
GFP_ATOMIC:处理紧急的事务,用在中断上下文
@order:填写的是想分配内存的次值n=get_order(57600)
57600 = 2^n
void free_pages(unsigned long addr, unsigned long order)
[1]カメラドライバー
V4L2:Linuxのビデオ2:Linuxカーネルのカメラドライバーのフレームワークであり、
アプリケーションに/ dev / video0 を提供します。
1.video:カメラドライバー
2.vbi:垂直ブランキングビデオ信号(ケーブルテレビ)
3.radio:無線機器(radio)
虚拟摄像头:vivi
linux lxr :在线查看linux内核源码的网站:
https://lxr.missinglinkelectronics.com/linux/
1.下载虚拟摄像头驱动
在网站中找到3.5内核的vivi.c的代码
点击下载:vivi.c
2.使用Makefile对vivi.c进行编译
编译完成之后生成vivi.ko
3.安装驱动
insmod: error inserting 'vivi.ko': -1 Unknown symbol in module
insmod安装驱动的时候不会安装依赖文件
modprobe:在安装驱动的时候会检查依赖
sudo modprobe -i vivi
sudo modprobe -r vivi
在安装成功之后就会产生一个/dev/video0的设备节点。
4.安装应用程序来读取摄像头的数据
sudo apt-get install xawtv
sudo apt-get install cheese
[2]カメラドライバー
1.カメラオブジェクト
struct video_device * vfd;
vfd = video_device_alloc();
2.初始化摄像头的对象
static struct video_device vfd = {
.name = "vivi",
.fops = &vivi_fops, //v4l2的操作方法结构体
.ioctl_ops = &vivi_ioctl_ops, //v4l2的ioctl函数
.release = video_device_release,
};
3.注册
video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
完成摄像头设备的初始化
__video_register_device(vdev, type, nr, 1, vdev->fops->owner);
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video"; //注册的是摄像头设备驱动
break;
case VFL_TYPE_VBI:
name_base = "vbi"; //场消隐设备驱动
break;
case VFL_TYPE_RADIO: //无线电设备的驱动
name_base = "radio";
break;
}
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
}
字符设备驱动的注册:
static const struct file_operations v4l2_fops = {
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
.release = v4l2_release,
.poll = v4l2_poll,
};
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
#define VIDEO_MAJOR 81 摄像头的主设备号是81
//创建了设备节点相当于class_create device_create
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
ユーザー:
open( "/ dev / video0")読み取り書き込みioctl close
---------- | ------------------------ ------------------------
|
字符设备驱动:v4l2_fops
static const struct file_operations v4l2_fops = { .read = v4l2_read、.write = v4l2_write、.open = v4l2_open、の.mmap = v4l2_mmap、.unlocked_ioctl = v4l2_ioctl、.release = v4l2_release、.poll = v4l2_poll、; 上述的オープン読み取りと書き込みのioctl ...会调用v4l2_file_operations ***************** ******************************************* static const struct v4l2_file_operations vivi_fops = { .open = v4l2_fh_open、.release = vivi_close、.read = vivi_read、.poll = vivi_poll、
.unlocked_ioctl = video_ioctl2、/ * V4L2 ioctlハンドラ* /
.mmap = vivi_mmap、
};
上述的ioctl函数会调用v4l2_ioctl_ops
***************************************************************
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
//查询设备的类型是否是摄像头设备
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
//枚举,获取,尝试,设置摄像头格式
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
//申请,查询,存放,取出buf等操作
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
//启动关闭摄像头
};