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

Linux设备驱动模型之字符设备

Linux设备驱动模型之字符设备

前面我们有介绍到Linux的设备树,这一节我们来介绍一下字符设备驱动。字符设备是在IO传输过程中以字符为单位进行传输的设备,而字符设备驱动则是一段可以驱动字符设备驱动的代码,当前Linux中,字符设备驱动是怎样的呢,下面一起来探讨学习一下。

基础小知识

字符设备框架

如果让你来设计字符设备框架,你会怎么设计呢?不同的开发者会有不同的需求,但是每个人都需要注册字符设备,需要有个地方来保存、管理这些设备驱动信息,在代码中如何保存会更灵活,更合适呢?

注册字符设备

当前Linux内核是通过主设备号与次设备号来定义某一个驱动,其中主设备号从0到CHRDEV_MAJOR_MAX(512) - 1共512个主设备号,而次设备号则是从0到220 - 1(MINORMASK定义)。所以,字符设备驱动第一步就是先向内核注册一个设备号:

#define DAO_NAME        "dao"
static dev_t            dao_devt;// 注册字符设备函数调用
alloc_chrdev_region(&dao_devt, 0, MINORMASK + 1, DAO_NAME);	//注册字符设备号/** 注册字符设备函数声明,通过下面我们可以知道,* dev 是保存主设备号,* baseminor 则是代表可以从该索引开始查找可使用的次设备号* count 代表次设备号可搜索的范围* name 则是该字符设备的名称*/
/*** alloc_chrdev_region() - register a range of char device numbers* @dev: output parameter for first assigned number* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: the name of the associated device or driver** Allocates a range of char device numbers.  The major number will be* chosen dynamically, and returned (along with the first minor number)* in @dev.  Returns zero or a negative error code.*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);

而 alloc_chrdev_region() 是怎么来注册字符设备号的呢?下面来看看它的实现。


/** Register a single major with a specified minor range.** If major == 0 this function will dynamically allocate an unused major.* If major > 0 this function will attempt to reserve the range of minors* with given major.**/
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,int minorct, const char *name)
{struct char_device_struct *cd, *curr, *prev = NULL;int ret;int i;/* 先检查需要申请的主设备号是否在合理范围之内:0 ~ CHRDEV_MAJOR_MAX(512) - 1 */if (major >= CHRDEV_MAJOR_MAX) {pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",name, major, CHRDEV_MAJOR_MAX-1);return ERR_PTR(-EINVAL);}/* 再检查次设备号是否在合理范围之内:0 ~ MINORMASK */if (minorct > MINORMASK + 1 - baseminor) {pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n",name, baseminor, baseminor + minorct - 1, 0, MINORMASK);return ERR_PTR(-EINVAL);}/* 申请一块内存,该内存将是保存字符设备信息的,内核通过结构体(struct char_device_struct)来存储该信息*/cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);if (cd == NULL)return ERR_PTR(-ENOMEM);mutex_lock(&chrdevs_lock);/* 如果调用该函数时传递进来的主设备号为0,则代表内核动态分配主设备号,* 此时通过 find_dynamic_major() 搜索可用的主设备号*/if (major == 0) {ret = find_dynamic_major();	/* 动态搜索可用的主设备号 */if (ret < 0) {pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",name);goto out;}major = ret;	/* 保存搜索到的主设备号到major */}ret = -EBUSY;i = major_to_index(major);/* 确认主、次设备号是否可用 */for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) {if (curr->major < major)continue;if (curr->major > major)break;if (curr->baseminor + curr->minorct <= baseminor)continue;if (curr->baseminor >= baseminor + minorct)break;goto out;}/* 保存字符设备的主、次设备号、设备名称 */cd->major = major;cd->baseminor = baseminor;cd->= minorct;strlcpy(cd->name, name, sizeof(cd->name));/* 将新注册的字符设备添加到chrdevs */if (!prev) {cd->next = curr;chrdevs[i] = cd;} else {cd->next = prev->next;prev->next = cd;}mutex_unlock(&chrdevs_lock);/* 返回配置了设备号的char_device_struct */return cd;
out:mutex_unlock(&chrdevs_lock);kfree(cd);return ERR_PTR(ret);
}

在alloc_chrdev_region中有搜索可用的主设备号以及将设备信息保存到chrdevs,它们究竟是什么呢?

上面我们就问,如果是你来设计这个字符设备驱动框架,你会怎么来设计,那么内核是怎么设计的呢?

// fs/char_dev.c#define CHRDEV_MAJOR_HASH_SIZE 255static struct char_device_struct {struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;char name[64];struct cdev *cdev;              /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

内核定义了一个叫chrdevs的指针数组来保存字符设备信息,这个指针指向的是struct char_device_struct结构体。chrdevs一共255个成员,它们是通过主设备号major来进行排序的。在find_dynamic_major()中,搜索可用主设备号先从chrdevs数组的末端开始查找到CHRDEV_MAJOR_DYN_END(234),如果有元素为空的,则直接返回索引,找到有效的主设备号。简单说就是从 234254,从后往前找,如果数组元素为空,则返回数组索引作为主设备号。如果动态分配(234254)的都找不到了,则从314~511开始查找,注意到chrdevs只有255个元素,所以内核通过major_to_index将主设备号转为cherdev的索引,实际上就是一个chrdevs的元素分别被两个字符设备共用,比如主设备号为59的设备与主设备号为314的公用一个chrdevs元素chrdevs[59]。此时检查chrdevs的元素是否为空,为空则直接返回,如果元素中的主设备已经被填充了,则查找下一次索引的chrdevs元素。

static int find_dynamic_major(void)
{int i;struct char_device_struct *cd;/* 234~254是否为空闲的,为空闲则返回 */for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {if (chrdevs[i] == NULL)return i;}/*234~254已经被占用了,从314~511开始查找 */for (i = CHRDEV_MAJOR_DYN_EXT_START;i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)/* 确认chrdevs元素对应的大于255的主设备号已经被占用了,* 则从再下一个主设备开始查找,该设备号已经被占用*/if (cd->major == i)	break;if (cd == NULL)return i;}return -EBUSY;
}

上述也就是说,每个chrdevs元素可以代表两个主设备号,一个是元素的索引i,一个是i+255,如果两个都被用了,则只能从下一个元素继续查找了。

总结

内核字符设备通过全局静态指针数组chrdevs来保存字符设备信息,数组一共255个元素,每个元素可保存两个不同主设备号的字符设备信息,所以内核一个可申请512个不同的主设备号字符设备。

当驱动调用alloc_chrdev_region()注册设备号之后,终端可以看到以下的信息:

root@root:/# cat /proc/devices
Character devices:1 mem4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input89 i2c90 mtd
116 alsa
128 ptm
136 pts
153 spi
180 usb
189 usb_device
247 ubi0
248 ttyS
249 hidraw
250 rpmb
251 dao		// 这里就是我们注册的字符设备,可以看到它的主设备号是251,名字叫dao
252 watchdog
253 rtc
254 gpiochipBlock devices:8 sd31 mtdblock65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
254 ubiblock
259 blkext

但是此时我们并没有给这个字符设备增加什么操作,下面我们继续看如何添加操作。

字符设备初始化

上面我们向内核申请注册了一个字符设备号,但是该设备号我们还没有将其与字符设备建立关系,那么,又是怎么建立的呢?

static dev_t            dao_devt;	// 上面注册得到的主设备号
static struct cdev      dao_cdev;	// 字符设备结构体
static const struct file_operations dao_fops = {	// 字符设备操作集
}cdev_init(&dao_cdev, &dao_fops);	// 初始化字符设备结构体,并更新其文件字符操作集
cdev_add(&dao_cdev, dao_devt, MINORMASK + 1);	// 将主设备号devt与字符设备 cdev 建立关系,实际上就是更新cdev中的主设备号成员信息。同时在cdev_add中,cdev信息保存到指针数组cdev_map中。

struct file_operations

file_operations 是干什么的呢?Linux中任意一个设备都是文件,针对文件,我们会有各种各样的操作,比如打开、读、写、关闭等,不同的设备它们上述的操作都会不一样,所以设备驱动中需要自定义好设备的操作集,方便应用层可正确使用该设备。

经过上面的操作之后,我们的字符设备已经完成了基本的初始化,在/proc/devices中可以看到以注册的字符设备号,而针对该设备操作的file_operations也已经填充,此时用户空间已经可以对该设备进行操作。

如果完成上述进行操作时,会发现/dev目录下并没有相关的字符节点,此时只能通过mknod /dev/dao c 251 0这样的命令来完成创建/dev/dao节点,执行cat /dev/dao将会看到依次调用到 file_operations 的 open、read、release函数。

那么我们需要如何操作才会在/dev目录下完成节点的注册呢?

创建设备节点

在介绍创建节点之前,我们先来了解class。在内核中,经常会看到xxx_class,内核将设备分为字符设备、块设备、网络设备,同时也会分class。有相同特性的设备为同一个class,class可以自己创建,设备会属于某一个class。那么,上面我们完成字符设备的初始化,但我们并完成将其与某个class绑定在一起。所以,创建设备节点,我们先创建一个class:

#define DAO_CLASS_NAME  "dao"
static struct class     *dao_class;/* 创建一个class,此时在/sys/class目录下看到一个叫dao的文件夹,这个就是我们注册的class */
dao_class = class_create(THIS_MODULE, DAO_CLASS_NAME); 

接着创建设备:

static struct device    *dao_dev;dao_dev = device_create(dao_class, NULL, dao_devt, NULL, DAO_NAME);函数声明:
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...);

通过device_create,我们将dao_devt这个字符设备号与dao_class绑定在一起并创建一个设备。通过device_create函数声明我们可以知道,创建一个设备时可传入该设备的class、父设备、设备号、设备私有数据以及设备名。

device_create主要完成下面的操作:

  • 完成struct device结构体的初始化;
  • 通知平台其他总线,系统有新设备加入;
  • 创建设备uevent节点;
  • 将设备与class建立链接;
  • 增加其他设备节点信息;
  • 如果bus则加入bus;
  • 创建设备attr_dev;
  • sys目录下创建设备节点;
  • dev目录下创建设备节点;
  • 触发bus的probe;

完成上述操作之后,设备完成相应的注册,用户空间可正常的操作该设备。

框架架构图

在这里插入图片描述

例程

// SPDX-License-Identifier: GPL-2.0+
/** dao char device test code** Copyright (c) 2022, dao. All rights reserved.*/#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/cdev.h>
#include <linux/sysfs.h>
#include <linux/fs.h>#define DAO_NAME	"dao"
#define DAO_CLASS_NAME	"dao"static dev_t		dao_devt;
static struct class	*dao_class;
static struct cdev	dao_cdev;
static struct device	*dao_dev;static int dao_dev_open(struct inode *inode, struct file *file)
{pr_info("%s\n", __func__);return 0;
}static int dao_dev_release(struct inode *inode, struct file *file)
{pr_info("%s\n", __func__);return 0;
}static ssize_t dao_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
{pr_info("%s\n", __func__);return 0;
}static long dao_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{pr_info("%s\n", __func__);return 0;
}static const struct file_operations dao_fops = {.owner          = THIS_MODULE,.open           = dao_dev_open,.release        = dao_dev_release,.read           = dao_dev_read,.unlocked_ioctl = dao_dev_ioctl,
};static int __init dao_dev_init(void)
{int ret = 0;ret = alloc_chrdev_region(&dao_devt, 0, MINORMASK + 1, DAO_NAME);if (ret < 0) {pr_err("Error: failed to register dao_dev, err: %d\n", ret);return ret;}cdev_init(&dao_cdev, &dao_fops);cdev_add(&dao_cdev, dao_devt, MINORMASK + 1);pr_info("%s: major %d\n", __func__, MAJOR(dao_devt));dao_class = class_create(THIS_MODULE, DAO_CLASS_NAME);if (IS_ERR(dao_class)) {pr_err("Error: failed to register dao_dev class\n");ret = PTR_ERR(dao_class);goto failed1;}dao_dev = device_create(dao_class, NULL, dao_devt, NULL, DAO_NAME);if (!dao_dev)goto failed2;return 0;failed2:class_destroy(dao_class);
failed1:cdev_del(&dao_cdev);unregister_chrdev_region(dao_devt, MINORMASK + 1);return ret;
}static void __exit dao_dev_exit(void)
{device_destroy(dao_class, dao_devt);class_destroy(dao_class);cdev_del(&dao_cdev);unregister_chrdev_region(dao_devt, MINORMASK + 1);
}module_init(dao_dev_init)
module_exit(dao_dev_exit)

驱动注册

上面介绍了设备的注册,但是驱动的注册流程又是怎样的呢?下面介绍来介绍一下,驱动注册通过driver_register()函数完成,而它主要进行下面几个事情:

  1. 驱动都是挂载在总线上的,同时驱动也是使用名字进行区分的,所以需要确认该总线上没有同样名字的驱动;
  2. 将该驱动挂载到总线上,将驱动添加到klist_drivers这个链表,然后和device进行匹配操作;
  3. 创建驱动的属性配置节点;

具体的device与driver匹配操作我们下一章节进行介绍。

相关文章:

Linux设备驱动模型之字符设备

Linux设备驱动模型之字符设备 前面我们有介绍到Linux的设备树&#xff0c;这一节我们来介绍一下字符设备驱动。字符设备是在IO传输过程中以字符为单位进行传输的设备&#xff0c;而字符设备驱动则是一段可以驱动字符设备驱动的代码&#xff0c;当前Linux中&#xff0c;字符设备…...

Kafka3.0.0版本——消费者(自动提交 offset)

目录 一、自动提交offset的相关参数二、消费者&#xff08;自动提交 offset&#xff09;代码示例 一、自动提交offset的相关参数 官网文档 参数解释 参数描述enable.auto.commi默认值为 true&#xff0c;消费者会自动周期性地向服务器提交偏移量。auto.commit.interval.ms如果…...

【业务功能116】微服务-springcloud-springboot-Kubernetes集群-k8s集群-KubeSphere-公共服务 DNS

kubernetes集群公共服务 DNS 一、软件安装 # yum -y install bind二、软件配置 # vim /etc/named.conf # cat -n /etc/named.conf1 //2 // named.conf3 //4 // Provided by Red Hat bind package to configure the ISC BIND named(8) DNS5 // server as a caching only…...

马斯洛的动机与人格、需求层次理论

马斯洛是在研究动机&#xff08;Motivation&#xff09;时&#xff0c;才提出需求层次作为理论基础来支持动机理论的。所谓动机&#xff0c;就是人类的行为到底是由什么驱动&#xff0c;其实是对人类行为的当下原动力&#xff0c;区别于过去、未来或者是有可能起作用的动力。 …...

TCP/IP网络传输模型及协议

文章目录 前言一、TCP/IP协议二、协议层报文间的封装与拆封1.发送数据2.接收数据前言 TCP/IP模型由OSI七层模型演变而来: 国际标准化组织 1984年提出了模型标准,简称 OSI(Open Systems Interconnection Model)七层模型: 物理层(Physics) :提供机械、电气、功能和过程特性…...

git 推送出现fatal: The remote end hung up unexpectedly解决方案

在使用git更新或提交项目时候出现 "fatal: The remote end hung up unexpectedly " 的报错&#xff1b; 报错的原因原因是推送的文件太大。 下面给出解决方法 方法一&#xff1a; 修改提交缓存大小为500M&#xff0c;或者更大的数字 git config --global http.po…...

Hive内置函数字典

写在前面&#xff1a;HQL同SQL有很多的类似语法&#xff0c;同学熟悉SQL后一般学习起来非常轻松&#xff0c;写一篇文章列举常用函数&#xff0c;方便查找和学习。 1. 执行模式 1.1 Batch Mode 批处理模式 当使用-e或-f选项运行$ HIVE_HOME / bin / hive时&#xff0c;它将以…...

svg 知识点总结

1. 引用 svg&#xff0c;直接用 img 标签 <img src"帐篷.svg" alt"露营">2. 画 svg 各种图形。 矩形 rect圆角矩形 rect圆圈 circle椭圆 ellipse线段 line折线 polyline多边形 polygon路径 path <svg width"200" height"250&qu…...

开源库源码分析:OkHttp源码分析(二)

开源库源码分析&#xff1a;OkHttp源码分析&#xff08;二&#xff09; 导言 上一篇文章中我们已经分析到了OkHttp对于网络请求采取了责任链模式&#xff0c;所谓责任链模式就是有多个对象都有机会处理请求&#xff0c;从而避免请求发送者和接收者之间的紧密耦合关系。这篇文章…...

校园地理信息系统的设计与实现

校园地理信息系统的设计与实现 摘 要 与传统的地图相比较&#xff0c;地理信息系统有着不可比拟的优势&#xff0c;信息量大&#xff0c;切换方便&#xff0c;可扩展性强。本文阐述了研究地理信息系统的背景、目的、方法&#xff0c;介绍了一个实用的、方便可靠的校园地理信息…...

Vulnhub实战-prime1

前言 VulnHub 是一个面向信息安全爱好者和专业人士的虚拟机&#xff08;VM&#xff09;漏洞测试平台。它提供了一系列特制的漏洞测试虚拟机镜像&#xff0c;供用户通过攻击和漏洞利用的练习来提升自己的安全技能。本次&#xff0c;我们本次测试的是prime1。 一、主机发现和端…...

Scala学习笔记

Scala学习笔记 Scala笔记一、学习Scala的目的二、Scala的基本概念2.1 JDK1.8版本的新特性2.2 Scala的运行机制 三、Scala的基本语法3.1 Scala中输出语句、键盘输入、注释语法3.1.1 Scala注释三种&#xff0c;和Java一模一样的3.1.2 Scala键盘输入3.1.3 Scala输出 3.2 Scala变量…...

虹科分享 | 软件供应链攻击如何工作?如何评估软件供应链安全?

说到应用程序和软件&#xff0c;关键词是“更多”。在数字经济需求的推动下&#xff0c;从简化业务运营到创造创新的新收入机会&#xff0c;企业越来越依赖应用程序。云本地应用程序开发更是火上浇油。然而&#xff0c;情况是双向的&#xff1a;这些应用程序通常更复杂&#xf…...

gRpc入门和springboot整合

gRpc入门和springboot整合 一、简介 1、gprc概念 gRpc是有google开源的一个高性能的pc框架&#xff0c;Stubby google内部的rpc,2015年正式开源&#xff0c;云原生时代一个RPC标准。 tips:异构系统&#xff0c;就是不同编程语言的系统。 2、grpc核心设计思路 grpc核心设计…...

基于FPGA点阵显示屏设计-毕设

本设计是一1616点阵LED电子显示屏的设计。整机以EP2C5T144C8N为主控芯片,介绍了以它为控制系统的LED点阵电子显示屏的动态设计和开发过程。通过该芯片控制一个行驱动器74HC154和两个列驱动器74HC595来驱动显示屏显示。该电子显示屏可以显示各种文字或单色图像,采用4块8 x 8点…...

Rocky9.2基于http方式搭建局域网yum源

当前负责的项目有几十台Linux服务器,在安装各类软件的时候需要大量依赖包,而项目部署的环境属于内网环境,与Internet网完全隔离,无法采用配置网络yum源的方式安装rpm包,直接在每台linux服务器上配置本地yum源也比较麻烦,而采用直接下载rpm包用rpm命令安装更是费时费力。所…...

Android 串口通讯

Serial Port Android 串口通讯 arm64-v8a、armeabi-v7a、x86、x86_64 AAR 名称操作serial.jar下载arm64-v8a下载armeabi-v7a下载x86下载x86_64下载arm-zip下载x86-zip下载 Maven 1.build.grade | setting.grade repositories {...maven { url https://jitpack.io } }2./a…...

论如何在Android中还原设计稿中的阴影

每当设计稿上注明需要添加阴影时&#xff0c;Android上总是显得比较棘手&#xff0c;因为Android的阴影实现方式与Web和iOS有所区别。 一般来说阴影通常格式是有&#xff1a; X: 在X轴的偏移度 Y: 在Y轴偏移度 Blur: 阴影的模糊半径 Color: 阴影的颜色 何为阴影 但是在A…...

Hadoop生态圈中的Flume数据日志采集工具

Hadoop生态圈中的Flume数据日志采集工具 一、数据采集的问题二、数据采集一般使用的技术三、扩展&#xff1a;通过爬虫技术采集第三方网站数据四、Flume日志采集工具概述五、Flume采集数据的时候&#xff0c;核心是编写Flume的采集脚本xxx.conf六、Flume案例实操1、采集一个网络…...

FFmpeg获取媒体文件的视频信息

视频包标志位 代码 printf("index:%d\n", in_stream->index);结果 index:0视频帧率 // avg_frame_rate: 视频帧率,单位为fps&#xff0c;表示每秒出现多少帧 printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));结果 fps:29.970070fps…...

io概述及其分类

一、IO概念 • I/O 即输入Input/ 输出Output的缩写&#xff0c;其实就是计算机调度把各个存储中&#xff08;包括内存和外部存储&#xff09;的数据写入写出的过程&#xff1b; I : Input O : Output 通过IO可以完成硬盘文件的读和写。 • java中用“流&#xff08;stream&am…...

前端面试话术集锦第 14 篇:高频考点(React常考基础知识点)

这是记录前端面试的话术集锦第十四篇博文——高频考点(React常考基础知识点),我会不断更新该博文。❗❗❗ 1. 生命周期 在V16版本中引入了Fiber机制。这个机制一定程度上的影响了部分生命周期的调用,并且也引入了新的2个API来解决问题。 在之前的版本中,如果你拥有一个很…...

UI/UX+前端架构:设计和开发高质量的用户界面和用户体验

引言 随着数字化和互联网的普及&#xff0c;越来越多的企业和组织需要高质量的用户界面和用户体验&#xff0c;以及可靠、高效的前端架构。UI/UX设计师和前端架构师可以为这些企业和组织提供所需的技术和创意支持。本文将介绍UI/UX前端架构这个方向&#xff0c;包括设计原则、…...

长尾关键词挖掘软件-免费的百度搜索关键词挖掘

嗨&#xff0c;大家好&#xff01;今天&#xff0c;我想和大家聊一聊长尾关键词挖掘工具。作为一个在网络世界里摸爬滚打多年的人&#xff0c;我对这个话题有着一些个人的感悟和见解&#xff0c;希望能与大家分享。 首先&#xff0c;让我坦白一点&#xff0c;长尾关键词挖掘工具…...

React Native 环境配置(mac)

React Native 环境配置&#xff08;mac&#xff09; 1.Homebrew2.Node.js、WatchMan3.Yarn4.Android环境配置1.安装JDK2.下载AndroidStudio1.国内配置 Http Proxy2.安装SDK1.首先配置sdk的路径2.SDK 下载 3.创建模拟器4.配置 ANDROID_HOME 环境变量 5.IOS环境1.升级ruby&#x…...

CAD for JS:VectorDraw web library 10.1004.1 Crack

VectorDraw web library经过几年的研究&#xff0c;通过互联网展示或工作的可能性并拒绝了各种项目&#xff0c;我们最终得出的结论是&#xff0c;在 javascript 的帮助下&#xff0c;我们将能够在 Microsoft IE 以外的互联网浏览器中通过网络演示矢量图形&#xff08;支持 ocx…...

代码管理工具git1

ctrl 加滚轮 放大字体 在计算机任意位置单击右键&#xff0c;选择&#xff1a;&#xff1a;Git Bash Here git version git清屏命令&#xff1a;ctrl L查看用户名和邮箱地址&#xff1a; $ git config user.name$ git config user.email修改用户名和邮箱地址&#xff1a;$ git…...

层次聚类分析

1、python语言 from scipy.cluster import hierarchy # 导入层次聚类算法 import matplotlib.pylab as plt import numpy as np# 生成示例数据 np.random.seed(0) data np.random.random((20,1))# 使用树状图找到最佳聚类数 Z hierarchy.linkage(data,methodweighted,metric…...

Jmeter性能实战之分布式压测

分布式执行原理 1、JMeter分布式测试时&#xff0c;选择其中一台作为调度机(master)&#xff0c;其它机器作为执行机(slave)。 2、执行时&#xff0c;master会把脚本发送到每台slave上&#xff0c;slave 拿到脚本后就开始执行&#xff0c;slave执行时不需要启动GUI&#xff0…...

学信息系统项目管理师第4版系列08_管理科学基础

1. 科学管理的实质 1.1. 反对凭经验、直觉、主观判断进行管理 1.2. 主张用最好的方法、最少的时间和支出&#xff0c;达到最高的工作效率和最大的效果 2. 资金的时间价值与等值计算 2.1. 资金的时间价值是指不同时间发生的等额资金在价值上的差别 2.2. 把资金存入银行&…...

丰台网站建设/淘宝定向推广

UV′(UV)′−U′VUV(UV)-UVUV′(UV)′−U′V ∫UV′dxUV−∫U′Vdx\int UV dx UV - \int UV dx ∫UV′dxUV−∫U′Vdx −−−−−−−−−−−−−−−−−−−−−−−−−−-------------------------- −−−−−−−−−−−−−−−−−−−−−−−−−− ↓\downarrow ↓…...

上海专业网站建设哪家好/seo计费系统

Python2的字符串有两种&#xff1a;str 和 unicode&#xff1b;Python3的字符串也有两种&#xff1a;str 和 bytes。bytes可以是任何二进制数据&#xff0c;文本/图片/视频/音频等等。str就是文本。str与bytes互转b b"example" # bytes objects "example"…...

沽源网站建设案例/西安seo服务

作者按&#xff1a;7月28日周日下午&#xff0c;在TDengine物联网大数据平台开源两周后&#xff0c;涛思数据联合CSDN举办了「TDengine 和他的小伙伴们」Beijing Meetup活动。活动后&#xff0c;我应CSDN邀约&#xff0c;撰写此文&#xff0c;讲述了我开发TDengine的新路历程&a…...

怎么做电脑端网站设计稿/电脑培训学校哪家好

1 继承的概念 在Java中&#xff0c;类的继承是指在一个现有类的基础上去构建一个新的类&#xff0c;构建出来的新类被称作子类&#xff0c;现有类被称作父类&#xff0c;子类会自动拥有父类所有可继承的属性和方法。 在程序中&#xff0c;如果想声明一个类继承另一个类&…...

微网站与手机网站/口碑营销5t理论

http://www.cnblogs.com/chenergougou/p/7056557.html openstack RPC通信 OpenStack 的主要组件有 Nova、Cinder、Neutron、Glance 等&#xff0c;分别负责云平台的计算、存储、网络资源管理。openstack 各组件之间是通过 REST 接口进行相互通信&#xff0c;而各组件内部则采用…...

什么网站做国外批发/360站长平台

转载自 IntegerCache的妙用和陷阱 考虑下面的小程序&#xff0c;你认为会输出为什么结果&#xff1f; public class Test {public static void main(String[] args) {Integer n1 123;Integer n2 123;Integer n3 128;Integer n4 128;System.out.println(n1 n2);System.out.…...