【Linux】内核驱动篇四--IO模型



一、五种IO模型------读写外设数据的方式

  1. 阻塞: 不能操作就睡觉
    在这里插入图片描述
  2. 非阻塞:不能操作就返回错误
    在这里插入图片描述
  3. 多路复用:委托中介监控
    在这里插入图片描述
  4. 信号驱动:让内核如果能操作时发信号,在信号处理函数中操作
    在这里插入图片描述
  5. 异步IO:向内核注册操作请求,内核完成操作后发通知信号
    在这里插入图片描述

二、阻塞与非阻塞

应用层:

  1. open函数的条件O_NONBLOCK指示read、write时是否阻塞
  2. open以后可以由fcntl函数来改变是否阻塞:
flags = fcntl(fd,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

驱动层:通过等待队列

wait_queue_head_t //等待队列头数据类型

init_waitqueue_head(wait_queue_head_t *pwq) //初始化等待队列头
    
wait_event_interruptible(wq,condition)   浅度等待,信号可以唤醒
/*
功能:条件不成立则让任务进入浅度睡眠,直到条件成立醒来
    wq:等待队列头
    condition:C语言表达式
返回:正常唤醒返回0,信号唤醒返回非0(此时读写操作函数应返回-ERESTARTSYS)
*/
        
wait_event(wq,condition) //深度睡眠

wake_up_interruptible(wait_queue_head_t *pwq)  //浅度唤醒
        
wake_up(wait_queue_head_t *pwq)  //深度唤醒
    
    
/*
注:
1. 读、写用不同的等待队列头rq、wq
2. 无数据可读、可写时调用wait_event_interruptible(rq、wq,条件)
3. 写入数据成功时唤醒rq,读出数据成功唤醒wq
*/


三、多路复用

描述符:

  1. 文件描述符:设备文件、管道文件
  2. socket描述符

3.1 应用层:三套接口select、poll、epoll

select:位运算实现 监控的描述符数量有限(32位机1024,64位机2048) 效率差

poll:链表实现,监控的描述符数量不限 效率差

epoll:效率最高,监控的描述符数量不限

三者使用驱动设备都会调用驱动代码内的poll函数

select使用模型:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
/*	功能:监听多个描述符,阻塞等待有一个或者多个文件描述符,准备就绪。
		内核将没有准备就绪的文件描述符,从集合中清掉了。
	参数:  nfds			最大文件描述符数 ,加1
			readfds		读文件描述符集合
			writefds		写文件描述符集合
			exceptfds		其他异常的文件描述符集合
			timeout		超时时间(NULL)
	返回值:当timeout为NULL时返回0,成功:准备好的文件描述的个数  出错:-1 
		   当timeout不为NULL时,如超时设置为0,则select为非阻塞,超时设置 > 0,则无描述符可被操作的情况下阻塞指定长度的时间 
*/
void FD_CLR(int fd, fd_set *set);
//功能:将fd 从集合中清除掉

int  FD_ISSET(int fd, fd_set *set);
//功能:判断fd 是否存在于集合中
 
void FD_SET(int fd, fd_set *set);
//功能:将fd 添加到集合中

void FD_ZERO(fd_set *set);
//功能:将集合清零

//使用模型:

while(1)
{
    /*得到最大的描述符maxfd*/
    /*FD_ZERO清空描述符集合*/
	/*将被监控描述符加到相应集合rfds里  FD_SET*/
    /*设置超时*/
    ret = select(maxfd+1,&rfds,&wfds,NULL,NULL);
    if(ret < 0)
    {
        if(errno == EINTR)//错误时信号引起的
        {
        	continue;   
        }
        else
        {
            break;
        }
    }
    else if(ret == 0)
    {//超时
        //.....
    }
    else
    { //> 0 ret为可被操作的描述符个数
        if(FD_ISSET(fd1,&rfds))
        {//读数据
            //....
        }
        if(FD_ISSET(fd2,&rfds))
        {//读数据
            //....
        }
        ///.....
        if(FD_ISSET(fd1,&wfds))
        {//写数据
            //....
        }
    }
}
   

3.2 驱动层:实现poll函数

poll_waith函数

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)/*
  功能:将等待队列头添加至poll_table表中
  参数:
  struct file :设备文件
  Wait_queue_head_t :等待队列头
  Poll_table :poll_table表
*/

poll函数制造与使用模板:

/*该函数与select、poll、epoll_wait函数相对应,协助这些多路监控函数判断本设备是否有数据可读写*/
unsigned int xxx_poll(struct file *filp, poll_table *wait) //函数名初始化给struct file_operations的成员.poll
{
    unsigned int mask = 0;
    /*
    	1. 将所有等待队列头加入poll_table表中
    	2. 判断是否可读,如可读则mask |= POLLIN | POLLRDNORM;
    	3. 判断是否可写,如可写则mask |= POLLOUT | POLLWRNORM;
    */
    return mask;
}

四、信号驱动

4.1 应用层:信号注册+fcntl

使用模板:

signal(SIGIO, input_handler); //注册信号处理函数

fcntl(fd, F_SETOWN, getpid());//将描述符设置给对应进程,好由描述符获知PID

oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);//将该设备的IO模式设置成信号驱动模式

void input_handler(int signum)//应用自己实现的信号处理函数,在此函数中完成读写
{
    //读数据
}

//应用模板
int main()
{
	int fd = open("/dev/xxxx",O_RDONLY);

	fcntl(fd, F_SETOWN, getpid());

	oflags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, oflags | FASYNC);

	signal(SIGIO,xxxx_handler);

	//......
}
    
void xxxx_handle(int signo)
{//读写数据
    
}


4.2 驱动层:实现fasync函数

制造与使用模板:

/*设备结构中添加如下成员*/
struct fasync_struct *pasync_obj;

/*应用调用fcntl设置FASYNC时调用该函数产生异步通知结构对象,并将其地址设置到设备结构成员中*/
static int xxx_fasync(int fd, struct file *filp, int mode) //函数名初始化给struct file_operations的成员.fasync
{
	struct xxx_device *dev = filp->private_data; 
	return fasync_helper(fd, filp, mode, &dev->pasync_obj);
}

/*写函数中有数据可读时向应用层发信号*/
if(dev->pasync_obj)
       kill_fasync(&dev->pasync_obj, SIGIO, POLL_IN);
       
/*release函数中释放异步通知结构对象*/
if(dev->pasync_obj) 
	fasync_helper(-1, filp, 0, &dev->pasync_obj);

int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **pp);
/*
	功能:产生或释放异步通知结构对象
	参数:
	返回值:成功为>=0,失败负数
*/

void kill_fasync(struct fasync_struct **, int, int);
/*	
	功能:发信号
	参数:
		struct fasync_struct ** 指向保存异步通知结构地址的指针
		int 	信号 SIGIO/SIGKILL/SIGCHLD/SIGCONT/SIGSTOP
		int 	读写信息POLLIN、POLLOUT
*/

实用例子:

/*************************************************************************
  > File Name: mytest1.c
  > Author: xiuchengzhen
  > CSDN: xiuchengzhen.blog.csdn.net
  > Created Time: Tue 10 May 2022 07:29:52 PM PDT
 ************************************************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

#include "mytest.h"

#define BUFSIZE 100 //模拟内核空间大小
int major = 11;   //主设备号
int minor = 0;    //次设备号
int dev_num = 1;  //设备数量

struct pdev 
{
	struct cdev mydev;     //设备结构体
	char dev_buf[BUFSIZE]; //模拟内核空间
	int buf_seek;          //内核空间储存值

	wait_queue_head_t dr;  //等待读队列
	wait_queue_head_t dw;  //等待写队列

	struct fasync_struct *pasync_obj;   //驱动信号对象
};

struct pdev pmydev;

int mydev_open(struct inode *pnode, struct file *pfile)	//打开设备
{
	pfile->private_data = (void *)(container_of(pnode->i_cdev,struct pdev,mydev)); //获取设备结构体地址

	return 0;
}

int mydev_release(struct inode *pnode, struct file *pfile)	//关闭设备
{
	struct pdev *gmydev = (struct pdev *)pfile->private_data;
	
	if(gmydev->pasync_obj != NULL) 
		fasync_helper(-1, pfile, 0, &gmydev->pasync_obj);
	return 0;
}

ssize_t mydev_read(struct file *pfile, char __user *user_buf, size_t user_size, loff_t *seek)	//读设备
{
	int size;   //读取的数据量
	int ret; 
	struct pdev *gmydev = (struct pdev *)pfile->private_data;

	printk("%s\n",__FUNCTION__);
	
	if(gmydev->buf_seek <= 0)
	{
		if(pfile->f_flags & O_NONBLOCK)  //判断是否为阻塞模式
		{
			printk("no data read!\n");
			return -1;
		}
		else
		{
			ret = wait_event_interruptible(gmydev->dr,gmydev->buf_seek>0);  //将信号加入等待队列
			if(ret)   //判断是否为信号唤醒
			{
				printk("sigal wake!\n");
				return -ERESTARTSYS;
			}
		}
	}

	
	printk("----------2-----------\n");
	if(user_size > gmydev->buf_seek)  //量定读取多少
	{
		size = gmydev->buf_seek;
	}
	else
	{
		size = user_size;
	}

	printk("----------3-----------\n");
	ret = copy_to_user(user_buf, gmydev->dev_buf, size);  //读取数据
	if(ret)
	{
		printk("copy_to_user1 fail!");
		return -1;
	}

	printk("----------4-----------\n");
	memcpy(gmydev->dev_buf, gmydev->dev_buf+size, gmydev->buf_seek-size);

	gmydev->buf_seek -= size;
	
	printk("----------5-----------\n");
	wake_up_interruptible(&gmydev->dw);   //唤醒写等待

	printk("----------6-----------\n");
	return size;
}

ssize_t mydev_write(struct file *pfile, const char __user *user_buf, size_t user_size, loff_t *seek)    //写设备
{
	int size;
	int ret;
	struct pdev *gmydev = (struct pdev *)pfile->private_data;

	
	if(gmydev->buf_seek >= BUFSIZE)
	{
		if(pfile->f_flags & O_NONBLOCK)
		{
			printk("buf full!\n");
			return -1;
		}
		else
		{
			ret = wait_event_interruptible(gmydev->dw,gmydev->buf_seek < BUFSIZE);  //加入写等待队列
			if(ret)    //判断是否为信号唤醒
			{
				printk("sigal wake!\n");  
				return -ERESTARTSYS;
			}
		}
	}

	
	if(user_size > BUFSIZE-gmydev->buf_seek)     //量定写入多少
	{
		size = BUFSIZE-gmydev->buf_seek;
	}
	else
	{
		size = user_size;
	}

	ret = copy_from_user(gmydev->dev_buf+gmydev->buf_seek, user_buf, size); //写入数据
	if(ret)
	{
		printk("copy_from_user fail!\n");
		return -1;
	}

	gmydev->buf_seek += size;

	wake_up_interruptible(&gmydev->dr);  //唤醒等待读

	if(gmydev->pasync_obj != NULL)
		kill_fasync(&gmydev->pasync_obj, SIGIO, POLL_IN);  //向app发送读信号

	return size;
}

long mydev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
	int __user *pret = (int *)arg;
	int maxlen = BUFSIZE;
	int ret;
	struct pdev *gmydev = (struct pdev *)pfile->private_data;

	switch(cmd)
	{
		case MYDEV_IOCTL_GET_MUTEX:
			ret = copy_to_user(pret, &maxlen, sizeof(int));
			if(ret)
			{
				printk("copy_to_user1 fail!\n");
				return -1;
			}
			break;
		case MYDEV_IOCTL_GET_SEEK:
			ret = copy_to_user(pret, &gmydev->buf_seek, sizeof(int));
			if(ret)
			{
				printk("copy_to_user2 fail!\n");
				return -1;
			}	
			break;
		default: 
			printk("cmd error!\n");
			return -1;
	}
	return 0;
}

unsigned int mydev_poll(struct file *pfile, poll_table *wait)
{
	struct pdev *gmydev = (struct pdev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile, &gmydev->dr, wait);   //将读等待与写等待加入表内
	poll_wait(pfile, &gmydev->dw, wait);

	if(gmydev->buf_seek > 0)   //判断是否可读
	{
		mask |= POLLIN | POLLRDNORM;
	}

	if(gmydev->buf_seek < BUFSIZE)   //判读是否可写
	{
		mask |= POLLOUT | POLLWRNORM;
	}

	return mask;
}

static int mydev_fasync(int fd, struct file *pfile, int mode) 
{
	struct pdev *gmydev = (struct pdev *)pfile->private_data;

	return fasync_helper(fd, pfile, mode, &gmydev->pasync_obj);  
}

struct file_operations fops ={
	.open = mydev_open,
	.release = mydev_release,
	.read = mydev_read,
	.write = mydev_write,
	.unlocked_ioctl = mydev_ioctl,
	.poll = mydev_poll,
	.fasync = mydev_fasync,
};

int __init mytest1_init(void)
{
	dev_t devno = MKDEV(major, minor);
	int ret = -1;

	/* 申请设备号 */
	if((ret = register_chrdev_region(devno, dev_num, "mytest1")) < 0)
	{
		ret = alloc_chrdev_region(&devno, minor, dev_num, "mytest1");
		if(ret < 0)
		{
			printk("alloc_chrdev_region fail!\n");
			return -1;
		}
		major = MAJOR(devno);
	}

	/* 设备初始化 */
	fops.owner = THIS_MODULE;
	cdev_init(&pmydev.mydev, &fops);
	pmydev.mydev.owner = THIS_MODULE;

	/* 设备添加 */
	cdev_add(&pmydev.mydev, devno, dev_num);

	/* 等待队列初始化 */
	init_waitqueue_head(&pmydev.dr); //初始化读等待队列头
	init_waitqueue_head(&pmydev.dw); //初始化写等待队列头

	return 0;
}

void __exit mytest1_exit(void)
{
	dev_t devno = MKDEV(major, minor);

	/* 注销设备 */
	cdev_del(&pmydev.mydev);

	/* 注销设备号 */
	unregister_chrdev_region(devno, dev_num);
}

MODULE_LICENSE("GPL");
module_init(mytest1_init);
module_exit(mytest1_exit);

到这里就结束啦!
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
目  录 第1章 引言 1 1.1 演进 1 1.2 gnu copyleft 2 1.3 kernel.org 2 1.4 邮件列表和论坛 3 1.5 linux发行版 3 1.6 查看源代码 4 1.7 编译内核 7 1.8 可加载的模块 8 1.9 整装待发 9 第2章 内核 11 2.1 启动过程 11 2.1.1 bios-provided physical ram map 12 2.1.2 758mb lowmem available 14 2.1.3 kernel command line: ro root=/dev/hda1 14 2.1.4 calibrating delay...1197.46 .bogomips (lpj=2394935) 15 2.1.5 checking hlt instruction 16 2.1.6 net: registered protocol family 2 17 2.1.7 freeing initrd memory: 387k freed 17 2.1.8 io scheduler anticipatory registered (default) 18 2.1.9 setting up standard pci resources 18 2.1.10 ext3-fs: mounted filesystem 19 2.1.11 init: version 2.85 booting 19 2.2 内核模式和用户模式 20 2.3 进程上下文和中断上下文 20 2.4 内核定时器 21 2.4.1 hz和jiffies 21 2.4.2 长延时 22 2.4.3 短延时 24 2.4.4 pentium时间戳计数器 24 2.4.5 实时钟 25 2.5 内核中的并发 26 2.5.1 自旋锁和互斥体 26 2.5.2 原子操作 30 2.5.3 读—写锁 31 2.5.4 调试 32 2.6 proc文件系统 32 2.7 内存分配 33 2.8 查看源代码 34 第3章 内核组件 37 3.1 内核线程 37 3.1.1 创建内核线程 37 3.1.2 进程状态和等待队列 41 3.1.3 用户模式辅助程序 42 3.2 辅助接口 43 3.2.1 链表 44 3.2.2 散列链表 49 3.2.3 工作队列 49 3.2.4 通知链 51 3.2.5 完成接口 54 3.2.6 kthread辅助接口 56 3.2.7 错误处理助手 57 3.3 查看源代码 58 第4章 基本概念 61 4.1 设备和驱动程序介绍 61 4.2 中断处理 63 4.2.1 中断上下文 63 4.2.2 分配irq号 64 4.2.3 设备实例:导航杆 65 4.2.4 softirq和tasklet 68 4.3 linux设备模型 71 4.3.1 udev 71 4.3.2 sysfs、kobject和设备类 73 4.3.3 热插拔和冷插拔 76 4.3.4 微码下载 76 4.3.5 模块自动加载 77 4.4 内存屏障 78 4.5 电源管理 79 4.6 查看源代码 79 第5章 字符设备驱动程序 81 5.1 字符设备驱动程序基础 81 5.2 设备实例:系统cmos 82 5.2.1 驱动程序初始化 83 5.2.2 打开与释放 86 5.2.3 数据交换 88 5.2.4 查找 92 5.2.5 控制 94 5.3 检测数据可用性 95 5.3.1 轮询 95 5.3.2 fasync 98 5.4 和并行端口交互 99 5.5 rtc子系统 108 5.6 伪字符驱动程序 109 5.7 混杂驱动程序 110 5.8 字符设备驱动程序注意事项 115 5.9 查看源代码 115 第6章 串行设备驱动程序 118 6.1 层次架构 119 6.2 uart驱动程序 121 6.2.1 设备实例:手机 122 6.2.2 rs-485 132 6.3 tty驱动程序 132 6.4 线路规程 134 6.5 查看源代码 141 第7章 输入设备驱动程序 143 7.1 输入事件驱动程序 144 7.2 输入设备驱动程序 150 7.2.1 serio 150 7.2.2 键盘 150 7.2.3 鼠标 152 7.2.4 触摸控制器 157 7.2.5 加速度传感器 158 7.2.6 输出事件 158 7.3 调试 159 7.4 查看源代码 160 第8章 i2c协议 161 8.1 i2c/smbus是什么 161 8.2 i2c核心 162 8.3 总线事务 164 8.4 设备实例:eeprom 164 8.4.1 初始化 165 8.4.2 探测设备 167 8.4.3 检查适配器的功能 169 8.4.4 访问设备 169 8.4.5 其他函数 170 8.5 设备实例:实时时钟 171 8.6 i2c-dev 174 8.7 使用lm-sensors监控硬件 174 8.8 spi总线 174 8.9 1-wire总线 176 8.10 调试 176 8.11 查看源代码 176 第9章 pcmcia和cf 179 9.1 pcmcia/cf是什么 179 9.2 linux-pcmcia子系统 181 9.3 主机控制器驱动程序 183 9.4 pcmcia核心 183 9.5 驱动程序服务 183 9.6 客户驱动程序 183 9.6.1 数据结构 184 9.6.2 设备实例:pcmcia卡 185 9.7 将零件组装在一起 188 9.8 pcmcia存储 189 9.9 串行pcmcia 189 9.10 调试 191 9.11 查看源代码 191 第10章 pci 193 10.1 pci系列 193 10.2 寻址和识别 195 10.3 访问pci 198 10.3.1 配置区 198 10.3.2 i/o和内存 199 10.4 dma 200 10.5 设备实例:以太网—调制解调器卡 203 10.5.1 初始化和探测 203 10.5.2 数据传输 209 10.6 调试 214 10.7 查看源代码 214 第11章 usb 216 11.1 usb体系架构 216 11.1.1 总线速度 218 11.1.2 主机控制器 218 11.1.3 传输模式 219 11.1.4 寻址 219 11.2 linux-usb子系统 220 11.3 驱动程序的数据结构 221 11.3.1 usb_device结构体 221 11.3.2 urb 222 11.3.3 管道 223 11.3.4 描述符结构 223 11.4 枚举 225 11.5 设备实例:遥测卡 225 11.5.1 初始化和探测过程 226 11.5.2 卡寄存器的访问 230 11.5.3 数据传输 233 11.6 类驱动程序 236 11.6.1 大容量存储设备 236 11.6.2 usb-串行端口转换器 241 11.6.3 人机接口设备 243 11.6.4 蓝牙 243 11.7 gadget驱动程序 243 11.8 调试 244 11.9 查看源代码 245 第12章 视频驱动程序 247 12.1 显示架构 247 12.2 linux视频子系统 249 12.3 显示参数 251 12.4 帧缓冲api 252 12.5 帧缓冲驱动程序 254 12.6 控制台驱动程序 265 12.6.1 设备实例:手机 266 12.6.2 启动logo 270 12.7 调试 270 12.8 查看源代码 271 第13章 音频驱动程序 273 13.1 音频架构 273 13.2 linux声音子系统 275 13.3 设备实例:mp3播放器 277 13.3.1 驱动程序函数和结构体 278 13.3.2 alsa编程 287 13.4 调试 288 13.5 查看源代码 289 第14章 块设备驱动程序 291 14.1 存储技术 291 14.2 linux块i/o层 295 14.3 i/o调度器 295 14.4 块驱动程序数据结构和方法 296 14.5 设备实例:简单存储控制器 298 14.5.1 初始化 299 14.5.2 块设备操作 301 14.5.3 磁盘访问 302 14.6 高级主题 304 14.7 调试 306 14.8 查看源代码 306 第15章 网络接口卡 308 15.1 驱动程序数据结构 308 15.1.1 套接字缓冲区 309 15.1.2 网络设备接口 310 15.1.3 激活 311 15.1.4 数据传输 311 15.1.5 看门狗 311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:以太网nic 316 15.5 isa网络驱动程序 321 15.6 atm 321 15.7 网络吞吐量 322 15.7.1 驱动程序性能 322 15.7.2 协议性能 323 15.8 查看源代码 324 第16章 linux无线设备驱动 326 16.1 蓝牙 327 16.1.1 bluez 328 16.1.2 设备实例:cf卡 329 16.1.3 设备实例:usb适配器 330 16.1.4 rfcomm 331 16.1.5 网络 332 16.1.6 hid 334 16.1.7 音频 334 16.1.8 调试 334 16.1.9 关于源代码 334 16.2 红外 335 16.2.1 linux-irda 335 16.2.2 设备实例:超级i/o芯片 337 16.2.3 设备实例:ir dongle 338 16.2.4 ircomm 340 16.2.5 联网 340 16.2.6 irda套接字 341 16.2.7 lirc 341 16.2.8 查看源代码 342 16.3 wifi 343 16.3.1 配置 343 16.3.2 设备驱动程序 346 16.3.3 查看源代码 347 16.4 蜂窝网络 347 16.4.1 gprs 347 16.4.2 cdma 349 16.5 当前趋势 350 第17章 存储技术设备 352 17.1 什么是闪存 352 17.2 linux-mtd子系统 353 17.3 映射驱动程序 353 17.4 nor芯片驱动程序 358 17.5 nand芯片驱动程序 359 17.6 用户模块 361 17.6.1 块设备模拟 361 17.6.2 字符设备模拟 361 17.6.3 jffs2 362 17.6.4 yaffs2 363 17.7 mtd工具 363 17.8 配置mtd 363 17.9 xip 364 17.10 fwh 364 17.11 调试 367 17.12 查看源代码 367 第18章 嵌入式linux 369 18.1 挑战 369 18.2 元器件选择 370 18.3 工具链 371 18.4 bootloader 372 18.5 内存布局 374 18.6 内核移植 375 18.7 嵌入式驱动程序 376 18.7.1 闪存 377 18.7.2 uart 377 18.7.3 按钮和滚轮 378 18.7.4 pcmcia/cf 378 18.7.5 sd/mmc 378 18.7.6 usb 378 18.7.7 rtc 378 18.7.8 音频 378 18.7.9 触摸屏 379 18.7.10 视频 379 18.7.11 cpld/fpga 379 18.7.12 连接性 379 18.7.13 专用领域电子器件 380 18.7.14 更多驱动程序 380 18.8 根文件系统 380 18.8.1 nfs挂载的根文件系统 381 18.8.2 紧凑型中间件 382 18.9 测试基础设施 383 18.10 调试 383 18.10.1 电路板返工 384 18.10.2 调试器 385 第19章 用户空间的驱动程序 386 19.1 进程调度和响应时间 387 19.1.1 原先的调度器 387 19.1.2 o(1)调度器 387 19.1.3 cfs 388 19.1.4 响应时间 388 19.2 访问i/o区域 390 19.3 访问内存区域 393 19.4 用户模式scsi 395 19.5 用户模式usb 397 19.6 用户模式i2c 400 19.7 uio 401 19.8 查看源代码 402 第20章 其他设备和驱动程序 403 20.1 ecc报告 403 20.2 频率调整 407 20.3 嵌入式控制器 408 20.4 acpi 408 20.5 isa与mca 410 20.6 火线 410 20.7 智能输入/输出 411 20.8 业余无线电 411 20.9 voip 411 20.10 高速互联 412 20.10.1 infiniband 413 20.10.2 rapidio 413 20.10.3 光纤通道 413 20.10.4 iscsi 413 第21章 调试设备驱动程序 414 21.1 kdb 414 21.1.1 进入调试器 415 21.1.2 kdb 415 21.1.3 kgdb 417 21.1.4 gdb 420 21.1.5 jtag调试器 421 21.1.6 下载 423 21.2 内核探测器 423 21.2.1 kprobe 423 21.2.2 jprobe 427 21.2.3 返回探针 429 21.2.4 局限性 431 21.2.5 查看源代码 431 21.3 kexec与kdump 431 21.3.1 kexec 432 21.3.2 kdump与kexec协同工作 432 21.3.3 kdump 433 21.3.4 查看源代码 437 21.4 性能剖析 437 21.4.1 利用oprofile剖析内核性能 438 21.4.2 利用gprof剖析应用程序性能 440 21.5 跟踪 441 21.6 ltp 444 21.7 uml 444 21.8 诊断工具 444 21.9 内核修改配置选项 444 21.10 测试设备 445 第22章 维护与发布 446 22.1 代码风格 446 22.2 修改标记 446 22.3 版本控制 447 22.4 一致性检查 447 22.5 构建脚本 448 22.6 可移植代码 450 第23章 结束语 451 23.1 流程一览表 451 23.2 下一步该做什么 452 附录a linux汇编 453 附录b linux与bios 457 附录c seq文件 461

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修成真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值