Chinaunix首页 | 论坛 | 博客
  • 博客访问: 111560
  • 博文数量: 35
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 287
  • 用 户 组: 普通用户
  • 注册时间: 2015-08-01 19:56
文章分类
文章存档

2017年(6)

2016年(21)

2015年(8)

我的朋友

分类: 嵌入式

2016-03-17 23:50:00

linux中架构
中断是指CPU在执行程序的过程中,出现了突发事件。CPU必须停止执行当前的程序,转去处理突发事件。处理完毕后CPU又返回原程序被中断的位置继续执行。在大多数系统中。当中断的到来。要进行大量的耗时的处理。所以linux内核的中断处理机制是一个分层机制。分为:顶半部 和 底半步。  
顶半部:完成尽可能少的紧急的功能,比如简单的读取寄存器中的中断状态并清除中断标志后进行"登记中断"工作。"登记中断"就是将底半部处理程序挂到该设备的底半部执行队列中。
底半部:中断的重心工作。通常底半部来完成中断事件的绝大多数任务。而且可以被新的中断所打断。这就是顶和底的不同。 

linux中断处理流程
1.获取中断号
2.根据中断号找到irq_desc[irq]
3.根据irq_desc[irq]结构中的action取出中断处理函数

linux中断程序设计
1.request_irq注册中断
nt request_irq(unsigned int irq, 
void (*handler)(int irq, void *dev_id, struct pt_regs *regs ), 
unsigned long irqflags
const char * devname, 
void *dev_id); 

unsigned int irq :申请的中断号
void (*handler)(int irq, void *dev_id, struct pt_regs *regs):是向系统登记的中断处理函数(顶半部)这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device id,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。
unsigned long irqflags是中断处理的属性,可以指定中断的触发方式以及处理方式。 [IRQF_DISABLED设置为快速中断 IRQF_SHARED表示多个设备共享中断]
const char * devname:设置中断名称,为注册的驱动程序的设备名
void *dev_id:在中断共享时会用到。一般设置为这个设备的设备结构本身或者NULL,共享的同一个中断号下不同的中断处理程序用dev_id来标注中断处理程序可以用dev_id找到相应的控制这个中断的设备
返回值:返回0 表示 成 功 ,或者 返回一 个 错误码。

2:中断处理程序
1)检查设备是否产生了中断
2)清楚中断产生的标志
3)相应的硬件操作

3:注销中断
void free_irq(unsigned int irq,void *dev_id)
unsigned int irq:申请的中断号
void *dev_id在中断共享时会用到。一般设置为这个设备的设备结构本身或者NULL,共享的下不同的中断处理程序用dev_id来标注,中断处理程序可以用dev_id找到相应的控制这个中断的设备


Linux中断嵌套处理:
概念:当一种类型的中断发生时又产生另外一种中断
    慢速中断:当处理中断函数时,中断总开关是不关闭的,允许别的中断产生。
1.不同中断设备:处理中断开始。。。。。。发生另外中断,执行发生的中断,执行完成,返回之前的中断处理函数继续执行。。。。
2.相同类型设备:处理中断开始。。。。。。发生另外中断,忽略发生的中断,继续执行之前的中断函数,造成中断丢失。。。。
    快速中断:当处理中断函数时,中断总开关(IF关闭),不接受外部中断。
1.不同中断设备:处理中断开始。。。。。。发生另外中断,忽略发生的中断,继续执行之前的中断函数,造成中断丢失。。。。
2.相同类型设备:处理中断开始。。。。。。发生另外中断,忽略发生的中断,继续执行之前的中断函数,造成中断丢失。。。。
 
linux中断分层:
作用:减少中断处理函数执行时间,减少中断丢失的概率
中断处理函数中相关代码:
   上半部 1.与硬件相关代码
   下半部 2.与硬件不相关代码
使用中断分层技术:中断处理函数中第一部分在中断函数中执行,第二部使用中断分层技术隔离出来。
中断分层方式:软中断、tasklet、工作队列(主流)

工作队列是一种将任务推后执行的形式,他把推后的任务交由一个内核线程去执行。这样如果在中断函数中使用中断分层(工作队列方式),中断函数的第二部分会在进程上下文执行,它允许重新调度甚至睡眠。每个被推后的任务叫做“工作”,由这些工作组成的队列称为工作队列。
当工作队列中的工作被执行后就会把被执行的工作在工作队列链表中删除此工作

首先中断处理程序按照工作队列的格式把下半部线程的一个工作挂载到处理链表上去;然后内核线程在适当的时候会扫描链表,把工作拿来执行。这样就尽量减少了中断丢失的可能。

使用工作队列:
内核使用struct workqueue_struct来描述一个工作队列;
内核使用struct work_struct来描述一个工作项;

1.创建工作队列:create_workqueue;
struct workqueue_struct *create_workqueue(const char *name);
2.创建工作:INIT_WORK;
INIT_WORK(_work, _func);
可以理解为INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func

3.提交工作:queue_work,提交工作后并不是马上运行,由内核空闲时内核线程会运行创建的工作队列
int queue_work(struct workqueue_struct *wq, struct work_struct *work);

创建工作队列首先要遵守“GPL”协议,不然会报错

在大多数情况下,驱动并不需要自己创建工作队列,只需要定义工作,然后将工作提交到内核已经定义好的工作队列Keventd_wq中。
提交工作到默认队列:schedule_work

  1. /**************************************************
  2. 使用内核已定义好的,默认的工作队列keventd_wq中
  3. 使用:
  4.     schedule_work
  5. ***************************************************/

  6. #include <linux/init.h>
  7. #include <linux/module.h>
  8. #include <linux/slab.h>


  9. struct workqueue_struct *my_wq; //使用workqueue_struct来描述工作队列
  10. struct work_struct *work1; //使用work_struct来描述一个工作
  11. struct work_struct *work2;


  12. void work1_func(struct work_struct *work)
  13. {
  14.    printk("this is work1\n");

  15. }

  16. void work2_func(struct work_struct *work)
  17. {
  18.    printk("this is work2\n");

  19. }


  20. int init_que(void)
  21. {
  22.     
  23.     
  24.     //创建工作
  25.     work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); //给指针分配空间
  26.     
  27.     INIT_WORK(work1, work1_func); // 创建一个work1_func工作
  28.     INIT_WORK(work2, work2_func);
  29.     
  30.     
  31.     //挂载(提交)工作给默认的工作队列keventd_wq
  32.     schedule_work(work1);
  33.     schedule_work(work2);
  34.         
  35.     return 0;
  36. }

  37. void clean_que(void)
  38. {
  39.     
  40.         
  41. }
  42.   

  43. MODULE_LICENSE("GPL");
  44. module_init(init_que);
  45. module_exit(clean_que);




  1. /*********************************************
  2. 使用自己创建工作队列
  3. **********************************************/


  4. /*#include <linux/init.h>
  5. #include <linux/module.h>
  6. #include <linux/slab.h>


  7. struct workqueue_struct *my_wq; //使用workqueue_struct来描述工作队列
  8. struct work_struct *work1; //使用work_struct来描述一个工作
  9. struct work_struct *work2;


  10. void work1_func(struct work_struct *work)
  11. {
  12.    printk("this is work1\n");

  13. }

  14. void work2_func(struct work_struct *work)
  15. {
  16.    printk("this is work2\n");

  17. }


  18. int init_que(void)
  19. {
  20.     //创建工作队列
  21.     my_wq = create_workqueue("my_wq");
  22.     
  23.     //创建工作
  24.     work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); //给指针分配空间
  25.     
  26.     INIT_WORK(work1, work1_func); // 创建一个work1_func工作
  27.     INIT_WORK(work2, work2_func);
  28.     
  29.     
  30.     //挂载(提交)工作
  31.     queue_work(my_wq,work1);//参数:工作队列指针,创建好的工作指针
  32.     queue_work(my_wq,work2) ;
  33.         
  34.     return 0;
  35. }

  36. void clean_que(void)
  37. {
  38.     
  39.         
  40. }
  41.  
  42. MODULE_LICENSE("GPL");
  43. module_init(init_que);
  44. module_exit(clean_que);*/











链表:
链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。它可以根据需要开辟内存单元。链表有一个“头指针”变量,以head表示,它存放一个地址。该地址指向一个元素。链表中每一个元素称为“结点”,每个结点都应包括两个部分:一为用户需要用的实际数据,二为下一个结点的地址。因此,head指向第一个元素:第一个元素又指向第二个元素;……,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。

进程和线程的理解:
进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。
在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定。线程的运行中需要使用计算机的内存资源和CPU。

进程是不干活的,是给线程执行任务提供空间和资源的。线程才是执行任务的最小单位。一个进程内有很多的线程在执行任务,关闭一个进程就会杀死进程内的所有线程。但关闭一个线程不会影响进程内别的线程。
阅读(1349) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~