系统调用与驱动程序的流程
如下图
通过分析源码,我们可以得出,从我们的测试程序到调用我们的驱动程序,整个过程包含如下:
应用程序通过调用open
函数,该函数由glibc库
提供,作用是执行swi汇编指令
,目的是为了产生中断异常
,使得CPU进入到SVC
模式,也就是我们常说的内核态。
当CPU的权限提升到内核态以后,会调用内核中VFS(虚拟文件系统)
提供的do_sys_open()函数
,VFS也就是我们常说的system_call,为我们的应用程序提供了统一的接口,也使得Linux一切皆文件变成了事实
。通过VFS提供的open函数对应用程序的传入文件、权限进行匹配处理。根据不同的情况调用不同的驱动处理程序。
那么对于我们的字符设备
,VFS采用的方式就是通过查看该设备文件类型是否为c
。并通过该文件的索引节点对象(inode)
,确定该设备文件是否注册其对应的file_operations
结构体,如果注册了,表示该设备文件有其对应的驱动程序,则在内核中创建一个 struct file的结构体来表示文件描述符
。如果没有注册其对应的驱动程序,则返回一个没有被使用的int
型数字,但是对该数字无法操作,因为该文件描述符并没有抽象出设备文件的驱动程序。
因此,当在内核中成功的申请了struct file结构体(文件描述符),我们便可以调用对应的驱动函数中的read、write等函数。而我们写驱动的核心工作就是实现驱动的read,write函数
。
那么如何来写一个最简单的驱动程序呢?
最简单的驱动程序
这个驱动程序很简单,即我们实现对一个设备的读和写就行
注意:这里我们进行在Ubuntu上测试。有兴趣的朋友,可以自行在板子上测试,都一样。
源码
/*驱动程序不同于一般的应用程序,驱动程序的阅读方式是从下往上阅读 * 即从入口函数开始阅读到最后的出口函数 * */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
/*设置主设备号,为什么定义为全局变量呢?就是为了能够在出口函数注销该主设备号*/
static int major=0;
static char buf[4]={
0};
/*简单的实现read、write函数*/
static ssize_t simple_read(struct file *fp, char __user *buffer, size_t len, loff_t *pos){
int ret;
printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
if(len<0){
return -1;
}
if(len>3){
len=3;
}
ret=copy_to_user(buffer,buf,len);
return ret;
}
static ssize_t simple_write(struct file *fp, const char __user *buffer, size_t len, loff_t *pos){
int ret;
printk("%s %s line %d\n",__FILE__,__FUNCTION__ ,__LINE__);
if(len<0){
return -1;
}
if(len>3){
len=3;
}
ret=copy_from_user(buf,buffer,len);
return ret;
}
/*定义文件操作结构体*/
static struct file_operations fops={
.owner = THIS_MODULE,
.read = simple_read,
.write = simple_write,
};
/*编写入口函数*/
/*__init的作用就是为了实现,当我们注册完驱动以后,simple_driver_init这段代码就没用了 * 因此我们释放掉这段空间多好,多香*/
static int __init simple_driver_init(void)
{
printk("simple_driver_init success\n");
major=register_chrdev(0,"simple_driver",&fops);
return 0;
}
static void __exit simple_driver_exit(void)
{
printk("simple_driver_exit success\n");
unregister_chrdev(major,"simple_driver");
return ;
}
module_init(simple_driver_init);
module_exit(simple_driver_exit);
MODULE_AUTHOR("jacky");
MODULE_LICENSE("GPL");
Makefile
ARCH := x86
CROSS_COMPILE :=
KERNEL_VERSION :=$(shell uname -r)
KERNEL_DIR :=/lib/modules/$(KERNEL_VERSION)/build
SOURCE :=app_test.c
all:
make -C $(KERNEL_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o build $(SOURCE)
clean:
make -C $(KERNEL_DIR) M=`pwd` modules clean
rm -rf modules.order build
obj-m +=simple_driver.o
驱动测试
编译
挂载:sudo insmod xxx.ko
查看内核打印信息 : dmesg
查看设备号: cat /proc/devices
创建simple_driver的设备节点:sudo mknod /dev/simple c 240 0
卸载驱动:sudo rmmod simple_driver
应用程序
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
char buffer[4]={
0};
int main(int argc ,char **argv){
//打开文件
int fd=open("/dev/simple",O_RDWR);
printf("fd:%d\n",fd);
if(fd==-1){
perror("[open file error]");
return -1;
}
//写数据
int ret=write(fd,"ABC",3);
if(ret<0){
fprintf(stderr,"write data error\n");
goto failed;
}
printf("write:%d\n",ret);
//读数据
ret=read(fd,buffer,sizeof(buffer)-1);
if(ret<0){
fprintf(stderr,"read data error\n");
goto failed;
}
printf("read:%s\n",buffer);
return 0;
failed:
return -1;
}
测试
今天的文章驱动开发(二)——最简单的驱动程序分析分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/24184.html