原文:十天学Linux内核之第四天---如何处理输入输出操作

  真的是悲喜交加呀,本来这个寒假早上8点都去练车,两个小时之后再来实验室陪伴Linux内核,但是今天教练说没名额考试了,好纠结,不过想想就可以睡懒觉了,哈哈,自从大三寒假以来还没睡过懒觉呢,现在也有更多的时间来分享自己学习Linux内核的感受,前几天觉得就是自己也有些不懂的,相信大家看了也是很模糊,以后我会标志出来自己不懂的,希望大神们指教,也希望大家多多指点,共同攻克Linux内核,今天将讲到处理器是如何与其它设备进行交互的,内核又是如何响应和控制这些交互的,今天内容不多但是很关键,写的不好希望大家批评,纯手打。

  Linux内核是如何将软硬件结合起来的呢?这里我们将一起探究内核与周围硬件主要是文件IO和硬件设备之间的关系,来解释这个问题。处理器与周围设备的通信依赖于一系列的电路电线,总线就是具有类似功能的电线,设备与处理器通信主要是通过地址总线,数据总线,控制总线来实现,这里在学习单片机原理的时候也提到过,这里对系统的基本结构就不多说了,觉得更新快,不好讲解,也没什么好总结的,大家看看相关书籍就行。了解到设备可以当做文件系统中的文件来处理,其细节都可隐藏在内核中,而对应用程序员透明,当进程对设备文件应用某一系统调用的时候,只要将这一系统调用转换成某种设备函数就足够了,其中设备驱动程序定义了这些函数,接下来看看这些设备类型。其中应用层,文件系统层,通用块设备层和设备驱动程序之间的关系如下图,这里贴出来供大家了解一下。读写块设备如下:

  先介绍块设备,设备驱动在驱动程序初始化时为自己注册,将这个驱动程序加入内核的驱动程序表中,并将设备号映射到数据结构block_device_operations中,数据结构block_device_operations包含了系统中启动和停止给设定块设备的函数(在include/linux/fs.h上。

struct block_operations{
int (*open) (struct inode *,struct file *);
int (*release) (struct inode *,struct file*);  //open()和release()都是同步的
int (*ioctl) (struct node *,struct file *,unsigned, unsigned long);
int (*media_changed) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
struct module *owner;
};

  从处理器的角度来看,在适合的cidao4上定位磁头并将磁盘转到相应的块要花费相当长的时间,这种延迟迫使内核实现了系统请求队列,在Linux2.6中,每个块设备都有自己的请求队列,以便管理对该设备的IO口请求,进程只有在获得请求队列锁之后才能 更新设备的请求队列,让我们先来看看request_queue结构(这些代码都是自己敲出来的,然后分析,分析不好的请各位大神批评改正。)这些代码都可以在include/linux/blkdev.h中查看。

struct request_queue
{
struct list_head queue_head;  //指向请求队列对首的指针
struct request *last_merge;  //加到请求队列的最后一个请求
elevator_t elevator;      //这个不懂,求大神指教
struct request_list rq;     //由两个wait_queue组成,分别用于块设备读请求队列和写请求队列
...
request_fn_proc *request_fn;
merge_request_fn *back_merge_fn;
merge_request_fn *front_merge_fn;
merge_requests_fn *merge_requests_fn;
make_request_fn *make_request_fn;
prep_rq_fn *prep_rq_fn;
unplug_fn *unplug_fn;
merge_bvec_fn *merge_bvec_fn;
activity_fn *activity_fn;      //定义调度程序相关函数来控制如何管理块设备的请求
...
struct time_list unplug_timer;
int unplug_thresh;
unsigned long unplug_delay;
struct work_struct unplug_work; struct backing_dev_info backing_dev_info;    //用于去掉设备的IO调度函数 ...
void *queuedata; void *activity_data;  //这些是对设备和设备驱动程序相关的队列进行管理 ...
unsigned long bounce_pfn;
int bounce_gfp;    //是指内核将高端内存缓存冲区的IO请求copy到低端内存缓冲区去 unsigned long queue_flags;//变量queue_flags存储一个或者多个队列标志,参见下表格。
标志名称 功能
QUEUE_FLAG_CLUSTER 将介几个段合成一个 
QUEUE_FLAG_QUEUED 使用通用标志队列
QUEUE_FLAG_STOPPED 队列被停止
QUEUE_FLAG_READFULL 读队列已经满了
QUEUE_FLAG_WRITEFULL 写队列已经满了
QUEUE_FLAG_DEAD 队列被撤销
QUEUE_FLAG_REENTER 避免重入
QUEUE_FLAG_PLUGGED 插入队列
spinlock_t *queue_lock;

struct kobject kobj;

unsigned long nr_requests;  
unsigned int nr_congestion_on;
unsigned int nr_congestion_off;
unsigned short max_sectors;
unsigned short max_phys_segments;
unsigned short max_hw_segments;
unsigned short hardsect_size;
unsigned int max_segment_size; unsigned long seg_boundary_mask;
unsigned long dma_alignment; struct blk_queue_tag *queue_tags; atomic_t refcnt; unsigned int in_flight; unsigned int sg_timeout;
unsigned int sg_reserved_size;  //前面这些变量定义了请求队列中可管理的资源
};

  Linux内核通过在设备的_init函数中调用下列函数来初始化块设备的请求队列,这些函数中,,可以看出请求对了内部的细节和相关帮助教程,在现在的Linux2.6内核中,每个块设备控制自己的锁,并且将自旋锁作为第二个参数来传递,其中第一个参数是块设备驱动程序提供的请求函数,下面的代码在drivers/block/11_rw_blk.c中查看得到。

request_queue_t *blk_init_queue(request_fn_proc *rfn,spinlock_t *lock)
{
request_queue_t *q;
static int printed; q = blk_alloc_queue(GFP_KERNEL);  //从内核的内存中分配空间队列,内容为0
if(!q)
return NULL; if(blk_init_free_list(q))  //初始化请求清单
goto out_init;    //表示一直怕goto,懂但不会用 if(!printed){
printed = ;
printk("Using %s io scheduler\n",chosen_elevator->elevator_name);
} if(elevator_init(q,chosen_elevator))  //这是个初始化的函数
goto out_elv; q->request_fn = rfn;
q->back_merge_fn = 11_back_merge_fn;
q->front_merge_fn = 11_front_merge_fn;
q->merge_requests_fn = 11_merge_requests_fn;
q->prep_rq_fn = NULL;
q->unplug_fn = generic_unplug_device;
q->queue_flags = (<<QUEUE_FLAG_CLUSTER);
q->queue_lock = lock;    //上述赋值是将电梯调度程序相关的函数与该队列关联 blk_queue_segment_boundary(q,0xffffffff);  //检查是否满足最小尺寸 blk_queue_make_request(q,__make_request)  //设置驱动从队列删除
blk_queue_max_segment_size(q,MAX_SEGMENT_SIZE);  //初始化归并段的上限 blk_queue_max_hw_segments(q,MAX_HW_SEGMENTS);  //初始化物理设备可以处理的最大段数
blk_queue_max_phys_segments(q,MAX_PHYS_SEGMENTS);  初始化每一请求的最大物理数目段 return q;  //返回已经初始化的队列
out_elv:
blk_cleanup_queue(q);
out_init:
kmem_cache_free(requestq_cachep,q);
return NULL;  //错误事件中清除内存的一个例程
}

  

  代码实在太难,我知道的也只是皮毛,那些都需要好好体会,如若有补充的希望各路大神能够多加改正我的缺点,现在来看看设备操作,基本 的通用块设备有open,close,ioctl以及request函数,请求队列不能直接被访问,但是可以通过一组帮助例程来访问,如下:

struct request *elv_next_request(request_queue_t *q)

这个帮助函数返回并指向下一个请求结构的指针,驱动程序可以通过查看该元素来收集所有信息,以确定它的的大小方向以及该请求相关的任何其他自定义操作,之后通过end_request()想内核报告这一信息:

void end_request(struct request *req,int uptodate)  //在请求队列中传递elev_next_request()获得的参数
{
if(!end_that_request_first(req,uptpdate,req->hrad_cur_sectors))  //传输适合的扇区数
{
add_disk_randomness(req->rq_disk);  //加入系统熵池,表示不懂,求大神指教,
blkdev_dequeue_request(req);  //删除请求结构
end_that_request_last(req);  //收集统计信息并且释放可用的数据结构
}
}

  下面来介绍一下其它各种设备,与块设备不同,字符设备用来传送数据流,所有串行设备都是字符设备,与字符设备类似,网络设备的数据在物理层上串行传输,而时钟设备是基于硬件脉搏跳动的设备,其实就是时钟相关的,还有那终端设备,这里就稍微提及一下。因为这些都和输入输出相关,大家只要有个印象就行了。

  

  小结

  结束了分析代码之旅,小结一下今天主要的内容,今天主要分享的是Linux内核是如何处理输入输出操作的,具体讨论了Linux是如何表示块设备和它的接口的,也介绍了Linux调度程序并且重点分析了请求队列,上述敲的代码,我也还有好多不懂,只能自己慢慢去体会了,希望各路大神看了之后能够稍加提醒一下,哎,反正这个寒假没啥事了,就一直和内核作伴吧,这些写的不好,以后继续努力,fighting~

  版权所有,转载请注明转载地址:http://www.cnblogs.com/lihuidashen/p/4244330.html

十天学Linux内核之第四天---如何处理输入输出操作的更多相关文章

  1. 十天学Linux内核之第九天---向内核添加代码

    原文:十天学Linux内核之第九天---向内核添加代码 睡了个好觉,很晚才起,好久没有这么舒服过了,今天的任务不重,所以压力不大,呵呵,现在的天气真的好冷,不过实验室有空调,我还是喜欢待在这里,有一种 ...

  2. 十天学Linux内核之第七天---电源开和关时都发生了什么

    原文:十天学Linux内核之第七天---电源开和关时都发生了什么 说实话感觉自己快写不下去了,其一是有些勉强跟不上来,其二是感觉自己越写越差,刚开始可能是新鲜感以及很多读者的鼓励,现在就是想快点完成自 ...

  3. 十天学Linux内核之第五天---有关Linux文件系统实现的问题

    原文:十天学Linux内核之第五天---有关Linux文件系统实现的问题 有时间睡懒觉了,却还是五点多醒了,不过一直躺倒九点多才算起来,昨晚一直在弄飞凌的嵌入式开发板,有些问题没解决,自己电脑系统的问 ...

  4. 十天学Linux内核之第十天---总结篇(kconfig和Makefile & 讲不出再见)

    原文:十天学Linux内核之第十天---总结篇(kconfig和Makefile & 讲不出再见) 非常开心能够和大家一起分享这些,让我受益匪浅,感激之情也溢于言表,,code monkey的 ...

  5. 十天学Linux内核之第八天---构建Linux内核

    原文:十天学Linux内核之第八天---构建Linux内核 今天是腊八节,说好的女票要给我做的腊八粥就这样泡汤了,好伤心,好心酸呀,看来代码写久了真的是惹人烦滴,所以告诫各位技术男敲醒警钟,不要想我看 ...

  6. 十天学Linux内核之第六天---调度和内核同步

    原文:十天学Linux内核之第六天---调度和内核同步 心情大好,昨晚我们实验室老大和我们聊了好久,作为已经在实验室待了快两年的大三工科男来说,老师让我们不要成为那种技术狗,代码工,说多了都是泪啊,, ...

  7. 十天学Linux内核之第三天---内存管理方式

    原文:十天学Linux内核之第三天---内存管理方式 昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今 ...

  8. 十天学Linux内核之第二天---进程

    原文:十天学Linux内核之第二天---进程 都说这个主题不错,连我自己都觉得有点过大了,不过我想我还是得坚持下去,努力在有限的时间里学习到Linux内核的奥秘,也希望大家多指点,让我更有进步.今天讲 ...

  9. 十天学Linux内核之第一天---内核探索工具类

    原文:十天学Linux内核之第一天---内核探索工具类 寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间 ...

随机推荐

  1. iOS 删除黑色边框线导航栏&amp;删除搜索框的阴影边界线和中黑色文本输入框 - 解

    删除黑色边框线导航栏 in viewDidload: [self.navigationController.navigationBar setBackgroundImage:[[UIImage all ...

  2. 采用PopupWin控制消息推送功能

    最近的项目需要,急需实现消息推送功能.接连试了很多办法,让我们用JavaScript为了实现啊,其效果是不咋好,最后,我发现了一个PopupWin这个,看着眼前的成绩不错,开始使用. 1.准备工作.先 ...

  3. 【iOS】随机三角瓷砖布局算法

    你已经看够iOS鉴于这些默认的正方形块,整齐地显示? 本篇给出一个随机算法设计的三角布局的瓷砖和实施. 这样的规则,并提出妥协随机排列间.它看起来很凌乱,不会有一个新事物. 重点是设计和实施,以实现布 ...

  4. 用SourceTree轻巧Git项目图解

    用SourceTree轻松Git项目图解 这篇文档的目的是:让使用Git更轻松. 看完这篇文档你能做到的是: 1.简单的用Git管理项目. 2.怎样既要开发又要处理发布出去的版本bug情况. Sour ...

  5. UVA 538 - Balancing Bank Accounts(贪心)

    UVA 538 - Balancing Bank Accounts 题目链接 题意:给定一些人的欠钱关系,要求在n-1次内还清钱,问方案 思路:贪心,处理出每一个人最后钱的状态,然后直接每一个人都和最 ...

  6. ASP中文件上传组件ASPUpload介绍和使用方法

    [导读]要实现该功能,就要利用一些特制的文件上传组件.文件上传组件网页非常多,这里介绍国际上非常有名的ASPUpload组件 1 下载和安装ASPUpload  要实现该功能,就要利用一些特制的文件上 ...

  7. cocos2d-x box2d Demo注解

    勤奋努力,持之以恒. 核心概念 Box2D 中有一些主要的对象,这里我们先做一个简要的定义,在随后的文档里会有更具体的描写叙述. 刚体(rigid body) 一块十分坚硬的物质,它上面的不论什么两点 ...

  8. 11gRAC CHM 管理

    Cluster Health Monitor(缩写CHM)是Oracle提供的工具,自己主动的资源来收集操作系统(CPU.内存.SWAP.过程.I/O与网络)用法. CHM数据被收集每秒一次,11.2 ...

  9. 【夯实基础】javakeywordsynchronized 详细说明

    尊重版权:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html Java语言的keyword.当它用来修饰一个方法或者一个代码 ...

  10. Java设计模式(三)-修饰模式

    我们都知道.能够使用两种方式给一个类或者对象加入行为. 一是使用继承.继承是给一个类加入行为的比較有效的途径.通过使用继承,能够使得子类在拥有自身方法的同一时候,还能够拥有父类的方法.可是使用继承是静 ...