Linux设备管理

设备管理概述 #

最复杂、最多样化的操作系统资源管理模块

设备并行操作是提高设备利用率关键所在

功能范畴

设备分类

设备管理模块功能

设备独立性

缓冲管理 #

缓冲及目的

缓冲池

设备分配 #

假脱机技术

输入/输出控制方式及模块 #

程序查询方式

中断控制方式

内存直接存取方式(DMA)

通道方式

输入输出模块:

Unix 设备管理 #

Unix 设备管理概要 #

Unix 设备管理数据结构 #

struct devtab
{
 int dvid;			/*设备标识符*/
 int dvadd; 		/*设备地址*/
 int *dvec; 		/*设备中断向量*/
 int *dvbuf; 		/*设备缓冲区指针*/
 int *dvque; 		/*设备等待队列首址*/
 int (*dvinit)(); 		/*设备驱动初始化函数*/
 int(*dvopen)(); 		/*设备驱动打开函数*/
 int(*dvclose)(); 		/*设备驱动关闭函数*/
 int(*dvread)(); 		/*设备驱动读函数*/
 int(*dvwrite)(); 		/*设备驱动写函数*/
 int(*dvseek)(); 		/*设备驱动查询函数*/
 int(*dvcntl)(); 		/*设备驱动控制操作函数*/
 int(*dvgetc)(); 		/*设备驱动取字符函数*/
 int(*dvputc)(); 		/*设备驱动送字符函数*/
}dct[];

Unix 设备请求及处理

int read(int dvcrp, char *buf, int size)
{
 struct devtab *devptr;  /*定义指向设备表的指针*/
 if (isbaddev(dvcrp)))	 /*确认设备标识符是否有效*/
  return(SYSERR);		
 devptr = &dct[dvcrp]; /*有效则找到对应设备表项*/
 return((*devptr->dvread)(devptr, buf, size));
 /*将参数传递给设备驱动程序*/
}

内核与设备驱动程序接口

Unix 块设备控制器控制表 struct iobuf

struct  iobuf
{
 int  b_flags;		/*设备的状态标志 */
 dev_t  b_dev;		/*设备名,即逻辑设备号*/
 char  b_active;		/*设备正在执行一个I/O请求的标志*/
 char  b_errcnt;		/*出错计数*/
 struct  buf  *b_forw;	/*指向本设备缓冲区散列队列下一个buf*/
 struct  buf  *b_back;	/*指向本设备缓冲区散列队列上一个buf*/
 struct  buf  *b_actf;	/*指向本设备I/O请求队列中第一个buf*/
 struct  buf  *b_actl;	/*指向本设备I/O请求队列中最后一个buf*/
 struct  eblock  *io_erec;	/*指向块设备错误记录块*/
 int  io_nreg;		/*设备寄存器的个数*/
 physadr  io_addr;		/*设备控制状态寄存器地址*/
 physadr  io_mba; 		/*MBA配置结构寄存器地址*/
 struct  iostart  *io_stp; 	/*指向部件I/O统计块*/
 time_t  io_start;		/*输入输出启动时间*/
 int  io_s1;		/*驱动程序留用位置*/
 int  io_s2;		/*驱动程序留用位置*/
};

Unix 块设备缓冲区管理 #

块设备缓冲池及其组成

Unix 缓冲区及块设备 I/O 管理

盘块缓冲区的分配与回收

Unix 块设备缓冲管理要旨归纳

Unix 设备驱动程序 #

基本功能任务

管控对象为设备控制器(含三种寄存器)

磁盘驱动程序

磁盘读写程序

基于设备文件的管理方式

Linux 内核模块及设备驱动 #

Linux 内核架构及动态扩展机制 #

Linux 采用了宏内核架构

Linux 内核动态扩展机制

Linux 是一个借鉴了微内核精髓的宏内核结构,Linux 支持模块化的设计、抢占式内核、对内核线程的支持以及动态加载内核模块的能力。不仅如此,Linux 还避免了其微内核设计的性能损失,允许一切运行在内核模式下,直接调用函数,无需消息传递。

所以综合一点来讲,Linux 是一个模块化、多线程和内核可调度的操作系统。

聊了聊宏内核和微内核,并吹了一波 Linux

Linux用户抢占和内核抢占详解(概念, 实现和触发时机)–Linux进程的管理与调度(二十)-腾讯云开发者社区-腾讯云

Linux 最简内核模块 #

#include <linux/init.h>
#include <linux/module.h>

static int __init init_lkm1st_zgs(void)
{
 printk("Welcome to init() for 1st LKM of BJTU_ZGS\n");
 return 0;
}

static void __exit exit_lkm1st_zgs(void)
{
 printk("Goodbye from exit() for 1st LKM of BJTU_ZGS\n");
}

module_init(init_lkm1st_zgs);
module_exit(exit_lkm1st_zgs);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GaoshouZhai@BJTU");
MODULE_DESCRIPTION("First LKM of BJTU_ZGS");
MODULE_ALIAS("LKM1ST_ZGS");

//模块参数声明(可选)
//模块导出符号声明(可选
BASEINCLUDE ?= /lib/modules/`uname -r`/build/

obj-m := FirstZgsLKM.o

all:
	$(MAKE) -C $(BASEINCLUDE) M=$(PWD) modules
clean:
	$(MAKE) -C $(BASEINCLUDE) M=$(PWD) clean

可执行与可链接格式 - 维基百科,自由的百科全书

ELF 文件格式 · Linux Inside 中文版

Linux 传参内核模块 #

Linux 内核模块参数传递

#include <linux/init.h>
#include <linux/module.h>

static int zDebug = 1;
module_param(zDebug, int, 0644);
MODULE_PARM_DESC(zDebug, "Enable to output debugging information");

#define printk_debug(args...) \
 if (zDebug) { \
  printk(KERN_DEBUG args); \
 }

static int __init init_lkm2nd_zgs(void)
{
 printk("Welcome to init() for 2nd LKM of BJTU_ZGS\n");
 printk_debug("Module parameter zDebug = %d\n", zDebug);
 return 0;
}

static void __exit exit_lkm2nd_zgs(void)
{
 printk("Goodbye from exit() for 2nd LKM of BJTU_ZGS\n");
}


module_init(init_lkm2nd_zgs);
module_exit(exit_lkm2nd_zgs);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("GaoshouZhai@BJTU");
MODULE_DESCRIPTION("Second LKM of BJTU_ZGS");
MODULE_ALIAS("LKM2ND_ZGS");

Linux 内核符号共享机制 #

Linux 主体内核以及内核模块间符号联系

Linux 内核符号共享机制

Linux 内核所导出的符号表查看方法

Linux 字符设备驱动 #

Linux 字符设备节点创建

/* FakeCharDevDrv.h */

#ifndef FAKE_CDEV_DRV_H_INCLUDED
#define FAKE_CDEV_DRV_H_INCLUDED

/* **可创建的伪字符设备最多台数,缺省情况下为2台** */
#ifndef NUM_OF_FAKE_CDEVS
#define NUM_OF_FAKE_CDEVS 2    
#endif

/* **用于数据存储的缓冲区大小** */
#ifndef SIZE_OF_BUFFER
#define SIZE_OF_BUFFER 4096
#endif

/* **每次操作可以读写的块大小** */
#ifndef SIZE_OF_BLOCK
#define SIZE_OF_BLOCK 512
#endif

/* **伪字符设备结构体类型** */
struct fake_cdev {
 unsigned char *data;
 unsigned long buffer_size;
 unsigned long block_size;
 struct mutex fake_cdev_mutex;
 struct cdev cdev;
};

#endif
/* FAKE_CDEV_DRV_H_INCLUDED */

Linux设备管理(二)_从cdev_add说起 - Abnor - 博客园

char_traits struct 初步学习-CSDN博客

Linux内核驱动:cdev、misc以及device三者之间的联系和区别 - schips - 博客园

Linux设备驱动程序 之 主次设备号 - AlexAlex - 博客园

从/dev 目录说起

从事 Linux 嵌入式驱动开发的人,都很熟悉下面的一些基础知识, 比如,对于一个 char 类型的设备,我想对其进行 read wirte 和 ioctl 操作,那么:

1、我们通常会在内核驱动中实现一个 file_operations 结构体,然后分配主次设备号,调用 cdev_add 函数进行注册。

2、从 /proc/devices 下面找到注册的设备的主次设备号,在用 mknod /dev/char_dev c major minor 命令行创建设备节点。

3、在用户空间 open /dev/char_dev 这个设备,然后进行各种操作。

OK,字符设备模型就这么简单,很多 ABC 教程都是一个类似的实现。

然后我们去看内核代码时,突然一脸懵逼。。。怎么内核代码里很多常用的驱动的实现不是这个样子的?没看到有 file_operations 结构体,我怎么使用这些驱动?看到了 /dev 目录下有需要的 char 设备,可是怎么使用呢?