继续上次的操作系统实验。这次需要添加一个新的设备,我选择添加一个比较好实现的字符设备。先说点基础知识吧:

系统调用是操作系统内核和应用程序之间的接口,而设备驱动程序是操作系统内核和机器硬件之间的接口。

设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以像操作普通文件一样对硬件设备进行操作。

设备驱动程序是内核的一部分,它完成以下的功能:

(1) 对设备初始化和释放.

(2) 把数据从内核传送到硬件和从硬件读取数据.

(3) 读取应用程序传送给设备文件的数据和回送应用程序请求的数据.

(4) 检测和处理设备出现的错误.

Linux支持三中不同类型的设备:字符设备(character devices)、块设备(block devices)和网络设备(network interfaces)。

字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了。

块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.

用户进程是通过设备文件来与实际的硬件打交道,每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。

另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,

比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.。

设备驱动程序工作的基本原理:

用户进程利用系统调用对设备进行诸如read/write操作,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。

最后,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。

如果你的驱动程序陷入死循环,你只有重新启动机器了。

下面我们就来添加一个字符设备:

编写设备驱动源代码

在设备驱动程序中有一个非常重要的结构file_operations,该结构的每个域都对应着一个系统调用。

用户进程利用系统调用对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。

structfile_operations {

int(*seek) (structinode * ,structfile *, off_t ,int);

int(*read) (structinode * ,structfile *,char,int);

int(*write) (structinode * ,structfile *, off_t ,int);

int(*readdir) (structinode * ,structfile *,structdirent * ,int);

int(*select) (structinode * ,structfile *,int,select_table *);

int(*ioctl) (structinode * ,structfile *, unsinedint,unsignedlong);

int(*mmap) (structinode * ,structfile *,structvm_area_struct *);

int(*open) (structinode * ,structfile *);

int(*release) (structinode * ,structfile *);

int(*fsync) (structinode * ,structfile *);

int(*fasync) (structinode * ,structfile *,int);

int(*check_media_change) (structinode * ,structfile *);

int(*revalidate) (dev_t dev);

}

编写设备驱动程序的主要工作是编写子函数,并填充file_operations的各个域。

例如:

Struct file_operations my_fops={

.read=my_read,

.write=my_write,

.open=my_open,

.release=my_release

}

然后再定义函数my_read,my_write,my_open,my_release相应的函数体。

例如:

staticssize_t my_open(structinode *inode,structfile *file){

staticintcounter=0;

if(Device_Open)

return-EBUSY;

Device_Open++;

/*写入设备的信息*/

sprintf(msg,"the device has been called %d times\n",counter++);

msg_ptr=msg;

return0;

}

同时对于可卸载的内核模块(LKM),至少还有两个基本的模块:

例如本例中的:

staticint__init my_init(void){

intresult;

result=register_chrdev(0,"sky_driver",&my_fops);

if(result<0){

printk("error:can not register the device\n");

return-1;

}

if(my_major==0){

my_major=result;

printk("<1>hehe,the device has been registered!\n");

printk("<1>the virtual device was assigned major number %d.\n",my_major);

printk("<1>To talk to the driver,create a dev file with\n");

printk("<1>'mknod/dev/my c %d 0'\n",my_major);

printk("<1>Remove the dev and the file when done\n");

}

return0;

}

staticvoid__exit my_exit(void){

unregister_chrdev(my_major,"sky_driver");

printk("<1>unloading the device\n");

}

my_init用于注册设备,获得设备的主设备号

调用register_chrdev(0,“sky_driver(设备名)”,&my_fops);

my_exit用于注销设备

调用unregister_chrdev(my_major, “sky_driver(设备名)”);

然后在程序尾再调用这两个函数

Module_init(my_init);

Module_exit(my_exit)

MODULE_LICENSE(“GPL”);

编写自己的驱动程序源文件mydriver.c:

#include

#include

#include

#include

#include

#if CONFIG_MODVERSIONS == 1

#define MODVERSIONS

#include

#endif

#define DEVICE_NUM 0 //随机产生一个设备号

staticintdevice_num = 0;//用来保存创建成功后的设备号

staticcharbuffer[1024] ="mydriver";//数据缓冲区

staticintopen_nr = 0;//打开设备的进程数,用于内核的互斥

//函数声明

staticintmydriver_open(structinode *inode,structfile *filp);

staticintmydriver_release(structinode *inode,structfile* filp);

staticssize_t mydriver_read(structfile *file,char__user *buf,size_tcount, loff_t *f_pos);

staticssize_t mydriver_write(structfile *file,constchar__user *buf,size_tcount, loff_t *f_pos);

//填充file_operations结构相关入口

staticstructfile_operations mydriver_fops = {

.read    = mydriver_read,

.write   = mydriver_write,

.open    = mydriver_open,

.release = mydriver_release,

};

//打开函数

staticintmydriver_open(structinode *inode,structfile *filp)

{

printk("\nMain device is %d, and the slave device is %d\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));

if(open_nr == 0) {

open_nr++;

try_module_get(THIS_MODULE);

return0;

}

else{

printk(KERN_ALERT "Another process open the char device.\n");//进程挂起

return-1;

}

}

//读函数

staticssize_t mydriver_read(structfile *file,char__user *buf,size_tcount, loff_t *f_pos)

{

//if (buf == NULL) return 0;

if(copy_to_user(buf, buffer,sizeof(buffer)))//读缓冲

{

return-1;

}

returnsizeof(buffer);

}

//写函数,将用户的输入字符串写入

staticssize_t mydriver_write(structfile *file,constchar__user *buf,size_tcount, loff_t *f_pos)

{

//if (buf == NULL) return 0;

if(copy_from_user(buffer, buf,sizeof(buffer)))//写缓冲

{

return-1;

}

returnsizeof(buffer);

}

//释放设备函数

staticintmydriver_release(structinode *inode,structfile* filp)

{

open_nr--; //进程数减1

printk("The device is released!\n");

module_put(THIS_MODULE);

return0;

}

//注册设备函数

staticint__init mydriver_init(void)

{

intresult;

printk(KERN_ALERT "Begin to init Char Device!");//注册设备

//向系统的字符登记表登记一个字符设备

result = register_chrdev(DEVICE_NUM, "mydriver", &mydriver_fops);

if(result

printk(KERN_WARNING "mydriver: register failure\n");

return-1;

}

else{

printk("mydriver: register success!\n");

device_num = result;

return0;

}

}

//注销设备函数

staticvoid__exit mydriver_exit(void)

{

printk(KERN_ALERT "Unloading...\n");

unregister_chrdev(device_num, "mydriver");//注销设备

printk("unregister success!\n");

}

//模块宏定义

module_init(mydriver_init);

module_exit(mydriver_exit);

MODULE_LICENSE("GPL");

编译该设备驱动代码

然后将设备驱动源文件复制到/usr/src/linux/drivers/misc下

修改misc目录下的Makefile文件,只要在最后添加一句即可:obj-m +=mydriver.o。

在/usr/src/linux/drivers/misc路径下执行命令:Make -C /usr/src/linux SUBDIRS=$PWD modules编译成功将得到mydriver.ko文件。

可以在misc目录下观察得到了mydriver.ko文件。

继续执行insmod ./mydriver.ko命令挂载内核中的模块。

然后通过lsmod命令可以看到增加的设备模块mydriver。

输入cat /var/log/messages可以看到设备注册成功。

此时进入/proc/devices文件会看到在字符设备中有250 mydriver。前面的是系统分配的主设备号,后面是设备注册名。

进入在/dev路径下,执行命令:

mknod /dev/mydriver c 250 0

第一个参数是新建设备文件的地址和名字。

第二个参数是指创建的是字符设备文件。

第三个参数是主设备号,第四个参数是从设备号,自己随便取。

执行成功会在/dev/char中看到一个新的设备文件mydriver

至此设备添加成功。

编译测试程序。

编写测试代码如下:

#include

#include

#include

#include

#include

intmain(void)

{

intfd;

charbuf[1024];

charget[1024];

memset(get, 0, sizeof(get));

memset(buf, 0, sizeof(buf));

printf("please enter a string you want input to mydriver:\n");

gets(get);

fd = open("/dev/mydriver", O_RDWR, S_IRUSR|S_IWUSR);//打开设备

if(fd > 0) {

read(fd, &buf, sizeof(buf));

printf("The message in mydriver now is: %s\n", buf);

//将输入写入设备

write(fd, &get, sizeof(get));

//读出设备的信息并打印

read(fd, &buf, sizeof(buf));

printf("The message changed to: %s\n", buf);

sleep(1);

}

else{

printf("OMG...");

return-1;

}

close(fd);//释放设备

return0;

}

gcc -o mydriver_test mydriver_test.c

./mydriver_test

输入任意字符串,驱动程序将字符串拷贝进新加入的设备,然后再读取出来,设备中保留字符串信息,再次输入将覆盖原来的信息。

ALL ended~~I will be back~~

linux添加模块设备,linux采用模块方法,添加一个新的设备相关推荐

  1. Linux 实用小脚本7(各种方法添加用户)

                        Linux 实用小脚本7(各种方法添加批量用户,用户存在就提示,不存在就添加) 前言: shell脚本的主要作用就是提升运维效率,用户对运维工作来说,通常是环境 ...

  2. 使用Response.Redirect 两种方法打开一个新窗口

    普通情况下,Response.Redirect 方法是在server端进行转向,因此,除非使用 Response.Write("<script>window.location=' ...

  3. React开发(226):默认方法返回一个新的参数两个括号

    <Form className={'form-customer'}><Form.Item style={{ width: '100%' }}>{getFieldDecorato ...

  4. linux 软件做raid,Linux用软件实现RAID 的方法

    数据的安全性是人们在使用计算机中最重要的问题之一.通常情况下,人们会在服务器环境中采用硬盘镜像技术,以达到数据的双重备份.同样在Linux环境下,我们可以采用这种技术.在Linux环境下,我们可以采用 ...

  5. linux图形化应用程序快捷方式制作方法

    linux图形化应用程序快捷方式制作方法 准备 一个Linux系统[传统Unity桌面环境,应用程序启动器的容器--Launcher] 说明 linux的.desktop文件是一种菜单和快捷方式的描述 ...

  6. Windows程序员初学Linux内核(附Linux内核各版本历史纪年表)

    我是荔园微风,作为一名在IT界整整25年的老兵,最近受邀给年轻人讲了一场Windows内核和Linux内核相关的讲座.大家听得非常认真.下面我把其中一些PPT放上来和大家分享. Windows内核(右 ...

  7. Linux内核发展史和linux发行版

    参考链接:Linux内核发展史(1)和Linux内核简介.版本号.发布历史及发行版 一.巨人的肩膀 其实,除了之前提到的Minix系统外,Linux系统本身也是站在巨人的肩膀上,在它发布之前操作系统就 ...

  8. 嵌入式Linux开发教程:Linux常见命令(上篇)

    摘要:这是对周立功编著的<嵌入式Linux开发教程>的第7期连载.本期刊载内容有关LinuxLinux常见命令中的导航命令.目录命令和文件命令.下一期将连载网络操作命令.安装卸载文件系统等 ...

  9. 【Linux驱动开发】Linux 自带按键驱动

    一.基础 1.以下介绍了 input 子系统的基础,使用的自己编的按键驱动.实际上内核自带了按键驱动(一般默认使能). [Linux驱动开发]INPUT 子系统实验 2.内核自带按键驱动简介 Linu ...

  10. linux qos 内核配置,Linux下QOS:应用篇

    我们讲了QOS的理论知识,了解了它是做什么用的,以及设计模式,下面就以tc的应用的更深入的理解qos. Linux采用了基于对象的实现方法,qos还能保证对不同接口采用不同的策略,TC QOS有很多拥 ...

最新文章

  1. Linux底层函数库“glibc”再现重大安全漏洞
  2. comboBox设置为只读(只选)
  3. Qt探索之路——获取QTextEdit文本内容
  4. nginx web服务理论与实战
  5. Maven3.2创建webapp项目过程中问题以及解决方案
  6. centos图形化桌面的文件在哪_Windows 远程连接 CentOS 7 图形化桌面
  7. mysql作为kafka生产者_Kafka之生产者
  8. java sound 多线程同一音频文件_Java在编程语言中占据何等优势?
  9. ORACLE 进入sqlplus的协议适配器错误
  10. 软件测试学生管理系统课程设计,软件测试课程设计-ERP进销存管理系统(1)
  11. 特殊时期,找工作的 9 点建议!
  12. Welcome-to-Swift-13继承(Inheritance)
  13. VMware Cloud Director 10.3.1 - 云计算调配和管理平台
  14. 即时通讯软件(即聊天软件)代表软件列表
  15. 世界人工智能大会-杨强演讲内容ppt
  16. 拼多多电商玩家如何利用软件机器人快速采集平台数据
  17. 使用python压缩文件夹
  18. 【转】聚类——GMM
  19. 基于SOA的设备智能维护系统架构设计及实现
  20. 【mcuclub】四相五线步进电机

热门文章

  1. Abaqus简单部件受力分析
  2. 5种电脑定时关机的方法分享
  3. 《信号与系统》4.11.2系统函数的幅频特性和相频特性分析 MATLAB实现
  4. python贝叶斯网络预测模型_概率图模型之:贝叶斯网络
  5. 实时全局光照Screen Space Reflection (SSR)
  6. Win server 2008 R2激活工具使用图文教程(SK Patch v1 R2 Final OEM)
  7. 如何使用python处理nc数据制作Mike风场文件--以ERA5数据为例
  8. 《钢构CAD》不断致力于帮助用户取得更多成就
  9. Octave与MATLAB
  10. mocano editor中使用代码比对功能