这段时间在学习linux的内核驱动,写一下我的学习心得。大家一起交流交流。
之前开发都是采用C++、C#、java来开发,第一次采用纯C的语言来开发。因此或多或少都会在开发的时候采用面向对象的思想来思考问题。C语言虽然是面向过程的语言,但是在linux的内核中到处都可以看到其采用面向对象的思想,最典型的就是文件系统,llinux把各种文件系统都可以转换成一种标准的文件系统,提供统一的接口给用户。
另一个例子就是在linux系统中,一切皆文件,对硬件设备的访问、操作都可以像文件一样通过read、write、open等接口(系统调用)来操作。下面结合linux内核驱动开发中的scull例子来详细描述一下。
开发scull的大致步骤如下:
1.获取主次设备号,初始化字符设备。
获取主次设备号这里就不讲了。scull使用scull_dev类型的结构表示设备,其定义:
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
在这里我们可以把cdev看作是基类,scull_dev继承cdev。在C语言中,没有继承机制,因此采用包含一个结构体对象来实现继承。而其他的成员则是在cdev基础上扩展的,是scull_dev自己所拥有的信息(资源)。因而采用面向对象思想来看这一步骤,就是继承cdev字符设备基类,实现一个我们所要开发的字符设备类,即scull_dev.
这一步也很好理解,因为scull_dev也属于字符设备,属于cdev的一种。
2.实现系统调用
在linux系统中,一切皆文件,因而对设备的调用同文件的调用是一样的。在scull中需要定义一个file_operations对象,并在初始化的时候调用cdev_init(&dev->cdev, &scull_fops)绑定这些实现函数到字符设备。
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
其中,scull_read,scull_release等就是我们要实现的函数。采用面向对象的思想话我们可以把这些函数看成是cdev的虚函数(接口,也许可能属于更上一层的接口),是我们继承cdev后需要按照我们自己需求实现的接口。系统调用read后根据虚函数机制,程序变根据虚指针选择scull_dev的scull_read。结合上面的步骤,如果linux中内核代码是C++,我们可以得到scull_dev的C++代码
class cdev
{
public:
cdev();
virtual ~cdev();
virtual int open();//参数我就不写了
virtual int close();
virtual ssize_t read();
.....
private:
//一些其他成员变量或其他
}
class scull_dev:public cdev
{
public:
scull_dev();
int open();//即scull_open
int close();//即scull_close
.....
private:
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
}
因此结合上面二个步骤,我们开发字符设备驱动非常简单,只是要实现一个我们自己需要的字符设备,即生成一个结构体,该结构体包括cdev这个结构体,再实现一些系统调用。在实现这些函数中,要注意一些其他的细节就可以了,比如同步、延时、休眠等。
这里只是采用面向对象的思想来分析字符设备驱动的开发而已,可以使字符设备的开发思路非常明了。