【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet。文章仅供学习交流。请勿用于商业用途】

        读写设备文件也就是调用系统调用read()和write(),系统调用就是内核提供给应用程序的接口,应用程序对底层的操作大部分都是通过系统调用来完毕。差点儿全部的系统调用都涉及到内核和应用的数据交换。本节并不是讲述怎样加入一个系统调用(那是第一节的内容),而是解说怎样利用现有系统调用来实现特定的内核与应用交互需求。
        本节将建立一个字符设备驱动来作为应用和内核之间数据通信的渠道。字符设备驱动有关信息能够參考作者这篇文章:

建立字符设备驱动有例如以下步骤:

第一步、注冊设备号。

能够使用例如以下函数分别静态和动态注冊:

int register_chrdev_region(dev_t from, unsigned count, const char *name)。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

第二步、初始化设备结构体。

使用函数:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)。

在调用该函数之前。还须要初始化file_operations结构体。该结构体成员函数是字符设备驱动设计的主要内容,当在应用上调用read和write等函数时。该结构体中相应的函数会被调用。

第三步、加入设备。

使用函数:

int cdev_add(struct cdev *p, dev_t dev, unsigned count);

第四步、实现file_operations结构体中open()、read()、write()、ioctl()等函数。

        file_operations中的read()和write()函数,就是用来在驱动程序和应用程序间交换数据的。通过数据交换。驱动程序和应用程序能够彼此了解对方的情况。可是驱动程序和应用程序属于不同的地址空间。驱动程序不能直接訪问应用程序的地址空间;相同应用程序也不能直接訪问驱动程序的地址空间。否则会破坏彼此空间中的数据,从而造成系统崩溃,或者数据损坏。安全的方法是使用内核提供的专用函数,完毕数据在应用程序空间和驱动程序空间的交换。这些函数对用户程序传过来的指针进行了严格的检查和必要的转换,从而保证用户程序与驱动程序交换数据的安全性。这些函数有:
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n); put_user(local,user);
get_user(local,user);当不是该设备时,应当删除该设备、释放申请的设备号。 void cdev_del(struct cdev *dev);
void unregister_chrdev_region(dev_t from, unsigned count);这两函数一般在卸载模块中调用。 以下看一下字符设备的完整实现代码:
#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> #include <linux/kernel.h>
#include "chrdev.h" int chr_major;
struct chr_dev *chr_devp; int chr_open(struct inode *inode, struct file *filp)
{
filp->private_data = chr_devp; return 0;
} static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)
{
struct chr_dev* dev = filp->private_data; printk(KERN_ALERT"=========%s()=========\n", __func__); switch(cmd) {
case MEM_CLEAR:
memset(dev->data, 0, CHAR_DEV_DATA_SIZE);
break;
case MEM_RESET:
snprintf(dev->data, CHAR_DEV_DATA_SIZE, "%s", "hello, user!");
break; default:
return -EINVAL;
} return 0;
} static ssize_t chr_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct chr_dev* dev = filp->private_data; printk(KERN_ALERT"=========%s()=========\n", __func__);
if(p >= CHAR_DEV_DATA_SIZE) {
return 0;
} if(count > CHAR_DEV_DATA_SIZE - p) {
return 0;
}
//将内核中数据dev->data,读取到用户空间buf中,读取count字节
if(copy_to_user(buf, (void*)(dev->data + p), count)) {
return -EINVAL;
} else {
*ppos += count;
ret = count;
} return ret;
} static ssize_t chr_write(struct file* filp, const char __user* buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct chr_dev* dev = filp->private_data; printk(KERN_ALERT"=========%s()=========\n", __func__);
if(p >= CHAR_DEV_DATA_SIZE) {
return 0;
} if(count > CHAR_DEV_DATA_SIZEE - p) {
count = CHAR_DEV_DATA_SIZE - p;
}
//将用户空间buf中数据copy到内核空间dev->data中,copy count字节数据
if(copy_from_user(dev->data + p, buf, count)) {
ret = -EINVAL;
} else {
*ppos += count;
ret = count;
} return ret;
} static loff_t chr_llseek(struct file* filp, loff_t offset, int orig)
{
loff_t ret = 0; printk(KERN_ALERT"=========%s()=========\n", __func__);
/* orig can be SEEK_SET, SEEK_CUR, SEEK_END */
switch(orig) {
case 0:
if(offset < 0) {
ret = -EINVAL;
break;
} if((unsigned int) offset > CHAR_DEV_DATA_SIZE) {
ret = -EINVAL;
break;
} filp->f_pos = (unsigned int) offset;
ret = filp->f_pos;
break; case 1:
if((filp->f_pos + offset) > CHAR_DEV_DATA_SIZE) {
ret = -EINVAL;
break;
} if((filp->f_pos + offset) < 0) {
ret = -EINVAL;
break;
} filp->f_pos += offset;
ret = filp->f_pos;
break; default:
ret = - EINVAL;
break;
} return ret;
} int chr_release(struct inode *inode, struct file *filp)
{
return 0;
} static const struct file_operations chr_fops =
{
.owner = THIS_MODULE,
.open = chr_open,
.release = chr_release,
.read = chr_read,
.write = chr_write,
.llseek = chr_llseek,
.ioctl = chr_ioctl, }; static int chr_dev_init(void)
{
int result;
dev_t devno; /* 注冊设备号 */
result = alloc_chrdev_region(&devno, 0, 1, "chardev");
if (result < 0) {
return result;
} // 分配自己定义设备结构体内存
chr_devp = kmalloc(CHAR_DEV_NO * sizeof(struct chr_dev), GFP_KERNEL);
if (!chr_devp) {
result = - ENOMEM;
goto err;
}
memset(chr_devp, 0, sizeof(struct chr_dev)); /*初始化设备*/
cdev_init(&chr_devp->cdev, &chr_fops);
chr_devp->cdev.owner = THIS_MODULE; /* 加入设备 */
chr_major = MAJOR(devno);
cdev_add(&chr_devp->cdev, MKDEV(chr_major, 0), CHAR_DEV_NO); /*初始自己定义设备结构体内存数据*/
chr_devp->data = kmalloc(CHAR_DEV_DATA_SIZE, GFP_KERNEL);
memset(chr_devp->data, '*', CHAR_DEV_DATA_SIZE / 100 ); //为避免输出太多影响结果显示,此处只初始化40个字节。 return 0; err:
unregister_chrdev_region(devno, 1); return result;
} static void chr_dev_exit(void)
{
cdev_del(&chr_devp->cdev); //delete device
kfree(chr_devp); // release device memory
unregister_chrdev_region(MKDEV(chr_major, 0), 1); // unregister char device No.
} module_init(chr_dev_init);
module_exit(chr_dev_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("shallnet");
MODULE_DESCRIPTION("blog.csdn.net/shallnet");应用程序实现代码例如以下:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h> #define SHR_MEMSIZE 4096
#define MEM_CLEAR 0x0
#define MEM_RESET 0x1
#define CHAR_DEV_FILENAME "/dev/sln_chardev" int main()
{
int fd;
char shm[SHR_MEMSIZE]; /* 打开设备文件 */
fd = open(CHAR_DEV_FILENAME, O_RDWR);
if(fd < 0) {
printf("open <%s> failed!\n", CHAR_DEV_FILENAME);
return -1;
} /* 直接设置共享内存数据 */
snprintf(shm, sizeof(shm), "this data is writed by user!"); /* 写入数据 */
printf("======== Write data========\n");
if (write(fd, shm, strlen(shm)) < 0) {
printf("write(): %s\n", strerror(errno));
return -1;
} /* 再读取数据,以验证应用上设置成功 */
printf("======== Read data========\n");
if (lseek(fd, 0, SEEK_SET) < 0) {
printf("llseek(): %s\n", strerror(errno));
return -1;
} if (read(fd, shm, SHR_MEMSIZE) < 0) {
printf("read(): %s\n", strerror(errno));
return -1;
}
printf("read data: %s\n", shm); /* 再清空数据之后再读取 */
printf("========= Clear it now: =======\n");
if (ioctl(fd, MEM_CLEAR, NULL) < 0) {
printf("ioctl(): %s\n", strerror(errno));
return -1;
} if (lseek(fd, 0, SEEK_SET) < 0) {
printf("llseek(): %s\n", strerror(errno));
return -1;
} if (read(fd, shm, SHR_MEMSIZE) < 0) {
printf("read(): %s\n", strerror(errno));
return -1;
}
printf("read data: %s\n", shm); /* reset all data, read it and check whether it is ok */
printf("========= Reset it now: =======\n");
if (ioctl(fd, MEM_RESET, NULL) < 0) {
printf("ioctl(): %s\n", strerror(errno));
return -1;
} if (lseek(fd, 0, SEEK_SET) < 0) {
printf("llseek(): %s\n", strerror(errno));
return -1;
} if (read(fd, shm, SHR_MEMSIZE) < 0) {
printf("read(): %s\n", strerror(errno));
return -1;
}
printf("read data: %s\n", shm); close(fd);
return 0;
}在编译驱动和应用程序之后就能够验证内核和应用的数据交换是否成功了。执行验证    例如以下: # insmod chardev.ko //首先插入模块
# cat /proc/devices | grep chardev //查看驱动模块相应主设备号
248 chardev
# mknod /dev/sln_chardev c 248 0 //为设备创建相应节点
#然后再执行应用程序:
# ./read_app
======== Write data========
======== Read data========
read data: this data is writed by user!************ //读取应用设置数据成功
========= Clear it now: ======= //在内核清空数据,应用读取数据为空
read data:
========= Reset it now: ======= //在内核重设空间数据为hello, user!,应用上再读取该数据,输出预期值!
read data: hello, user!
# 本节仅仅实现了file_operations结构体中部分函数,在后面我们还能够实现其他的函数,也能够实现内核和应用交互数据。 本节源代码下载:
http://download.csdn.net/detail/gentleliu/9035821

最新文章

  1. mysql状态取反(0变1,1变0)
  2. docker部署tomcat
  3. JAVA的包装类 【转】
  4. Wix 安装部署教程(三)自定义安装界面和行为
  5. 003. vs2010发布、打包安装程序(转)
  6. vs 下 opengl 配置问题
  7. Blacksmith test
  8. Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)
  9. dom4j 间隔插入节点 处理复杂的xml文档
  10. 学习Javascript闭包(Closure)及几个经典面试题理解
  11. mysql 盲注二分法python脚本
  12. Python3 安装basemap
  13. keil项目的调试与编译
  14. oracle 如何查询/修改dmp文件的字符集
  15. http状态码 301、302区别
  16. mysql 开启profiling
  17. tomcat热启动没问题, 访问项目报500解决办法
  18. S3 exercise -- 文件操作&amp;函数
  19. [HDU6212]Zuma
  20. [DeeplearningAI笔记]序列模型3.2有条件的语言模型与贪心搜索的不可行性

热门文章

  1. com/opensymphony/xwork2/spring/SpringObjectFactory.java:220:-1问题出现的原因及解决办法
  2. 78.员工个人信息保镖页面 Extjs 页面
  3. python多线程,限制线程数
  4. simpleOS 1.0
  5. 使用filezella服务器配置ftp
  6. codevs1253 超级市场(dp)
  7. .net core2.0 读取appsettings.json
  8. UML基本关系
  9. oracle中sum求和问题
  10. Python 遍历目录