婚庆网站设计自己做网站可以用私有云吗
web/
2025/9/27 18:24:06/
文章来源:
婚庆网站设计,自己做网站可以用私有云吗,网站源码后台,网站出现乱码的原因一、字符设备驱动结构
1. cdev结构体
在Linux内核中#xff0c;使用cdev结构体来描述一个字符设备
struct cdev {struct kobject kobj; //内嵌kobject对象struct module *owner; //所属的模块const struct file_operations *ops; //该设备的文件操作结构体struct list_head…一、字符设备驱动结构
1. cdev结构体
在Linux内核中使用cdev结构体来描述一个字符设备
struct cdev {struct kobject kobj; //内嵌kobject对象struct module *owner; //所属的模块const struct file_operations *ops; //该设备的文件操作结构体struct list_head list;dev_t dev; //设备号unsigned int count;
};cdev相关的操作
cdev_init: 初始化cdev的函数实际上就是将cdev和file_operation进行关联
void cdev_init(struct cdev *cdev, struct file_operations *fops)
{memset(cdev, 0, sizeof *cdev);INIT_LIST_HEAD(cdev-list);kobject_init(cdev-kobj, ktype_cdev_default);cdev-ops fops; /* 将传入的文件操作结构体指针赋值给 cdev 的 ops*/
}cdev_alloc: 动态申请一个cdev
struct cdev *cdev_alloc(void)
{struct cdev *p kzalloc(sizeof(struct cdev), GFP_KERNEL);if (p) {INIT_LIST_HEAD(p-list);kobject_init(p-kobj, ktype_cdev_dynamic);}return p;
}cdev_add/cdev_del 向内核中添加/删除cdev即对设备的注册和注销。通常发生在加载/卸载模块时
设备号 cdev结构体的dev_t定义了设备号前12bit代表主设备号后20bit代表次设备号。同一驱动可支持多个同类设备因此同一类设备一般使用相同的主设备号次设备号从0开始用来描述驱动的不同设备序号
MKDEV(int major, int minor) //组成一个设备号
MAJOR(dev_t dev) //获取主设备号
MINOR(dev_t dev) //获取次设备号
在调用cdev_add函数注册设备之前需要先申请设备号
//已知设备号时使用直接申请
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);在调用cdev_del函数注销设备前需要释放设备号
void unregister_chrdev_region(dev_t from, unsigned count);2. file_operation结构体
应用程序调用的open/read/write等函数最终时调用的对应设备的file_operation结构体中的对应函数所以驱动程序设计的主题内容就是实现file_operation结构体中的成员函数
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*iterate) (struct file *, struct dir_context *);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long,unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);int (*show_fdinfo)(struct seq_file *m, struct file *f);
};llseek(): 修改一个文件的当前读写位置 read()/write(): 向设备中读/写数据 unlocked_ioctl(): 提供设备相关控制命令的实现 mmap(): 将设备内存映射到进程的虚拟内存 open()/release(): 打开/关闭设备。
3. 组成
加载/卸载函数 字符设备驱动模块加载函数中需要实现设备号的申请和cdev的注册
static int __init xxx_init(void)
{.../* 初始化 cdev */cdev_init(xxx_dev.cdev, xxx_fops); xxx_dev.cdev.owner THIS_MODULE;/* 获取字符设备号 */if (xxx_major) {register_chrdev_region(xxx_dev_no, 1, DEV_NAME);} else {alloc_chrdev_region(xxx_dev_no, 0, 1, DEV_NAME);}/* 注册设备 */ret cdev_add(xxx_dev.cdev, xxx_dev_no, 1); ...
}卸载时需要释放设备号和注销设备
static void _ _exit xxx_exit(void)
{unregister_chrdev_region(xxx_dev_no, 1); /* 释放占用的设备号 */cdev_del(xxx_dev.cdev); /* 注销设备 */...
}file_operations结构体成员函数 大多数字符设备会实现read()、write()、ioctl()函数。由于用户空间不能直接访问内核空间的内存所以使用copy_from_user()和copy_to_user()进行交互
/* 读设备 */
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t*f_pos)
{...copy_to_user(buf, ..., ...);...
}
/* 写设备 */
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{...copy_from_user(..., buf, ...);...
}
/* ioctl 函数 */
long xxx_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{...switch (cmd) {case XXX_CMD1:...break;case XXX_CMD2:...break;default:/* 不能支持的命令 */return - ENOTTY;}return 0;
}驱动程序结构 二、实例
实例是宋宝华的Linux设备驱动开发详解里的例子。实现了一个虚拟的globalmem设备的驱动程序这个设备会分配一块内存空间主要是实现对这块内存的相关驱动函数 代码实现
/** a simple char device driver: globalmem without mutex** Copyright (C) 2014 Barry Song (baohuakernel.org)** Licensed under GPLv2 or later.*/#include linux/module.h
#include linux/fs.h
#include linux/init.h
#include linux/cdev.h
#include linux/slab.h
#include linux/uaccess.h#define GLOBALMEM_SIZE 0x1000 //要分配的内存空间大小
#define MEM_CLEAR 0x1 //清内存cmd
#define GLOBALMEM_MAJOR 230 //主设备号static int globalmem_major GLOBALMEM_MAJOR; //定义主设备号
module_param(globalmem_major, int, S_IRUGO); //可以接受传参来定义主设备号//定义globalmem设备的类型
struct globalmem_dev {struct cdev cdev; //所有字符设备都必须包含的结构体unsigned char mem[GLOBALMEM_SIZE]; //不同设备可以自定义不同的变量
};//定义一个globalmem设备
struct globalmem_dev *globalmem_devp;//globalmem设备的open函数,filep代表打开动作的状态inode代表文件本身的属性
static int globalmem_open(struct inode *inode, struct file *filp)
{//将globalmem设备结构体传给对应的file以便后续其他函数使用globalmem_devp结构体filp-private_data globalmem_devp; return 0;
}//当所有调用globalmem的进程都关闭时才会调用release
static int globalmem_release(struct inode *inode, struct file *filp)
{return 0;
}//可以接收MEM_CLEAR 命令里面实现相关对应命令的操作
static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct globalmem_dev *dev filp-private_data; //通过传入的file来获取globalmem_dev switch (cmd) {case MEM_CLEAR:memset(dev-mem, 0, GLOBALMEM_SIZE); //对应命令的操作printk(KERN_INFO globalmem is set to zero\n);break;default:return -EINVAL;}return 0;
}//当应用程序调用read函数时最终会调用此函数
static ssize_t globalmem_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 globalmem_dev *dev filp-private_data; //通过传入的file来获取globalmem_dev if (p GLOBALMEM_SIZE)return 0;if (count GLOBALMEM_SIZE - p)count GLOBALMEM_SIZE - p;if (copy_to_user(buf, dev-mem p, count)) { //将数据从内核空间copy到用户空间ret -EFAULT;} else {*ppos count;ret count;printk(KERN_INFO read %u bytes(s) from %lu\n, count, p);}return ret;
}//当应用程序调用write函数时最终会调用此函数
static ssize_t globalmem_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 globalmem_dev *dev filp-private_data;if (p GLOBALMEM_SIZE)return 0;if (count GLOBALMEM_SIZE - p)count GLOBALMEM_SIZE - p;if (copy_from_user(dev-mem p, buf, count))ret -EFAULT;else {*ppos count;ret count;printk(KERN_INFO written %u bytes(s) from %lu\n, count, p);}return ret;
}static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{loff_t ret 0;switch (orig) {case 0:if (offset 0) {ret -EINVAL;break;}if ((unsigned int)offset GLOBALMEM_SIZE) {ret -EINVAL;break;}filp-f_pos (unsigned int)offset;ret filp-f_pos;break;case 1:if ((filp-f_pos offset) GLOBALMEM_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;
}//定义file_operation结构体并填充成员函数
static const struct file_operations globalmem_fops {.owner THIS_MODULE,.llseek globalmem_llseek,.read globalmem_read,.write globalmem_write,.unlocked_ioctl globalmem_ioctl,.open globalmem_open,.release globalmem_release,
};//初始化并向内核注册globalmem_dev设备
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{//创建一个设备号int err, devno MKDEV(globalmem_major, index);//关联cdev和file_operationscdev_init(dev-cdev, globalmem_fops);dev-cdev.owner THIS_MODULE;//注册cdev到内核err cdev_add(dev-cdev, devno, 1);if (err)printk(KERN_NOTICE Error %d adding globalmem%d, err, index);
}//模块初始化函数
static int __init globalmem_init(void)
{int ret;//创建一个次设备号为0的设备号dev_t devno MKDEV(globalmem_major, 0);//向内核注册设备号if (globalmem_major)ret register_chrdev_region(devno, 1, globalmem);else {ret alloc_chrdev_region(devno, 0, 1, globalmem);globalmem_major MAJOR(devno);}if (ret 0)return ret;//申请一块globalmem_devp内存并清0globalmem_devp kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);if (!globalmem_devp) {ret -ENOMEM;goto fail_malloc;}//初始化并向内核注册globalmem_dev设备globalmem_setup_cdev(globalmem_devp, 0);return 0;fail_malloc:unregister_chrdev_region(devno, 1);return ret;
}
//模块加载时会调用此函数
module_init(globalmem_init);//模块卸载函数
static void __exit globalmem_exit(void)
{cdev_del(globalmem_devp-cdev); //从内核中注销设备kfree(globalmem_devp); //释放申请的设备内存unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); //释放设备号
}
module_exit(globalmem_exit);MODULE_AUTHOR(Barry Song baohuakernel.org);
MODULE_LICENSE(GPL v2);
验证
### 加载驱动 ###
sudo insmod globalmem.ko #加载驱动
lnsmod #查看是否已经被加载
cat /proc/devices #可以看到多出的主设备号为230名字为globalmem的字符设备驱动
mknod /dev/globalmem c 230 0 #创建/dev/globalmem设备节点### 读写验证 ###
echo hello world /dev/globalmem #将会调用write函数将hello wrold写进去
cat /dev/globalmem #将会调用read函数将字符串读出来
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/82910.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!