当前位置: 首页 > news >正文

4. 字符设备驱动高级--- 下篇

文章目录

  • 一、字符设备驱动高级
    • 1.1 注册字符设备驱动新接口
      • 1.1.1 新接口与旧接口
      • 1.1.2 cdev介绍
      • 1.1.3 设备号
      • 1.1.4 编程实践
      • 1.1.5 alloc_chrdev_region自动分配设备号
      • 1.1.6 中途出错的倒影式错误处理方法
  • 二、字符设备驱动注册代码分析
    • 2.1 旧接口register_chrdev
    • 2.2 新接口register_chrdev_region & alloc_chrdev_region
    • 2.3 注销
  • 三、自动创建字符设备驱动的设备文件
    • 3.1 解决方案:udev(嵌入式中用的是mdev)
    • 3.2 内核驱动设备类相关函数
  • 四、设备类相关代码分析
    • 4.1 sysfs文件系统
  • 五、静态映射表建立过程分析
    • 5.1 建立映射表的三个关键部分
  • 六、动态映射结构体方式操作寄存器
  • 七、内核提供的读写寄存器接口

一、字符设备驱动高级

1.1 注册字符设备驱动新接口

1.1.1 新接口与旧接口

  • 旧接口:register_chrdev
  • 新接口:register_chrdev_region(注册设备号)/alloc_chrdev_region(分配设备号) + cdev

1.1.2 cdev介绍

(1) 结构体

struct cdev {struct kobject kobj;          // 内嵌的内核对象,每个 cdev 都是一个 kobjectstruct module *owner;       // 指向实现驱动的模块const struct file_operations *ops;   // 操纵这个字符设备文件的方法struct list_head list;       // 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头,用来将已经向内核注册的所有字符设备形成链表dev_t dev;                   // 字符设备的设备号,由主设备号和次设备号构成unsigned int count;       // 隶属于同一主设备号的次设备号的个数.
};

(2) 模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号;
(3) 通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;
(4) 模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号;

1.1.3 设备号

(1) 设备号 = 主设备号 + 次设备号
(2) dev_t类型
(3) MKDEV(MAJOR, MINOR);
说明: 获取设备在设备表中的位置。
MAJOR 主设备号
MINOR 次设备号

1.1.4 编程实践

(1) 使用register_chrdev_region + cdev_init + cdev_add进行字符设备驱动注册

全局变量:#define MYMAJOR		200#define MYCNT		1#define MYNAME		"testchar"static dev_t mydev;static struct cdev test_cdev;注册驱动:// 新的接口注册字符设备驱动需要2步// 第1步:静态注册/分配主次设备号int retval;mydev = MKDEV(MYMAJOR, 0);retval = register_chrdev_region(mydev, MYCNT, MYNAME);if (retval){printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);return -EINVAL;}printk(KERN_INFO "register_chrdev_region success\n");// 第2步:注册字符设备驱动cdev_init(&test_cdev, &test_fops);retval = cdev_add(&test_cdev, mydev, MYCNT);if (retval) {printk(KERN_ERR "Unable to cdev_add\n");return -EINVAL;}printk(KERN_INFO "cdev_add success\n");注销驱动:
// 使用新的接口来注销字符设备驱动// 注销分2步:// 第一步真正注销字符设备驱动用cdev_delcdev_del(&test_cdev);// 第二步去注销申请的主次设备号unregister_chrdev_region(mydev, MYCNT);

1.1.5 alloc_chrdev_region自动分配设备号

(1) register_chrdev_region是在事先知道要使用的主、次设备号时使用的;要先查看cat /proc/devices去查看没有使用的
(2) 更简便、更智能的方法是让内核给我们自动分配一个主设备号,使用alloc_chrdev_region就可以自动分配了。
(3) 自动分配的设备号,必须去知道它的主次设备号,否则后面没法去mknod创建他对应的设备文件。
(4) 使用MAJOR宏和MINOR宏从dev_t得到major和minor
(5) 反过来使用MKDEV宏从major和minor得到dev_t。
(6) 使用这些宏的代码具有可移植性

	#define MYCNT		1#define MYNAME		"testchar"static dev_t mydev;static struct cdev test_cdev;//自动分配主次设备号retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);if (retval < 0) {printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);goto flag1;}printk(KERN_INFO "alloc_chrdev_region success\n");printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));  //获取我们的主次设备号,用于创建设备文件

1.1.6 中途出错的倒影式错误处理方法

(1) 内核中很多函数中包含了很多个操作,这些操作每一步都有可能出错,而且出错后,后面的步骤就没有进行下去的必要性了。所以就有了倒影式处理错误的方法。

// 第1步:分配主次设备号retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);if (retval < 0) {printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);goto flag1;}// 第2步:注册字符设备驱动cdev_init(&test_cdev, &test_fops);retval = cdev_add(&test_cdev, mydev, MYCNT);if (retval) {printk(KERN_ERR "Unable to cdev_add\n");goto flag2;}// 第3步:使用动态映射的方式来操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))goto flag3;// 如果第3步才出错跳转到这里来	
flag3:release_mem_region(GPJ0CON_PA, 4);// 如果第2步才出错跳转到这里来
flag2:cdev_del(&test_cdev);// 如果第1步才出错跳转到这里来
flag1:// 在这里把第1步做成功的东西给注销掉unregister_chrdev_region(mydev, MYCNT);
注:使用cdev_alloc,cdev_init的替代(重点)

在这里插入图片描述

二、字符设备驱动注册代码分析

2.1 旧接口register_chrdev

register_chrdev注册函数

=》 __register_chrdev -- 内核级函数==》 __register_chrdev_region -- 内核级函数==》cdev_alloc -- 让内核为这个结构体分配内存的。===》cdev_add -- 向内核里面添加一个驱动,注册驱动。

函数 __register_chrdev_region() 主要执行以下步骤:

  1. 分配一个新的 char_device_struct 结构,并用 0 填充。
  2. 如果申请的设备编号范围的主设备号为 0,那么表示设备驱动程序请求动态分配一个主设备号。动态分配主设备号的原则是从散列表的最后一个桶向前寻找,那个桶是空的,主设备号就是相应散列桶的序号。所以动态分配的主设备号总是小于 256,如果每个桶都有字符设备编号了,那动态分配就会失败。
  3. 根据参数设置 char_device_struct 结构中的初始设备号,范围大小及设备驱动名称。
  4. 计算出主设备号所对应的散列桶,为新的 char_device_struct 结构寻找正确的位置。同时,如果设备编号范围有重复的话,则出错返回。
  5. 将新的 char_device_struct 结构插入散列表中,并返回 char_device_struct 结构的地址。

2.2 新接口register_chrdev_region & alloc_chrdev_region

register_chrdev_region== 动态分配主次设备号
=》__register_chrdev_region

register_chrdev_region() 函数用于分配指定的设备编号范围。如果申请的设备编号范围跨越了主设备号,它会把分配范围内的编号按主设备号分割成较小的子范围,并在每个子范围上调用 __register_chrdev_region() 。如果其中有一次分配失败的话,那会把之前成功分配的都全部退回。

alloc_chrdev_region==让内核自动给我们分配设备号
=》__register_chrdev_region

alloc_chrdev_region() 函数用于动态申请设备编号范围,通过指针参数返回实际获得的起始设备编号。

2.3 注销

注销和注册分配字符设备编号范围类似,内核提供了两个注销字符设备编号范围的函数,分别是 unregister_chrdev_region() 和 unregister_chrdev() 。它们都调用__unregister_chrdev_region函数,这个就不分析了。

三、自动创建字符设备驱动的设备文件

(1) 整体流程回顾
(2) 使用mknod创建设备文件的缺点
(3) 能否自动生成和删除设备文件

3.1 解决方案:udev(嵌入式中用的是mdev)

(1) 什么是udev?应用层的一个应用程序
(2) 内核驱动和应用层udev之间有一套信息传输机制(netlink协议)
(3) 应用层启用udev,内核驱动中使用相应接口
(4) 驱动注册和注销时信息会被传给udev,由udev在应用层进行设备文件的创建和删除
在这里插入图片描述

3.2 内核驱动设备类相关函数

  • class_create/class_destroy
  • device_create/device_destroy
#include <linux/device.h>  //相关的函数包含在这个头文件里面static dev_t mydev;
static struct class *test_class;// 注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息// 给udev,让udev自动创建和删除设备文件test_class = class_create(THIS_MODULE, "aston_class");if (IS_ERR(test_class))return -EINVAL;// 最后1个参数字符串,就是我们将来要在/dev目录下创建的设备文件的名字// 所以我们这里要的文件名是/dev/test111device_create(test_class, NULL, mydev, NULL, "test111");//在注销设备驱动之前device_destroy(test_class, mydev);class_destroy(test_class);

在这里插入图片描述

四、设备类相关代码分析

4.1 sysfs文件系统

因为udev需要sysfs文件系统的支持(sysfs文件系统只在linux-2.6内核以上才有),所以它存在于Linux-2.6版本之后的内核。udev借助于netlink协议在内核驱动和应用层之间传递信息。当内核中的驱动完成注册和注销时,信息会被传送给应用层的udev,udev便会自动地完成设备文件的创建和删除。

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类, 内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
在这里插入图片描述
2、class_creat & device_createt
(1) class_creat 树形调用的主要的函数
class_create();
__class_create();
__class_register();
kset_register();
kobject_uevent();

(2) device_createt 树形调用的主要的函数

struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...) ------------------- 参数”...”就是可变参数, 更多的时候表示了次设备号。
{
.........
}

例如:此图中 tty 就表示主设备号, 后面的14… 15等表示次设备号
在这里插入图片描述

device_createt();device_create_vargs();kobject_set_name_vargs();device_register();device_add();kobject_add(); //真正的把设备添加进去了device_create_file();//下面这些函数都是操作 sysfs 的函数device_create_sys_dev_entry();devtmpfs_create_node();device_add_class_symlinks();device_add_attrs();device_pm_add();kobject_uevent();

device_create_file() 函数创建的就是 dev 目录(sysfs函数实现的),

int device_create_file(struct device *dev,const struct device_attribute *attr)
{int error = 0;if (dev) {....error = sysfs_create_file(&dev->kobj, &attr->attr);}return error;
}
EXPORT_SYMBOL_GPL(device_create_file);

参数 uevent_attr

static struct device_attribute uevent_attr =__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); -------- 给出的属性, 参数中 show_uevent, store_uevent 表示读和存储。

所以在执行cat dev 读取 dev 这个文件时, 内核调用的就是 show_uevent 函数。

五、静态映射表建立过程分析

5.1 建立映射表的三个关键部分

(1) 主映射表:映射表具体物理地址和虚拟地址的值相关的宏定义
在这里插入图片描述

(2) 映射表建立函数。该函数负责由(1)中的映射表来建立linux内核的页表映射关系。

在kernel/arch/arm/mach-s5pv210/mach-smdkc110.c中的smdkc110_map_io函数
smdkc110_map_io();s5p_init_io();iotable_init();  // 引出io 描述符的概念

  经过分析,真正的内核移植时给定的静态映射表在arch/arm/plat-s5p/cpu.c中的s5p_iodesc,本质是一个结构体数组,数组中每一个元素就是一个映射,这个映射描述了一段物理地址到虚拟地址之间的映射。这个结构体数组所记录的几个映射关系被iotable_init所使用,该函数负责将这个结构体数组格式的表建立成MMU所能识别的页表映射关系,这样在开机后可以直接使用相对应的虚拟地址来访问对应的物理地址。
注:该部分重点记录分析方法。
在这里插入图片描述
// 由上图可以看出,内存管理最小的表是4K。

(3) 开机时调用映射表建立函数

问题:开机时(kernel启动时)smdkc110_map_io怎么被调用的?
函数调用层级:
start_kernel();setup_arch();paging_init();devicemaps_init();if (mdesc->map_io)mdesc->map_io();

六、动态映射结构体方式操作寄存器

知识回顾:之前的动态映射,每个寄存器地址是单独映射的,要进行多次。
(1) 仿效真实驱动中,用结构体封装的方式来进行单次多寄存器的地址映射。

实验代码:
typedef struct GPJ0REG
{volatile unsigned int gpj0con;volatile unsigned int gpj0dat;
} gpj0_reg_t;#define GPJ0_REGBASE	0xe0200240    //物理地址
gpj0_reg_t *pGPJ0REG;// 使用动态映射的方式来操作寄存器if (!request_mem_region(GPJ0_REGBASE, sizeof(gpj0_reg_t), "GPJ0REG"))return -EINVAL;pGPJ0REG = ioremap(GPJ0_REGBASE, sizeof(gpj0_reg_t));// 映射之后用指向结构体的指针来进行操作// 指针使用->结构体内元素的方式来操作各个寄存器pGPJ0REG->gpj0con = 0x11111111;pGPJ0REG->gpj0dat = ((0<<3) | (0<<4) | (0<<5));		// 亮// 解除映射iounmap(pGPJ0REG);release_mem_region(GPJ0_REGBASE, sizeof(gpj0_reg_t));

七、内核提供的读写寄存器接口

  1. 内核提供的寄存器读写接口(在不同的架构下,可移植性高)
    (1) writel和readl,在3.字符设备驱动上篇已经提过了。
    (2) iowrite32和ioread32
  2. 代码实践
#define GPJ0CON		S5PV210_GPJ0CON
#define GPJ0DAT		S5PV210_GPJ0DAT#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)#define GPJ0CON_PA	0xe0200240
#define GPJ0DAT_PA 	0xe0200244#define S5P_GPJ0REG(x)		(x)
#define S5P_GPJ0CON			S5P_GPJ0REG(0)
#define S5P_GPJ0DAT			S5P_GPJ0REG(4)unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;static void __iomem *baseaddr;			// 寄存器的虚拟地址的基地址// 使用动态映射的方式来操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))return -EINVAL;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))return -EINVAL;pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);/************/  原始的用解引用指针的方法  /***********/*pGPJ0CON = 0x11111111;         *pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮/***********/ 使用内部读写接口的方法  /***********/测试1:用2次ioremap得到的动态映射虚拟地址来操作,测试成功
writel(0x11111111, pGPJ0CON);
writel(((0<<3) | (0<<4) | (0<<5)), pGPJ0DAT);测试2:用静态映射的虚拟地址来操作,测试成功
writel(0x11111111, GPJ0CON);
writel(((0<<3) | (0<<4) | (0<<5)), GPJ0DAT);测试3:用1次ioremap映射多个寄存器得到虚拟地址,测试成功
if (!request_mem_region(GPJ0CON_PA, 8, "GPJ0BASE"))return -EINVAL;
baseaddr = ioremap(GPJ0CON_PA, 8);writel(0x11111111, baseaddr + S5P_GPJ0CON);
writel(((0<<3) | (0<<4) | (0<<5)), baseaddr + S5P_GPJ0DAT);
  1. 操作寄存器的大致流程
    总结:

注: 本文参考朱有鹏老师网上学员的学习记录博客,根据自己的理解,进行学习汇总,如有侵权,联系本人及时删除。

相关文章:

4. 字符设备驱动高级--- 下篇

文章目录一、字符设备驱动高级1.1 注册字符设备驱动新接口1.1.1 新接口与旧接口1.1.2 cdev介绍1.1.3 设备号1.1.4 编程实践1.1.5 alloc_chrdev_region自动分配设备号1.1.6 中途出错的倒影式错误处理方法二、字符设备驱动注册代码分析2.1 旧接口register_chrdev2.2 新接口regist…...

ChatGPT介绍以及一些使用案例

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…...

PCL 点云高斯混合聚类(GMM)

文章目录 一、简介二、算法实现三、实现效果参考资料一、简介 与k均值使用原型向量来刻画聚类结构不同,高斯混合聚类(Mixture-of-Gaussian)采用了概率模型来表达聚类原型。从名字中就可以知晓,该方法将会结合高斯分布来进行聚类过程,该分布的概率密度函数定义如下所示: p (…...

Docker学习(十六)踩坑,如何将对容器的修改同步到基础镜像中

目录1.背景2.解决方法1&#xff09;将容器文件进行归档2&#xff09;创建一个新的 Dockerfile3&#xff09;构建新的基础镜像3.注意事项4.commit命令踩坑记录1.背景 最近接手了一个docker服务&#xff0c;现需要对镜像进行修改&#xff0c;原始的 Dockerfile 已经丢失&#xff…...

食品与疾病关系预测赛题

和鲸平台数据分析实战 题目&#xff1a;食品与疾病关系预测算法赛道 一、赛题描述 食品与疾病关系预测算法赛道 越来越多的证据表明&#xff0c;食物分子与慢性疾病之间存在关联甚至治疗关系。营养成分可能直接或间接地作用于人类基因组&#xff0c;并调节参与疾病风险和疾病…...

Symbol

Symbol是ES6新增的一种基本数据类型 它用来表示独一无二的值&#xff0c; 通过Symbol函数生成 Symbol前面不能加new ,创建symbol类型指的时候传入一个参数&#xff0c;这个参数需要是字符串 使用Symbol函数创建一个symbol类型值&#xff0c;可以给它传入一个字符串参数&#xf…...

NC65 对上年度反结账,调整数据后重新结账后,对本年度年初重算时系统报错:更新记数错误。

1、对上年度反结账,调整数据后重新结账后,对本年度年初重算时系统报错:更新记数错误。 解决方案: 1、在期初余额节点,按Ctrl+ALT+A重建期初凭证; 2、到结账节点,重建余额表,选择有问题的财务核算账簿,注意:会计期间要放空; 3、到期初余额节点,将刚才删除期初数据的…...

位运算相关

文章目录一、求1的个数二、另类加法三、数组中出现一次的数字四、数组中出现一次的数字变形一、求1的个数 二进制中1的个数 法一&#xff1a;逐位判断 根据与&运算 n&10&#xff0c;说明n的最右边一位为0 n&11&#xff0c;说明n的最右边一位为1 所以思路就是&…...

Linux进程信号(产生、保存、处理)/可重入函数概念/volatile理解/SIGCHLD信号

首先区分一下Linux信号跟进程间通信中的信号量&#xff0c;它们的关系就犹如老婆跟老婆饼一样&#xff0c;没有一毛钱的关系。 信号的概念 信号的概念&#xff1a;信号是进程之间事件异步通知的一种方式&#xff0c;属于软中断。比如&#xff1a;红绿灯是一种信号&#xff0c…...

锯齿数组 - 贪心

文章目录锯齿数组 -贪心&#xff08;不过挺像滑动窗口的&#xff09;1144. 递减元素使数组呈锯齿状锯齿数组 -贪心&#xff08;不过挺像滑动窗口的&#xff09; 1144. 递减元素使数组呈锯齿状 题目链接&#xff1a;1144. 递减元素使数组呈锯齿状 题目大意&#xff1a;给你一个…...

[CVPR 2022] Balanced Contrastive Learning for Long-Tailed Visual Recognition

Contents IntroductionMethodPreliminariesBalanced Contrastive Learning (BCL)Drawbacks of SCLClass-averagingClass-complementLower bound of BCLOptimization with Logit CompensationFrameworkExperimentReferencesIntroduction 作者发现对于在长尾数据集上,Supervised…...

23种设计模式-工厂模式

工厂模式是一种创建型设计模式&#xff0c;它提供了一种创建对象的方式&#xff0c;而无需将具体的对象创建逻辑暴露给客户端。在Java中&#xff0c;工厂模式常常用于创建复杂对象或对象的构造过程涉及到多个步骤的情况。 在Android开发中&#xff0c;工厂模式也经常被使用&am…...

Linux操作系统学习(进程等待)

文章目录进程等待进程等待的必要性如何进程等待waiwaitpid验证进程等待 ​ 我们知道fork函数可以创建一个子进程&#xff0c;而子进程通常是替父进程完成一些任务&#xff0c;而父进程在fork之后需要通过wait/waitpid等待子进程退出。这就是进程等待 进程等待的必要性 通过获…...

Docker学习(十八)load 和 import 命令的区别

Docker 中有两个命令可以将本地文件系统中的 tar 文件导入到 Docker 中&#xff1a;docker load 和 docker import。尽管它们的作用类似&#xff0c;但它们之间有一些重要的区别。 1.使用方式的不同&#xff1a; docker load 的使用示例&#xff1a; docker load --input tes…...

mysql中的事务

在日常生活中,我们会遇到一个场景,那就是在转账的时候,A有1000块钱,要给B转账500,那么最后的结果是A有500,B有500,但是也有可能出现A没有钱了,B有1000块,或者在转账过程中卡顿,这是不符合逻辑的,那么这个时候就要使用事务来解决问题 事务就是把一堆sql语句打包成一个整体,要么…...

《C++ Primer Plus》第18章:探讨 C++ 新标准(9)

编程练习 下面是一个简短程序的一部分&#xff1a; int main() {using namespace std;// list of double deduced from list contentsauto q average_list ({15.4, 10.7, 9.0});cout << q << endl;// list of int deduced from list contentscout << averag…...

记录一次PWM信号异常问题

问题我使用单片机输出PWM控制机械臂&#xff0c;但是控制过程中&#xff0c;机械臂总是会出现莫名的抽动。利用示波器测试PWM信号&#xff0c;发现信号正常。过程&#xff08;1&#xff09;在反复的测试过程中&#xff0c;队友提出&#xff0c;将示波器的地线放在左侧的GND波形…...

简单了解---性能测试

目录 一、什么是性能测试 二、常见的性能测试指标 1、并发 2、响应时间 3、事务 4、点击率 5、吞吐量 6、资源利用率 三、性能测试的分类 1、一般测试 2、负载测试 3、压力测试 4、稳定性测试 四、为什么要做性能测试&#xff1f; 五、影响性能的因素有哪些&…...

1.机器学习笔记第一周

机器学习利用领域&#xff1a; 1&#xff1a;随着网络数据增大&#xff0c;需要搜集用户的数据&#xff0c;做喜好性偏向判断等。 2&#xff1a;只要有数据的&#xff0c;无论是医疗领域&#xff0c;还是基因领域都是需要机器学习来发现数据密码。 3&#xff1a;机器自我学习…...

若依学习(前后端分离版)——启动时发生了啥?(@PostConstruct)(mybatis log free)

我们可以发现若依启动时执行了一些sql我们可以安装一个插件mybatis log free 来更好的进行sql查看 &#xff0c;安装后需要修改一下若依的日志配置如下查看日志&#xff0c;我们发现执行了三个方法&#xff08;&#xff09;&#xff0c;分别查询了一些数据。以第二个方法为例子…...

每日十问9c++-内存模型和名称空间

每日十问9c内存模型和名称空间 1.对于下面的情况&#xff0c;应使用哪种存储方案? a.homer 是函数的形参。 b. secret变量由两个文件共享。 c.topsecret 变量由一个文件中的所有函数共享&#xff0c;但对于其他文件来说是隐藏的。 d. beencalled 记录包含它的函数被调用的次数…...

【python】JSON数据类型与Python数据类型之间的转化

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录JSON格式文件JSON格式序列化与反序列化作用JSON常用数据结构键值对的集合值的有序列表JSON数据类型与Python数据类型之间的转化JSON格式和python的区别读写json文件dump 把python 写到json文件load 把json写…...

Spring——什么是事务?传播行为?事务隔离级别有哪些?

思维导图一、什么是事务&#xff1f;多条DML要么同时成功&#xff0c;要么同时失败Transaction&#xff08;tx&#xff09;二、事务的四个过程&#xff1a;开启事务&#xff08;start transaction&#xff09;执行核心业务代码提交事务&#xff08;如果核心业务处理过程中没有出…...

【项目实战】使用Feign服务间相互调用,其实OpenFeign也没有想象中那么难嘛

一、Feign介绍 openfeign是一个java的http客户端,用来简化http调用 二、Feign架构(来自官方) Feign由五大部分组成, 由于刚开始接触 feign ,比较关注的 clients 跟 encoders/decoders 三、OKHTTP与Feign之间的关系 在Feign中,Client是一个非常重要的组件,Feign最终…...

tun驱动之ioctl

struct ifreq ifr; ifr.ifr_flags | IFF_TAP | IFF_NO_PI; ioctl(fd, TUNSETIFF, (void *)&ifr); 上面的代码的意思是设置网卡信息&#xff0c;并将tun驱动设置为TAP模式。在TAP模式下&#xff0c;在用户空间下调用open打开/dev/net/tun驱动文件&#xff0c;发送(调用send函…...

[acwing周赛复盘] 第 93 场周赛20230304

[acwing周赛复盘] 第 93 场周赛20230304 一、本周周赛总结二、 4867. 整除数1. 题目描述2. 思路分析3. 代码实现三、 4868. 数字替换1. 题目描述2. 思路分析3. 代码实现四、4869. 异或值1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 彩笔了&#xff0c;只A…...

NOIP2022 T4 比赛

P8868 [NOIP2022] 比赛 题目大意 有两个长度为nnn的序列aaa和bbb&#xff0c;有qqq次询问&#xff0c;每次询问给出l,rl,rl,r&#xff0c;求 ∑ilr∑ji1r(max⁡kijak)(max⁡lijbl)\sum\limits_{il}^r\sum\limits_{ji1}^r(\max\limits_{ki}^ja_k)\times(\max\limits_{li}^jb_l…...

计算机组成原理

目录 ❤ 控制器 ❤ 运算器 ❤ 控制器运算器(计算机的中央处理器CPU) ❤ 存储器 内存(主存) 外存 内存和外村的区别 ❤ 输入设备 ❤ 输出设备 ❤ 适配器 ❤ 总线 ❤ 启动计算机的流程 ❤ 机械硬盘 ❤ 固态硬盘 python从小白到总裁完整教程目录:https://b…...

1. 命名规范

1. 命名规范 成绩10开启时间2021年09月17日 星期五 18:00折扣0.8折扣时间2021年11月6日 星期六 00:00允许迟交否关闭时间2021年11月21日 星期日 00:00 家有家法&#xff0c;行有行规。在家有家的规矩&#xff0c;入行有行的规矩。我们计算机一行就有一个命名的规矩&#xff0c;…...

论文投稿指南——中文核心期刊推荐(新闻事业)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…...