网站站点结构图怎么做,网站一般做多大的,新媒体营销策略分析,广州市南沙区基本建设办公室网站声明#xff1a;内容源于B站UP主——北京迅为电子一、简介字符设备#xff1a;IO的传递传递过程是以字符设备为单位的#xff0c;没有缓冲#xff0c;比如I2C,SPI都是字符设备
块设备#xff1a;IO传递过程是一块为单位的#xff0c;跟存储相关的#xff0c;都属于块设备…声明内容源于B站UP主——北京迅为电子一、简介字符设备IO的传递传递过程是以字符设备为单位的没有缓冲比如I2C,SPI都是字符设备块设备IO传递过程是一块为单位的跟存储相关的都属于块设备网络设备与前面两个不一样是以sockett套接字来访问的1.1 杂项设备特点杂项设备使字符设备的一种可以自动生成设备节点系统中有很多杂项设备可以通过cat /proc/misc命令来查看问题一杂项设备除了比字符设备代码简单还有别的区别吗杂项设备的主设备号都是相同的均为10次设备是不同的主设备号相同就可以节省内核资源问题二主设备号与次设备号是什么设备号包含主设备号和此设备号主设备号在Linux系统里面是唯一的次设备号不一定唯一主设备号可以通过命令cat /proc/devices二、杂项设备2.1 杂项设备描述定义在内核源码路径下include/linux/miscdevicestruct miscdevice { int minor; //次设备号。可设为 MISC_DYNAMIC_MINOR通常为255让内核自动分配一个空闲的。 const char *name; //设备名称。注册成功后会在 /dev/ 下生成以此命名的设备节点 const struct file_operations *fops;//文件操作集。指向实现设备操作函数集如 open、read、write、ioctl 等 struct list_head list; struct device *parent; struct device *this_device; const struct attribute_group **groups; const char *nodename; umode_t mode; }; extern int misc_register(struct miscdevice *misc); //注册杂项设备 extern void misc_deregister(struct miscdevice *misc); //注销杂项设备file_operations文件操作集定义在incldue/linux/fs.h下面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 (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*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 *); unsigned long mmap_supported_flags; 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 (*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 **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64); int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t, u64); int (*fadvise)(struct file *, loff_t, loff_t, int); ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); } __randomize_layout;里面的一个结构体成员都对应一个调用2.2 注册杂项设备的流程①填充miscdevice这个结构体②填充file_operations这个结构体③注册杂项设备并生成设备节点/*模板*/ struct const file_operation xx_fops { .owner THIS_MODULE, //固定写法 ...... }; struct miscdevice xx_dev { .minor MISC_DYNAMIC_MINOR, //自动分配次设备号 .name xxx, //定义自己的杂项设备名称 .fopsxx_fops //设备操作函数集合 }; static int xxx_init() { int ret; retmisc_misc_register(xx_dev); //注册设备 if(ret0) { printk(misc register error\n); return -1; } return 0; } void xxx_exit() { misc_deregister(xx_dev); //注销设备 }三、编写一个杂项设备驱动编写misc_hello.c文件#include linux/module.h #include linux/init.h #include linux/miscdevice.h #include linux/fs.h //2.填充file_operations结构体 struct file_operations misc_fops { .owner THIS_MODULE //固定写法 }; //1.填充miscdevice结构体 struct miscdevice misc_dev { .minor MISC_DYNAMIC_MINOR, //自动分配次设备号 .name hello_misc, //定义自己的杂项设备名称 .fopsmisc_fops //设备操作函数集合 }; static int misc_init(void) //驱动入口函数 { int ret; ret misc_register(misc_dev); //注册杂项设备 if(ret0) { printk(misc device is register error\n); return -1; } printk(misc device is register succeed\n);//注意 内核打印用 printk 而不是 printf return 0; } static void misc_exit(void) //驱动出口函数 { misc_deregister(misc_dev); //注销杂项设备 printk(misc device is deregister\n); } module_init(misc_init); //告诉linux模块入口函数加载模块代码到操作系统 module_exit(misc_exit); //卸载 MODULE_LICENSE(GPL v2); //同意 GPL 开源协议编写Makefile文件export ARCHarm64 #设置平台架构 export CROSS_COMPILEaarch64-linux-gnu- #交叉编译器前缀 obj-m helloworld.o #m表示将helloworld.o编译为模块 KDIR : ~/LubanCat_SDK/kernel #内核源码路径 PWD ? $(shell pwd) #获取当前目录 all: make -C $(KDIR) M$(PWD) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) modules #make -C $(KDIR)切换到内核目录;M$(PWD) modules将当前路径下的代码编译为模块 clean: make -C $(KDIR) M$(PWD) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) clean #清理生成的文件注意make -C $(KDIR) 会切换到内核源码目录并启动一个新的 make 进程。这个新进程的环境变量可能不会完全继承父进程即Makefile的导出变量尤其是当内核源码目录的 Makefile 有自己的默认设置时。显式传递参数如 ARCHarm64可以确保内核的 make 系统使用正确的架构和编译器而不受环境变量继承问题的影响。四、文件操作函数集问题如果我们在应用层使用系统IO对设备节点进行打开关闭读写等操作时会发生什么呢当在应用层read设备节点的时候就会触发驱动里面read这个函数ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);当在应用层write设备节点的时候就会触发驱动里面write这个函数ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);当在应用层poll/select设备节点的时候就会触发驱动里面poll/select这个函数__poll_t (*poll) (struct file *, struct poll_table_struct *);当在应用层ioctl设备节点的时候就会触发驱动里面ioctl这个函数long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long)当在应用层open设备节点的时候就会触发驱动里面open这个函数int (*open) (struct inode *, struct file *);当在应用层close设备节点的时候就会触发驱动里面close这个函数int (*release) (struct inode *, struct file *);/*驱动层代码*/ #include linux/module.h #include linux/init.h #include linux/miscdevice.h #include linux/fs.h //3.实现open函数 static int misc_open(struct inode *inode, struct file *file) { printk(misc device is opened:hello misc open\n); return 0; } static int misc_release(struct inode *inode, struct file *file) { printk(misc device is released:hello misc release\n); return 0; } static ssize_t misc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { printk(misc device is read:hello misc read\n); return 0; } static ssize_t misc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { printk(misc device is write:hello misc write\n); return 0; } //2.填充file_operations结构体 struct file_operations misc_fops { .owner THIS_MODULE, //固定写法 .open misc_open, .release misc_release, .read misc_read, .write misc_write }; //1.填充miscdevice结构体 struct miscdevice misc_dev { .minor MISC_DYNAMIC_MINOR, //自动分配次设备号 .name hello_misc, //定义自己的杂项设备名称 .fops misc_fops //设备操作函数集合 }; static int misc_init(void) //驱动入口函数 { int ret; ret misc_register(misc_dev); //注册杂项设备 if(ret0) { printk(misc device is register error\n); return -1; } printk(misc device is register succeed\n);//注意 内核打印用 printk 而不是 printf return 0; } static void misc_exit(void) //驱动出口函数 { misc_deregister(misc_dev); //注销杂项设备 printk(misc device is deregister\n); } module_init(misc_init); //告诉linux模块入口函数加载模块代码到操作系统 module_exit(misc_exit); //卸载 MODULE_LICENSE(GPL v2); //同意 GPL 开源协议/*上层应用用于测试应用层调用open函数驱动层是否有相应的反应*/ #include stdio.h #include fcntl.h #include unistd.h #include sys/stat.h #include sys/types.h int main(int argc, char *argv[]) { int fd; char buf[64]{0}; fd open(/dev/hello_misc, O_RDWR); //打开驱动对应的设备文件 if(fd 0) { printf(misc device open failed\n); return -1; } printf(app layer device open success\n); read(fd,buf,sizeof(buf)); close(fd); return 0; }设备节点就是连接上层应用和底层驱动的桥梁问题假如在驱动层代码中file_operitions 里面没有read,但是在应用层有read设备节点代码的时候会发生什么答什么也不会发生也不会报错问题应用层和内核层是不能直接进行数据传输的//用户层向内核层传递数据 static __always_inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) //内核层向用于层传递数据 static __always_inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)注意这两个函数只能在驱动代码中使用函数copy_from_user(void *to, const void __user *from, unsigned long n)函数作用从用户层读取数据到内核层函数参数param1 to 目的地址。数据拷贝的目的地位于内核空间的缓冲区。param2 from 源地址。数据来源位于用户空间的缓冲区由应用层传入param3 n 要拷贝的字节数返回值0n个字节完全拷贝0:未能成功拷贝的剩余字节数函数copy_to_user(void __user *to, const void *from, unsigned long n)函数作用将内核层数据写入用户空间函数参数param1 to 目的地址。数据拷贝的目的地位于用户空间的缓冲区由应用层提供param2 from 源地址。数据来源位于内核空间的缓冲区param2 n 要拷贝的字节数返回值0n个字节完全拷贝0:未能成功拷贝的剩余字节数当应用层运行read(fd,buf,sizeof(buf)); printf(app layer read data : %s\n,buf);驱动层会运行static ssize_t misc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char kbuf[64]嵌入式linux驱动; printk(misc device is read:hello misc read\n); if(copy_to_user(buf,kbuf,sizeof(kbuf))!0) { printk(misc device copy to user error\n); return -1; } return 0; }read函数中的buf是应用层的缓存地址传递给驱动层的misc_read中buf通过copy_to_user将kbuf中的内容拷贝到buf中在传递到应用层最终在应用层printf输出