linux驱动程序

今天在复习指针的时候,想起了最近要考的驱动程序代码。当我看到函数指针这个东西的时候,突然就想明白一个问题:

驱动程序到底是如何工作的——当我们加载模块后,我们在用户程序中如何访问到了它?

从一个简单的空的字符设备驱动开始,代码结构如下:

头文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/types.h>

用于分配设备号的,用于内核表示字符设备的结构体

int major=0;				//主设备号
int minor=0;				//次设备号
char * name ="em_chrdev";	//驱动设备名,在/proc/devices中出现
struct cdev *cdev;			//内核表示字符设备的结构体
struct file_operations fops = {

	.owner = THIS_MODULE,
	.open = sc_open,
	.release = sc_release,
	.write = sc_write,
	.read = sc_read,
};

初始化函数


退出函数


在初始化函数中我们可以说只做了一件事,那就是初始化cdev结构体。

cdev结构体的定义:(引用自:https://blog.csdn.net/zqixiao_09/article/details/50839042)

[cpp]  view plain  copy
  1. <include/linux/cdev.h>  
  2.   
  3. struct cdev {   
  4.     struct kobject kobj;                  //内嵌的内核对象.  
  5.     struct module *owner;                 //该字符设备所在的内核模块的对象指针.  
  6.     const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.  
  7.     struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.  
  8.     dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.  
  9.     unsigned int count;                   //隶属于同一主设备号的次设备号的个数.  
  10. };  

第一个,不管(因为我也不知道是做什么的)

第二个,设置为THIS_MODUL

第三个,一个file_operations结构体,它用来定义驱动程序操作,如read,write,open。实际上我们的大部分代码都在实现这个结构体的各种方法。

第四个,没有涉及到

第五个,一个代表设备号的结构体,包括主设备号和此设备号,dev_t本身是一个32位数(12位主设备号和20位次设备号)

第六个,次设备号的个数

因此我们看到初始化程序是这个样子的

static int __init hello_init(void)
{
	int ret=0;
	
	dev_t dev_no;			//设备号
	dev_no=MKDEV(major,minor);      //一个用来将两个整形数转换为dev_t结构体的宏

	//register device number
	if(major)
	{
		ret=register_chrdev_region(dev_no,1,name);//一个用来注册字符设备编号的函数,
		if(ret<0)
		{
			printk(KERN_ALERT"register_chrdev_region error\n");
			goto out;
		}
	}
	else
	{
		ret=alloc_chrdev_region(&dev_no,0,1,name);
		if(ret<0)
		{
				printk(KERN_ALERT"alloc_chrdev_region error\n");
			goto out;
		}
		major =MAJOR(dev_no);
	}

	//注册设备结构体
	cdev =cdev_alloc();//为cdev分配内存空间
	if(cdev==NULL)
	{
		printk(KERN_ALERT"cdev_alloc error\n");
		ret=ENOMEM;
		goto out;
	}
	cdev->ops=&fops;        //对应第三个,将ops赋值为fops
	cdev->owner = THIS_MODULE;//对应第二个,设置所有者字段

	ret=cdev_add(cdev,dev_no,1);//在这一步中完成了第5和第6
	if(ret)
	{
		printk(KERN_ALERT"cdev_add error\n");
		goto out;
	}
	out:
	return ret;
}

而退出函数大体上只调用了一个cdev_del函数,这个函数移除了该字符设备。

其余的代码如下:


结合file_operations结构体的定义:(参考linux/fs.h)

如.open,对应的是一个函数指针:int (*open)(struct inode *,struct file *)。因此我们定义的sc_open函数将会被赋给该指针!!!!

这倒是很像C#语言中的委托,linux系统帮我们设置了一个个接口,而我们对驱动的操作则通过实现这些接口来达成。

因此,当我们在用户程序中 open,read,实际上就是在调用这些函数。



猜你喜欢

转载自blog.csdn.net/Codeeror/article/details/80498343