Linux设备模型剖析系列之二(uevent、sysfs)

article/2025/9/7 14:38:54

CSDN链接:

Linux设备模型剖析系列一(基本概念、kobject、kset、kobj_type)

Linux设备模型剖析系列之二(uevent、sysfs)

Linux设备模型剖析系列之三(device和device driver)

Linux设备模型剖析系列之四(BUS)

Linux设备模型剖析系列之五(class)

Linux设备模型系列文章之六(设备资源管理)

Linux设备模型剖析系列文章之七(kobj、kset)

三、uevent

1. uevent的功能

  • uevent是kobject的一部分,用于在kobject状态发生改变时(增加、移除等),通知用户空间程序。用户空间程序收到这样的事件后,会做相应的处理。该机制通常是用来支持热拔插设备的,例如U盘插入后,USB相关的驱动软件会动态创建用于表示该U盘的device结构(相应的也包括其中的kobject),并告知用户空间程序,为该U盘动态的创建/dev/目录下的设备节点,更进一步,可以通知其它的应用程序,将该U盘设备mount到系统中,从而动态的支持该设备。

2. uevent在kernel中的位置

下面图片描述了uevent模块在内核中的位置:

image-20221029144943073

由此可知,uevent的机制是比较简单的,设备模型中任何设备有事件需要上报时,会触发uevent提供的接口。uevent模块准备好上报事件的格式后,可以通过两个途径把事件上报到用户空间:一种是通过kmod模块,直接调用用户空间的可执行文件;另一种是通过netlink通信机制,将事件从内核空间传递给用户空间。有关kmod和netlink,会在其它文章中描述,因此本文就不再详细说明了。

3. uevent的内部逻辑解析

3.1 源代码的位置

uevent的代码比较简单,主要涉及kobject.h和kobject_uevent.c两个文件,如下:

  • include/linux/kobject.h
  • lib/kobject_uevent.c

3.2 数据结构描述

kobject.h定义了uevent相关的常量和数据结构,如下:

  • kobject_action
/* include/linux/kobject.h, line 50 */
enum kobject_action {KOBJ_ADD,KOBJ_REMOVE,    KOBJ_CHANGE, KOBJ_MOVE,KOBJ_ONLINE, KOBJ_OFFLINE,KOBJ_MAX 
};
  • kobject_action定义了event的类型,包括:

ADD/REMOVE: kobject(或上层数据结构)的添加/移除事件。

ONLINE/OFFLINE:kobject(或上层数据结构)的上线/下线事件,即是否使能。

CHANGE: kobject(或上层数据结构)的状态或者内容发生改变。如果设备驱动需要上报的事件不再上面事件的范围内,或者是自定义的事件,可以使用该event,并携带相应的参数。

MOVE: kobject(或上层数据结构)更改名称或者更改parent(意味着在sysfs中更改了目录结构)。

MAX: 事件种类的最大值

  • kobj_uevent_env

    /* include/linux/kobject.h, line 31 */
    #define UEVENT_NUM_ENVP         32 /* number of env pointers */
    #define UEVENT_BUFFER_SIZE      2048 /* buffer for the variables *//* include/linux/kobject.h, line 116 */
    struct kobj_uevent_env {char *envp[UEVENT_NUM_ENVP];int envp_idx;char buf[UEVENT_BUFFER_SIZE];int buflen;
    };
    

    envp,指针数组,用于保存每个环境变量的地址,最多可支持的环境变量数量为UEVENT_NUM_ENVP。

    envp_idx,用于访问环境变量指针数组的index。

    buf,保存环境变量的buffer,最大为UEVENT_BUFFER_SIZE。

    buflen,访问buf的变量。

    • 前面有提到过,在利用kmod向用户空间上报event事件时,会直接执行用户空间的可执行文件。而在Linux系统,可执行文件的执行,依赖于环境变量,因此kobj_uevent_env用于组织此次事件上报时的环境变量
  • kset_uevent_ops

    /* include/linux/kobject.h, line 123 */
    struct kset_uevent_ops {int (* const filter)(struct kset *kset, struct kobject *kobj);const char *(* const name)(struct kset *kset, struct kobject *kobj);int (* const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);
    };
    
    • kset_uevent_ops是为kset量身订做的一个数据结构,里面包含filteruevent两个回调函数,用处如下:

      • filter:当任何kobject需要上报uevent时,它所属的kset可以通过该接口过滤,阻止(过滤掉)不希望上报的event,从而达到从整体上管理的目的。

      • uevent:当任何kobject需要上报uevent时,它所属的kset可以通过该接口统一为这些event添加环境变量。因为很多时候上报uevent时的环境变量都是相同的,因此可以由kset统一处理,就不需要让每个kobject独自添加了。

      • name:该接口可以返回kset的名称。如果一个kset没有合法的名称,则其下的所有kobject将不允许上报uevent。

3.3 内部接口

  • 通过kobject.h,uevent模块提供了如下的API(这些API的实现是在lib/kobject\_uevent.c文件中):

    /* include/linux/kobject.h, line 206 */
    int kobject_uevent(struct kobject *kobj, enum kobject_action action);
    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp[]);
    int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);
    int kobject_action_type(const char *buf, size_t count, enum kobject_action *type);
    

3.3.1 kobject_uevent_env

  • kobject_uevent_env函数以envp为环境变量,上报一个指定action的uevent。环境变量的作用是为执行用户空间程序指定运行环境。具体动作如下:

    1. 查找kobj本身或者其parent是否从属于某个kset,如果不是,则报错返回(如果一个kobject没有加入kset,是不允许上报uevent的);
    2. 查看kobj->uevent_suppress是否设置,如果设置,则忽略所有的uevent上报并返回(可以通过kobject的uevent_suppress标志,管控kobject的uevent的上报);
    3. 如果所属的kset有uevent_ops->filter函数,则调用该函数,过滤此次上报(kset可以通过filter接口过滤不希望上报的event,从而达到整体的管理效果);
    4. 判断所属的kset是否有合法的名称(称作subsystem,和前期的内核版本有区别),否则不允许上报uevent;
    5. 分配一个用于此次上报的、存储环境变量的buffer(结果保存在env指针中),并获得该kobject在sysfs中的路径信息(用户空间软件需要依据该路径信息在sysfs中访问它);
    6. 调用add_uevent_var接口(下面会介绍),将action、路径信息、subsystem等信息,添加到env指针中;
    7. 如果传入的envp不空,则解析传入的环境变量,同样调用add_uevent_var接口,添加到env指针中;
    8. 如果所属的kset存在uevent_ops->uevent接口,调用该接口,添加kset统一的环境变量到env指针;
    9. 根据ACTION的类型,设置kobj->state_add_uevent_sent和kobj->state_remove_uevent_sent变量,以记录正确的状态;
    10. 调用add_uevent_var接口,添加格式为"SEQNUM=%llu”的序列号;
    11. 如果定义了"CONFIG_NET”,则使用netlink发送该uevent;
    12. 以uevent_helper、subsystem以及添加了标准环境变量(HOME=/,PATH=/sbin:/bin:/usr/sbin:/usr/bin)的env指针为参数,调用kmod模块提供的call_usermodehelper函数,上报uevent。
      1. uevent_helper的内容是由内核配置项CONFIG_UEVENT_HELPER_PATH(位于./drivers/base/Kconfig)决定的(可参考lib/kobject_uevent.c, line 32),该配置项指定了一个用户空间程序(或者脚本),用于解析上报的uevent,例如"/sbin/hotplug”。
      2. call_usermodehelper的作用,就是fork一个进程,以uevent为参数,执行uevent_helper。

3.3.2 kobject_uevent

  • kobject_uevent,和kobject_uevent_env功能一样,只是没有指定任何的环境变量。

    int kobject_uevent(struct kobject *kobj, enum kobject_action action)
    {return kobject_uevent_env(kobj, action, NULL);
    }
    

3.3.3 add_uevent_var

  • add_uevent_var以格式化字符的形式(类似printf、printk等),将环境变量copy到env指针中。

3.3.4 kobject_action_type

  • kobject_action_type将enum kobject_action类型的action转换为字符串。

  • 注意:怎么指定处理uevent的用户空间程序(简称uevent helper)?

    • 上面介绍kobject_uevent_env的内部动作时,有提到,uevent模块通过Kmod上报uevent时,会通过call_usermodehelper函数,调用用户空间的可执行文件(或者脚本,简称*uevent helper* )处理该event。而该uevent helper的路径保存在**uevent_helper数组中。

    • 可以在编译内核时,通过CONFIG_UEVENT_HELPER_PATH配置项,静态指定uevent helper。但这种方式会为每个event fork一个进程,随着内核支持的设备数量的增多,这种方式在系统启动时将会是致命的(可以导致内存溢出等)。因此只有在早期的内核版本中会使用这种方式,现在内核不再推荐使用该方式。因此内核编译时,需要把该配置项留空

    • 在系统启动后,大部分的设备已经ready,可以根据需要,重新指定一个uevent helper,以便检测系统运行过程中的热拔插事件。这可以通过把helper的路径写入到/sys/kernel/uevent_helper文件中实现。实际上,内核通过sysfs文件系统的形式,将uevent_helper数组开放到用户空间,供用户空间程序修改访问,具体可参考./kernel/ksysfs.c中相应的代码,这里不再详细描述。

四、sysfs

1. 前言

sysfs是一个基于RAM的文件系统,它和kobject一起,可以将kernel的数据结构导出到用户空间,以文件目录结构的形式,提供对这些数据结构(以及数据结构的属性)的访问支持。sysfs是一种表示内核对象、对象属性,以及对象关系的一种机制,一般内核对象、属性、以及对象关系组织成树状形式。其中内核对象被映射为用户态的目录;对象属性被映射为用户态的文件,文件在目录下;对象关系被映射成用户空间的符号链接。

sysfs具备文件系统的所有属性,而本文主要侧重其设备模型的特性,因此不会涉及过多的文件系统实现细节,而只介绍sysfs在Linux设备模型中的作用和使用方法。具体包括:

  • sysfs和kobject的关系
  • attribute的概念
  • sysfs的文件系统操作接口

2. sysfs和kobject的关系

在前面有提到过,每一个kobject,都会对应sysfs中的一个目录。因此在将kobject添加到kernel时,create_dir函数会调用sysfs文件系统的创建目录接口,创建和kobject对应的目录,相关的代码如下:

/* lib/kobject.c, line 47 */
static int create_dir(struct kobject *kobj)
{int error = 0;if (kobject_name(kobj)) {error = sysfs_create_dir(kobj);if (!error) {error = populate_dir(kobj);if (error)sysfs_remove_dir(kobj);}}return error;
}
  • 这个函数接受唯一的一个参数,也就是要为其创建目录的kobject的地址。它完成如下操作:
    1. 检查传递进来kobject对象的name的有效性,若无效,则返回0。
    2. 若有效,则调用sysfs_create_dir(kobj)来在sysfs中创建目录。
/* fs/sysfs/dir.c, line 736 */
/**  sysfs_create_dir - create a directory for an object.*/
int sysfs_create_dir(struct kobject * kobj)
{enum kobj_ns_type type;struct sysfs_dirent *parent_sd, *sd;const void *ns = NULL;int error = 0;BUG_ON(!kobj);if (kobj->parent)parent_sd = kobj->parent->sd;elseparent_sd = &sysfs_root;error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);if (!error)kobj->sd = sd;return error;
}
  • 这个函数首先会找到要为其创建目录的kobject的sysfs_dirent对象的父sysfs_dirent,通常是由父kobject的sd字段所指向。若父kobject为NULL,则设为sysfs根目录的sysfs_dirent。
  • 然后调用create_dir(kobj, parent_sd, kobject_name(kobj) , &sd)来创建目录。
  • 最后,若成功,设置kobject的sd字段指向为其新创建的sysfs_dirent,并返回0。若失败则返回错误码。

3. attribute

3.1 attribute的功能概述

在sysfs中,为什么会有attribute的概念呢?其实它是对应kobject而言的,指的是kobject的属性。我们知道,sysfs中的目录描述了kobject,而kobject是特定数据类型变量(如struct device)的体现。因此kobject的属性,就是这些变量的属性。它可以是任何东西,名称、一个内部变量、一个字符串等等。而attribute,在sysfs文件系统中是以文件的形式提供的,即:kobject的所有属性,都在它对应的sysfs目录下以文件的形式呈现。这些文件一般是可读写的,而kernel中定义了这些属性的模块,会根据用户空间的读写操作,记录和返回这些attribute的值。

总结一下:所谓的attibute,就是内核空间和用户空间进行信息交互的一种方法。例如某个驱动定义了一个变量,却希望用户空间程序可以修改该变量,以控制driver的运行行为,那么就可以将该变量以sysfs attribute的形式开放出来。

Linux内核中,attribute分为普通的attribute和二进制attribute,如下:

/* include/linux/sysfs.h, line 26 */
struct attribute
{const char *name;umode_t         mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOCbool ignore_lockdep:1;struct lock_class_key   *key;struct lock_class_key   skey;
#endif
};/* include/linux/sysfs.h, line 100 */
struct bin_attribute {struct attribute    attr;size_t	size;void 	*private;ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t);ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, char *, loff_t, size_t);int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma);
};

struct attribute为普通的attribute,使用该attribute生成的sysfs文件,只能用字符串的形式读写(后面会说为什么)。而struct bin_attribute在struct attribute的基础上,增加了read、write等函数,因此它所生成的sysfs文件可以用任何方式读写

说完基本概念,我们要问两个问题:

kernel怎么把attribute变成sysfs中的文件呢?

用户空间对sysfs的文件进行的读写操作,怎么传递给kernel呢?

下面来看看这个过程。

3.2 attibute文件的创建

在linux内核中,attibute文件的创建是由fs/sysfs/file.c中sysfs_create_file接口完成的,该接口的实现没有什么特殊之处,大多是文件系统相关的操作,和设备模型没有太多的关系,这里先略过不提。

3.3 attibute文件的read和write

看到3.1章节struct attribute的原型时,也许我们会犯嘀咕,该结构很简单啊,name表示文件名称,mode表示文件模式,其它的字段都是内核用于debug kernel Lock的,那文件操作的接口在哪里呢?

不着急,我们去fs/sysfs目录下看看sysfs相关的代码逻辑。

所有的文件系统,都会定义一个struct file_operations变量,用于描述本文件系统的操作接口,sysfs也不例外:

/* fs/sysfs/file.c, line 472 */
const struct file_operations sysfs_file_operations = {.read       = sysfs_read_file,.write      = sysfs_write_file,.llseek     = generic_file_llseek,.open       = sysfs_open_file,.poll       = sysfs_poll,
};

attribute文件的read操作,会由VFS转到sysfs_file_operations的read(也就是sysfs_read_file)接口上,让我们大概看一下该接口的处理逻辑。

/* fs/sysfs/file.c, line 127 */
static ssize_t sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{struct sysfs_buffer *buffer = file->private_data;ssize_t retval = 0;mutex_lock(&buffer->mutex);if (buffer->needs_read_fill || *ppos == 0) {retval = fill_read_buffer(file->f_path.dentry, buffer);if (retval)goto out;}...
}/* fs/sysfs/file.c, line 67 */
static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
{           struct sysfs_dirent *attr_sd = dentry->d_fsdata;struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;const struct sysfs_ops * ops = buffer->ops;...        count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);...
}

read处理看着很简单,sysfs_read_file从file指针中取一个私有指针,转换为一个struct sysfs_buffer类型的指针,以此为参数(buffer),转身就调用fill_read_buffer接口。而fill_read_buffer接口,直接从buffer指针中取出一个struct sysfs_ops指针,调用该指针的show函数,即完成了文件的read操作。

那么后续呢?当然是由ops->show接口接着处理咯。而具体怎么处理,就是其它模块(例如某个driver)的事了,sysfs不再关心(其实,Linux大多的核心代码,都是只提供架构和机制,具体的实现,也就是苦力,留给那些码农吧!这就是设计的魅力)。

不过还没完,这个struct sysfs_ops指针哪来的?好吧,我们再看看open(sysfs_open_file)接口吧。

 /* fs/sysfs/file.c, line 326 */static int sysfs_open_file(struct inode *inode, struct file *file){struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;struct sysfs_buffer *buffer;const struct sysfs_ops *ops;int error = -EACCES;/* need attr_sd for attr and ops, its parent for kobj */if (!sysfs_get_active(attr_sd))return -ENODEV;/* every kobject with an attribute needs a ktype assigned */if (kobj->ktype && kobj->ktype->sysfs_ops)ops = kobj->ktype->sysfs_ops;else {WARN(1, KERN_ERR "missing sysfs attribute operations for kobject: %s\n", kobject_name(kobj));goto err_out;}...buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);if (!buffer)goto err_out;mutex_init(&buffer->mutex);buffer->needs_read_fill = 1;buffer->ops = ops;file->private_data = buffer;...
}

原来和ktype有关系。这个指针是从该attribute所从属的kobject中拿的。再去看一下ktype的定义,里面有一个struct sysfs_ops的指针。

注意:通过注释“every kobject with an attribute needs a ktype assigned”以及其后代码逻辑可知,如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的!

经过确认后,sysfs_open_file从ktype中取出struct sysfs_ops指针,并在随后的代码逻辑中,分配一个struct sysfs_buffer类型的指针(buffer),并把struct sysfs_ops指针保存在其中,随后把buffer指针交给file的private_data,随后read/write等接口便可以取出使用。这是内核中的常用方法!

顺便看一下struct sysfs_ops吧,我想你已经能够猜到了。

/* include/linux/sysfs.h, line 124 */
struct sysfs_ops {ssize_t (*show)(struct kobject *, struct attribute *,char *);ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);const void *(*namespace)(struct kobject *, const struct attribute *);
};

attribute文件的write过程和read类似,这里就不再多说。另外,上面只分析了普通attribute的逻辑,而二进制类型的呢?也类似,去看看fs/sysfs/bin.c吧,这里也不说了。

讲到这里,应该已经结束了,事实却不是如此。上面read/write的数据流,只到kobject(也就是目录)级别哦,而真正需要操作的是attribute(文件)啊!这中间一定还有一层转换!确实,不过又交给其它模块了。 下面我们通过一个例子,来说明如何转换的。

4. sysfs在设备模型中的应用总结

让我们通过设备模型class.c中有关sysfs的实现,来总结一下sysfs的应用方式。

首先,在class.c中,定义了class所需的ktype以及sysfs_ops类型的变量,如下:

/* drivers/base/class.c, line 86 */
static const struct sysfs_ops class_sysfs_ops = {.show      = class_attr_show,.store     = class_attr_store,.namespace = class_attr_namespace,
};  static struct kobj_type class_ktype = {.sysfs_ops  = &class_sysfs_ops,.release    = class_release,.child_ns_type  = class_child_ns_type,
};

由前面章节的描述可知,所有class_type的kobject下面的attribute文件的读写操作,都会交给class_attr_show和class_attr_store两个接口处理。以class_attr_show为例:

/* drivers/base/class.c, line 24 */
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{   struct class_attribute *class_attr = to_class_attr(attr);struct subsys_private *cp = to_subsys_private(kobj);ssize_t ret = -EIO;if (class_attr->show)ret = class_attr->show(cp->class, class_attr, buf);return ret;
}

该接口使用container_of从struct attribute类型的指针中取得一个class模块的自定义指针:struct class_attribute,该指针中包含了class模块自身的show和store接口。下面是struct class_attribute的声明:

/* include/linux/device.h, line 399 */
struct class_attribute {struct attribute attr;ssize_t (*show)(struct class *class, struct class_attribute *attr,char *buf);ssize_t (*store)(struct class *class, struct class_attribute *attr, const char *buf, size_t count);const void *(*namespace)(struct class *class, const struct class_attribute *attr); 
};

因此,所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)中的show和store函数中。这样,每个atrribute文件,实际上对应到一个自定义数据结构变量中了。

  • 【参考资料】
    • 蜗窝科技

http://chatgpt.dhexx.cn/article/UysWHnss.shtml

相关文章

Linux下的uevent

查找linux的uevent节点(find /sys -name uevent),大概有1000多个,那这些节点是怎么实现的呢。 drivers/base/core.c 有如下代码,每创建一个device,都会创建一个event节点 static ssize_t uevent_show(struct device *dev, struc…

Android UEvent事件分析

1.背景概述 众所周知,在安卓系统中有状态栏,在插入外设的时候,会在顶部状态栏显示小图标。 比如,camera设备,耳机设备,U盘,以及电池等等。这些都需要在状态栏动态显示。 从上面这张图片可以看出这些设备都有自己的服务一直在跑,并且都是继承了UEventObserver.java这个…

嵌入式Linux——uevent机制:uevent原理分析

简介: 本文主要介绍uevent机制是什么,并通过代码分析使用uevent机制生成设备节点的过程。而本文将分为两部分,第一部分我们介绍一些预备知识和uevent的原理,而第二部分——通过代码介绍使用uevent机制创建设备节点。 Linux内核&am…

uevent机制:uevent原理分析

简介: 本文主要介绍uevent机制是什么,并通过代码分析使用uevent机制生成设备节点的过程。而本文将分为两部分,第一部分我们介绍一些预备知识和uevent的原理,而第二部分——通过代码介绍使用uevent机制创建设备节点。 声明&#…

900 多道 LeetCode 题解,这个 GitHub 项目值得 Star!

公众号关注 “GitHubPorn” 设为 “星标”,每天带你逛 GitHub! 大家好,我是小 G。 周末风和日丽,适合刷 LeetCode 今天给你们推荐个 GitHub 项目,里面收集了 900 多道 LeetCode 题解,并包含中英文两个版本&…

Leetcode中你的代码执行之后显示超出时间限制

Leetcode中但凡是你的代码执行之后显示超出时间限制 比如: 那么必定是你写的代码不够完善甚至是还存在错误!

升职加薪,必不可少!Python刷题打怪,你要的LeetCode答案都在这里了!

对于还不了解LeetCode的同学,那比较厉害了,估计离大厂还有一步距离! LeetCode,让程序员进阶的在线平台,找工作备战名企技术面试!(文末阅读原文到达学习平台) 本公众号之前陪伴了几期LeetCode的打卡之旅&…

LeetCode 96~100

前言 本文隶属于专栏《LeetCode 刷题汇总》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构请见LeetCode 刷题汇总 正文 幕布 幕布链接 96. 不同的二叉搜索树 题解 官方…

【下载】快速通过Python笔试?学大家一样先把LeetCode答案私藏了

如今学习python的同学越来越多了,也正是同学们秋招时期,去年分享了LeetCode答案后,已经有上百位同学找到小编开始实践这个平台。 LeetCode,让程序员进阶的在线平台,找工作备战名企技术面试!(文末阅读原文到…

面试失败总结,这 577 道 LeetCode 题 Java 版答案你值得拥有

去字节、美团、BAT 等大厂面试,刷 LeetCode 上的数据结构算法题是必修课。许多读者说,刷题的时候经常会遇到困难,想要找一本答案题解做参考。 下面分享几个用 Java 语言实现的开源 LeetCode 题解,也要感谢这些优秀的开源作者们&a…

LeetCode答案大全题(java版)

思路:查找时, 建立索引(Hash查找) 或进行排序(二分查找)。本题缓存可在找的过程中建立索引,故一个循环可以求出解(总是使用未 使用元素查找使用元素,可以保证每一对都被检…

LeetCode数据库题目汇总一(附答案)

1、基础SQL 数据表: dept: deptno(primary key), dname, loc emp: empno(primary key), ename, job, mgr(references emp(empno)), sal, deptno(references dept(deptno)) 1 列出emp表中各部门的部门号,最高工资,最低工资 select max(sal) as 最高工资,min(sal) as 最…

Leetcode各种题型题目+思路+代码(共176道题及答案)

文章目录 第一章:Leetcode 每日很多题 1、Leetcode-1047 删除字符串中的所有相邻重复项 2、剑指 Offer 53 - I. 在排序数组中查找数字 I 3、Leetcode704:二分查找 4、 Leetcode 227:基本计算器II 5、leetcode 224:基本计算器(带括号的计…

Leetcode Top100题目和答案(Java完整版 面试必备)

二刷完剑指Offer后又刷了一遍Leetcode Top 100专栏的题目,听说基本上能涵盖面试的算法题,总体来说收获还是很大的,下面贴出答案,又不懂的可以给我留言,博主会及时解答。 我的github 准备把春招复习的知识都整理到githu…

数据可视化-柱状图-dict结构MACARONS主题

from pyecharts.charts import Bar from pyecharts.faker import Faker from pyecharts.globals import ThemeTypec (Bar({"theme": ThemeType.MACARONS}).add_xaxis(Faker.choose()).add_yaxis("商家A", Faker.values()).add_yaxis("商家B", F…

echarts图表主题--马卡龙macarons--自己配置主题颜色

用过echarts的人都几道,他的官网风格颜色对比强烈,这样儿式的: 大多时候和你的项目风格难免冲突,它有一些风格配置,我觉得马卡龙这个配色就很好: 当然,既然是配置项,肯定不止这一种…

若依vue --雷达图封装使用

大概效果: 如下 1:封装 <template><div :class"className" :style"{ height: height, width: width }" /> </template><script> import echarts from "echarts"; require("echarts/theme/macarons"); // e…

vuejs集成echarts的一些问题

最近在做Beetlex的数据分析平台&#xff0c;在开发这个产品过程中涉及到大量的数据图表展示功能&#xff1b;由于产品前端使用的是vuejs开发&#xff0c;所以在集成echarts或多或少会碰到一些问题&#xff0c;在这里主要讲解一下碰到的问题和解决方法。 在讲解之前先分享一下实…

Echarts主题构建工具的使用

Echarts自带丰富的主题配色&#xff0c;对于有独立的UI设计&#xff0c;主题的应用范围不是很广泛&#xff0c;但是官方的配色还是具有很大的参考价值的。 传送门&#xff1a;https://echarts.apache.org/zh/theme-builder.html 下载或复制以下的主题保存至 *.js 文件&#x…

Echarts-主题切换

从网上搜索了相关的方法&#xff0c;是主题之前的切换&#xff0c;但是用的是下拉框类型的&#xff0c;也可以设置div样式&#xff0c;参考官网那种 设置一个div&#xff0c;通过三个图片的点击效果实现切换主题的功能 我用的jQuery和Echarts是cdn&#xff0c;如果您想引用js文…