`
mfcai
  • 浏览: 412252 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

内核等待机制学习(一)

 
阅读更多
假设我们在 kernel有一个 buffer,应用可以通过read,write等系统调用来读取或写数据到这个 buffer 里。
如果有一个 app 写数据到 buffer 时,此时 buffer 已经满了。那么如何去处理这种情形呢 ?
第一种,传给 user 一个错误讯息,说 buffer 已经满了,不能再写入。
第二种,将 user 的要求 block 住, 等app将 buffer 数据读走,留出空位时,再让 user 写入资料。
如果采用第二种,我们就要用到wait_queue了。wait_queue 是 kernel 所提供的.


假设这里这个驱动例子将维护一个大小为20的buffer,并加入read和write功能:
当 buffer 中没有数据时,read() 就会被 block 住,直到有数据写入为止。
而当 write buffer 时,如果调用 write() 时,空间已满或写入的数据比 buffer 大时,就会被 block 住,
直到有A数据读出来为止。在 write buffer 的程序代码中,我们使用 wait_queue 来做到 block IO 的功能。


今天主要先建立linux驱动程序的框架
一、linux驱动程序框架
第1步:建立Linux驱动程序框架(装载和卸载Linux驱动)
     任何类型的程序都有一个基本的结构,例如,C语言需要有一个入口函数main。Linux驱动程序也不例外。Linux内核在使用驱动时首先需要装载驱动。
在装载过程中需要进行一些初始化工作,例如,建立设备文件,分配内存地址空间等。当Linux系统退出时需要卸载Linux驱动,在卸载的过程中需要释放
由Linux驱动占用的资源,例如,删除设备文件、释放内存地址空间等。在Linux驱动程序中需要提供两个函数来分别处理驱动初始化和退出的工作。这两个函数分别用module_init和module_exit宏指定。
Linux驱动程序一般都都需要指定这两个函数,因此包含这两个函数以及指定这两个函数的两个宏的C程序文件也可看作是Linux驱动的骨架。

第2步:注册和注销设备文件
      任何一个Linux驱动都需要有一个设备文件。否则应用程序将无法与驱动程序交互。建立设备文件的工作一般在第1步编写的处理Linux初始化工作的函数
中完成。删除设备文件一般在第1步编写的处理Linux退出工作的函数中完成。

第3步:指定与驱动相关的信息
     驱动程序是自描述的。例如,可以通过modinfo命令获取驱动程序的作者姓名、使用的开源协议、别名、驱动描述等信息。这些信息都需要在驱动源代
码中指定。通过 MODULE_AUTHOR、MODULE_LICENSE 、MODULE_ALIAS 、MODULE_DESCRIPTION等宏可以指定与驱动相关的信息。

第4步:定义能在设备上进行的操作
struct file_operations,函数指针的集合,定义能在设备上进行的操作。
当执行exampledev_init时,它将调用内核函数 register_chrdev,
把驱动程序的基本入口点指针存放在内核的字符设备地址表中,在用户进程对该设备执行系统调用时提供入口地址


二.模块    
linux中的驱动程序,是以模块的形式编写的。模块的加载方式有两种:
1)用insmod命令手工加载模块。
2)一种直接编译进内核。
模块的卸载通过rmmod命令来删除模块。
模块的实现机制:
对于每一个内核模块来说,必定包含两个函数:卸载函数和初始化函数
1、将模块插入到内核时的初始化工作
1)准备struct file_operations结构体;
2)使用register_chrdev_region注册,申请设备号;
3)使用cdev_init初始化struct cdev结构体,建立cdev和file_operation之间的连接;
4)使用cdev_add增加struct cdev结构体;
5)出错处理。
1.2 从内核中删除模块时的清理工作
1)使用cdev_del删除struct cdev结构体;
2)释放其他申请的资源;
3)使用unregister_chrdev_region释放设备号。


三、设备注册
在linux2.6内核中,字符设备使用struct cdev结构来描述。
本文中,字符设备的注册分为两个步骤:
1、初始化cdev结构
   struct cdev的初始化使用cdev_init来完成
   syntax:
   void cdev_init(struct cdev *cdev,struct file_operations *fops)
   参数:
   cdev:待初始化的cdev结构
   fops:设备对应的操作函数集
2、添加cdev
   struct cdev的注册使用cdev_add来完成
   syntax:
   int cdev_add(struct cdev *p,dev_t dev,unsigned count)
   参数:
   p:要注册的字符设备
   dev:设备号,驱动程序对应的主设备号
   count:添加的设备个数


以下是代码
globalfifo.c
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>


#define GLOBALFIFO_SIZE 20
#define GLOBALFIFO_MAJOR 150    
static int globalfifo_major = GLOBALFIFO_MAJOR;


struct globalfifo_dev                                     
{                                                        
struct cdev cdev;                       
  unsigned int current_len;    
  unsigned char mem[GLOBALFIFO_SIZE];                
  
};


struct globalfifo_dev *globalfifo_devp;
int globalfifo_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalfifo_devp;
return 0;
}


int globalfifo_release(struct inode *inode, struct file *filp)
{
return 0;
}


static const struct file_operations globalfifo_fops =
{
  .owner = THIS_MODULE,
  .open = globalfifo_open,
  .release = globalfifo_release,
};


static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
int err, devno = MKDEV(globalfifo_major, index);
cdev_init(&dev->cdev, &globalfifo_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &globalfifo_fops;
err = cdev_add(&dev->cdev, devno, 1);
if(err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}


int globalfifo_init(void)
{
  int ret;
  dev_t devno = MKDEV(globalfifo_major, 0);
        ret = register_chrdev_region(devno, 1, "globalfifo");
 
  if(ret < 0)
    return ret;
  globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  if(!globalfifo_devp) 
  {
    ret =  - ENOMEM;
    goto fail_malloc;
  }
memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
globalfifo_setup_cdev(globalfifo_devp, 0);
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
  return ret;
}
void globalfifo_exit(void)
{
cdev_del(&globalfifo_devp->cdev);  
kfree(globalfifo_devp);    
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1);
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(globalfifo_init);
module_exit(globalfifo_exit);




本文欢迎转载,转载请注明出处与作者
出处:http://blog.sina.com.cn/staratsky
作者:流星
0
0
分享到:
评论

相关推荐

    \Linux内核机制之等待队列

    等待队列(Wait Queue)作为Linux内核机制的重要组成部分,为进程提供了一种有效的方式来处理异步事件或共享资源的获取。本文将深入探讨等待队列的概念、数据结构以及其实现细节。 #### 二、等待队列基础概念 等待...

    操作系统同步研究——Linux内核同步机制.pdf

    3. 等待队列和异步信号:等待队列是一个基本的功能单元,在 Linux 内核中广泛应用于异步事件通知机制。它可以用来实现核心的异步事件通知机制,例如在核心运行过程中,因某些条件不满足而需要挂起当前线程,直至条件...

    linux内核驱动学习

    在需要超时机制的延时中,可以利用等待队列和相关函数,如`wait_event_timeout()`或`wait_event_interruptible_timeout()`,这些函数使进程在等待特定事件的同时,设置了超时限制。超时以jiffies表示,如果超时,...

    操作系统课程设计 内核定时器机制

    在操作系统中,内核定时器是一项至关重要的机制,它在系统调度、进程管理、中断处理等多个方面起着基础性作用。这篇“操作系统课程设计:内核定时器机制”将深入探讨Linux操作系统内核定时器的实现原理和应用。 ...

    内核驱动基础学习资料及其源码.zip

    3. **内核编程接口**:熟悉内核提供的API,如kmalloc/kfree(内存分配与释放)、spinlock(锁机制)和wait_queue(等待队列),这些都是编写驱动时会用到的。 4. **模块化编程**:Linux内核支持模块化,这意味着...

    Linux内核入门学习,适合初学者

    总的来说,Linux内核的学习是一个循序渐进的过程,从基础概念入手,逐步深入到具体实现细节。随着对内核理解的加深,你将能够更好地优化系统性能,解决系统问题,甚至参与到开源社区的开发工作中去。

    Linux内核驱动fsync机制实现图解.docx

    aio是一个比较新的框架,较为复杂,而fasync是另一种实现方式,在《深入Linux设备驱动程序内核机制》中有详细的讲解和实验。 fasync机制是通过内核发送出的SIGIO信号来实现通知机制的,并不是通过休眠唤醒。为了...

    linux内核模块编程

    Linux内核模块编程是深入Linux操作系统核心的一种编程实践,它允许开发者动态地向内核添加代码(模块),而无需重新编译整个内核。这种机制极大地提高了系统的可扩展性和灵活性,使得增加、更新或删除特定功能成为...

    Linux内核内存Cache机制原理

    Linux内核内存Cache机制是操作系统优化性能的关键组成部分,它通过减少对主存的访问次数,提高了数据读取和写入的速度。在Linux系统中,内存Cache主要分为两种类型...对于Linux内核的学习者来说,这是一项重要的技能。

    11. 等待多个内核对象.zip

    在FreeRTOS操作系统中,"等待多个内核对象"是一个关键的概念,它涉及到任务调度、同步和通信机制。FreeRTOS是一个实时操作系统(RTOS),专为嵌入式系统设计,提供了丰富的功能,包括任务管理、信号量、互斥锁、事件...

    Linux内核完全注释2.01(带书签)

    《Linux内核完全注释2.01》是一本针对Linux 0.11内核的详细注释版,由赵炯编著,特别适合初学者了解和学习Linux内核的工作原理。 在Linux内核的早期版本中,0.11是一个重要的里程碑,它为后续版本的发展奠定了基础...

    epoll内核代码学习

    【epoll内核代码学习】 epoll是一种高效、可扩展的I/O事件通知机制,用于Linux内核中,尤其适用于高并发的网络编程。...学习epoll的内核代码有助于理解其底层原理,从而更好地优化和利用这一机制。

    linux内核分析 0.11版内核完全注释

    0.11版虽然较早,但对于理解内核基本结构和机制具有重要意义,它是Linux发展史上的一个里程碑,许多基础设计和思想至今仍被沿用。 在内核启动部分,书中详细阐述了如何从BIOS引导,加载bootloader(如LILO或GRUB)...

    windows内核部分源码分析

    总结,Windows内核源码分析是一项深度学习的过程,涉及到操作系统理论、硬件交互、内存管理等多个方面。通过学习和理解内核源码,开发者可以更有效地进行系统优化和驱动开发,从而提升软件性能和稳定性。

    linux内核总结,一份详细的资料

    Linux内核是开源操作系统的核心,它的开放源代码性质使得全世界的开发者都能参与其改进和优化。...对于想要深入理解操作系统原理和Linux技术的开发者来说,研究Linux内核是一个极具价值的学习过程。

    ucosii内核学习笔记.zip

    这篇“UCOSII内核学习笔记”文档,无疑是一个深入理解并掌握UCOSII操作系统的宝贵资源。 首先,UCOSII的核心概念是任务(Task)。任务是UCOSII中执行的基本单元,每个任务都有自己的堆栈和独立的执行路径。任务之间...

    ucos内核学习PPT

    这份“uCOS内核学习PPT”集合了多个关键内核功能的实验讲解,为深入理解其工作原理提供了宝贵的资源。 首先,我们从“内核实验一(任务管理).ppt”开始,任务管理是RTOS的基础,它包括任务创建、删除、挂起、恢复和...

    ucos 内核工作原理

    ### uCOS内核工作原理深入...通过对任务状态、互斥锁定、事件类型验证、任务挂起与唤醒机制的深入分析,我们不仅能够理解uCOS内核的基本运作原理,还能学习到如何在嵌入式系统中有效管理资源和任务之间的同步与通信。

    Linux内核视频教程(70G百度网盘资源)

    3. **内核编译与配置**:学习如何通过`make menuconfig`或`make xconfig`进行内核配置,选择需要的模块,以及如何编译和安装自定义内核。 4. **进程管理**:理解进程的概念,包括进程的创建、销毁、状态转换(如...

    windows内核分析(上)

    1. **内核对象的作用**:内核对象的主要作用是提供一种机制,使得用户空间的应用程序能够安全地与操作系统交互,同时也为系统内部的组件之间提供了一种通信方式。 2. **内核对象的生命周期**:内核对象通常在其被...

Global site tag (gtag.js) - Google Analytics