【香橙派系列教程】(五)Linux的热拔插UDEV机制

【五】Linux的热拔插UDEV机制

在上一篇中我们发现,当手机接入开发板时,系统并不认识,当我们在/etc/udev目录下创建一个规则后,就可以通过adb访问到手机了,这里到底是怎么回事?

文章目录

  • 【五】Linux的热拔插UDEV机制
    • 1.简介
    • 2.守护进程(重要概念)
    • 3.守护进程开发
      • 1.daemon()函数
      • 2.time()函数
      • 3.asctime()函数
      • 4.localtime()函数
      • 5.代码逻辑
    • 4.开机自启动
    • 5.守护进程应用
      • 一、编写判断某进程是否在运行的程序:
      • 二、守护进程不让控制程序退出
    • 6.守护进程和后台进程的区别
    • 7.UDEV的配置文件详解
      • 1.使用 udev 的好处
      • 2.udev 添加/删除 设备文件的过程
      • 3.手动去编写一个规则
      • 4.实战:自动挂载U盘

1.简介

  • udev是一个设备管理工具,udev以守护进程的形式运行,通过侦听内核发出来的uevent来管理/dev目录下的设备文件。
  • udev在用户空间运行,而不在内核空间 运行。它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。
  • 设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。

当一个硬件接入时,内核是首先知道的,而在用户应用层,并不知道这个设备接入了,如何将他们连接起来->udev机制

/dev/bus/usb目录底下,进入001文件中,可以看到003是我接入的手机文件,当我拔掉之后就没有了

当设备插入,内核知道后,发出uevent,udev设备工具一直在监听,监听到uevent后,根据规则文件的规则(可以是自己创建的),判断它是什么类型的设备,并在/dev下面为它创建对应的设备文件,这样应用层就通过文件句柄访问到设备了。

创建规则文件

创建规则文件是为了让udev机制能认识他是usb类型设备,或者其他类型设备,并为usb设备创建文件,否则即使内核识别到了usb设备,发出uevent,udev也不会在/dev下面创建对应的设备文件。

如何建立自己的规则文件

//1.进入规则目录
cd /etc/udev/rules.d///2.比如创建usb热拔插的规则:
sudo vim 51-android.rules//在51-android.rules 中输入
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"

2.守护进程(重要概念)

1.简介

**Linux Daemon(守护进程)**是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。

Linux系统的大多数服务器就是通过守护进程实现的。【常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等】

守护进程的名称通常以d结尾

UDEV守护进程,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。

2.基本特点

  • 生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。
  • 守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出
  • 守护进程是在后台运行,不会占着终端,终端可以执行其他命令
  • 一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程

linux操作系统本身是有很多的守护进程在默默执行,维持着系统的日常活动。大概30-50个

那么不是说:你是守护进程,操作系统就知道你是守护进程,然后启动它。

需要我们人为的,在开机脚本里面,自动的通过脚本的方式去启动这个进程(下面会讲到)

说明:

  1. PPID = 0:内核进程,跟随系统启动而启动,生命周期贯穿整个系统。
  2. CMD列中:名字带[]这种,叫内核守护进程
  3. 老祖init:也是系统守护进程,它负责启动各运行层次特定的系统服务;所以很多进程的PPID是init,也负责收养孤儿进程。
  4. CMD列中:名字不带[]的普通守护进程(用户集守护进程)。

查看udev进程:

ps -ef|grep udev|grep -v grep  //grep -v grep 是过滤grep进程

3.守护进程开发

1.daemon()函数

**函数功能:**当调用这个函数时,此进程变为守护进程,在后台运行

直接借助damon()函数完成。

//1.头文件
#include <unistd.h>//2.函数原型
int daemon(int nochdir, int noclose);//3.函数参数:
nochdir:为0时表示将当前目录更改至“/”(工作目录)
noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”//4.返回值:
成功则返回0,失败返回-1

2.time()函数

函数功能: 得到当前日历时间或者设置日历时间

//1.头文件
#include <time.h>//2.函数原型 
time_t time(time_t *timer)
/*time_t是一个unsigned long类型*///3.参数说明
1.timer=NULL0时得到当前日历时间(从1970-01-01 00:00:00到现在的秒数),
2.timer=时间数值时,用于设置日历时间。
3.如果 timer不为空,则返回值也存储在变量 timer中。函数返回: 当前日历时间

示例

#include <stdio.h>
#include <time.h>int main ()
{time_t seconds;seconds = time(NULL);printf("自 1970-01-01 起的秒数 = %ld\n", seconds);return(0);
}

3.asctime()函数

简单说就是:一个存时间的结构体,这个存的时间是多少需要我们自己传进去,然后将它以字符串的形式返回来

C 库函数 char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结构 struct timeptr 的日期和时间。

//1.头文件
#include <time.h>//2.函数原型
char *asctime(const struct tm *timeptr)//3.参数
timeptr 是指向 tm 结构的指针,包含了分解为如下各部分的日历时间:
struct tm {int tm_sec;         /* 秒,范围从 0 到 59                			  */int tm_min;         /* 分,范围从 0 到 59               			  */int tm_hour;        /* 小时,范围从 0 到 23                			 */int tm_mday;        /* 一月中的第几天,范围从 1 到 31                  */int tm_mon;         /* 月份,范围从 0 到 11               			 */int tm_year;        /* 自 1900 起的年数                             */int tm_wday;        /* 一周中的第几天,范围从 0 到 6                   */int tm_yday;        /* 一年中的第几天,范围从 0 到 365                 */int tm_isdst;       /* 夏令时                                   */    
};//3.返回值
返回的时间字符串格式为:星期,,,小时::,

示例

#include <stdio.h>
#include <string.h>
#include <time.h>int main()
{struct tm t;t.tm_sec = 0;t.tm_min = 5;t.tm_hour  = 6;t.tm_mday  = 25;t.tm_mon  = 1;t.tm_year  = 59;t.tm_wday  = 5;puts(asctime(&t));return(0);
}

4.localtime()函数

函数功能: 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。

C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。

//1.头文件
#include <time.h>//2.函数原型
struct tm *localtime(const time_t *timer)timer -- 这是指向表示日历时间的 time_t 值的指针
//3.返回值
以tm结构表达的时间

示例

#include <stdio.h>
#include <time.h>int main ()
{time_t rawtime;struct tm *info;char buffer[80];time( &rawtime );info = localtime( &rawtime );printf("当前的本地时间和日期:%s", asctime(info));return(0);
}

5.代码逻辑

每隔十秒向日志目录里写当地的时间,当收到某个信号时,终止输出

为什么要用信号?

用于进程间通信,当我运行此程序后,他会像一个幽灵一样,运行在后台,这个时候我想要给他一些指令,可以通过进程间的通信

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>#include <stdbool.h>   //bool
static bool flag = true;void handler(int sig)
{printf("I got a signal %d\nI'm quitting.\n", sig);flag = false;
}
int main()
{time_t t;int fd;//创建守护进程if(-1 == daemon(0, 0)){printf("daemon error\n");exit(1);}//设置信号处理函数struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;if(sigaction(SIGQUIT, &act, NULL)){printf("sigaction error.\n");exit(0);}//进程工作内容while(flag){fd = open("/home/orangepi/daemon.log", O_WRONLY | O_CREAT | O_APPEND,0644);if(fd == -1){printf("open error\n");}t = time(0);char *buf = asctime(localtime(&t));write(fd, buf, strlen(buf));close(fd);sleep(10);}return 0;
}

运行结果

热知识:

我们知道守护进程有个特点,随着内核的启动而启动,但是我们只是通过调用daemon函数来将这个进程变为了守护进程,内核并不知道。

其次守护进程会被init进程来启动,但是init进程并不会无缘无故的启动你,他有对应的一些规则。一般说,在/etc/init.d目录下

如果在工作中不是专门做守护进程的,这部分了解即可,不用刻意去学习

4.开机自启动

那如果我想要开机启动怎么办?有一种更为简便的方法

第一步:

sudo vi /etc/rc.local 
在这个里面加入绝对路径加程序名字,就可以开机自启动

进入/etc/rc.local 后输入守护进程的路径/home/orangepi/hardwaresoft/daemon/a.out

第二步:

重启测试是否启动了守护进程

sudo reboot

取消要加sudo超级用户权限

5.守护进程应用

**需求:**要求语音刷手机的程序一直保持运行,防止应用程序崩溃意外

在开始前我们先把生成的程序名字修改一下

gcc uartTest.c uartTool.c -o douyinUtils -pthread

一、编写判断某进程是否在运行的程序:

#include <stdio.h>
#include <string.h>
int main()
{FILE *file;char buffer[128] = {'\0'};char *cmd = "ps -elf |grep douyin|grep -v grep";file = popen(cmd, "r");fgets(buffer, 128, file);//1.放到哪里?2.读多少个?3.从哪里读?if(strstr(buffer, "douyin") != NULL){printf("douyinPro is running\n");}else{printf("douyinPro is not running\n");}printf("BUFFER:%s\n",buffer);
}

1.当刷抖音程序没有运行时:

2.当刷抖音程序运行时:

二、守护进程不让控制程序退出

程序功能:作为一个守护进程周期性的检测抖音程序是否在运行,当没有运行时,启动抖音程序

当守护进程收到SIGQUIT信号时,关闭对抖音的守护进程

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
static bool flag = true;
void handler(int sig)
{printf("I got a signal %d\nI'm quitting.\n", sig);flag = false;
}
//判断抖音程序进程是否在后台运行
int judMent()
{FILE *file;char buffer[128] = {'\0'};char *cmd = "ps -elf |grep douyinUtils|grep -v grep";file = popen(cmd, "r");fgets(buffer, 128, file);if(strstr(buffer, "douyinUtils") != NULL){return 0;}else{return -1;}printf("BUFFER:%s\n",buffer);
}
int main()
{time_t t;int fd;//创建守护进程if(-1 == daemon(0, 0)){printf("daemon error\n");exit(1);}//设置信号处理函数struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;if(sigaction(SIGQUIT, &act, NULL)){printf("sigaction error.\n");exit(0);}//进程工作内容while(flag){if( judMent() == -1){//路径根据自己存放的抖音程序system("/home/orangepi/hardwaresoft/douyinUtils /dev/ttyS5&");//&表示在后台运行}sleep(2);}return 0;
}

添加开机自启动

sudo vi /etc/rc.local/home/orangepi/hardwaresoft/douyinUtils /dev/ttyS5 &
/home/orangepi/hardwaresoft/shouhuDouyin
exit 0

6.守护进程和后台进程的区别

  1. 守护进程和终端不挂钩;后台进程能往终端上输出东西(和终端挂钩);
  2. 守护进程关闭终端时不受影响,守护进程不会随着终端的退出而退出;而后台程序会随用户退出而停止

如何成为后台进程

a.运行程序时,&;例如:./a.out &
b.使用ctrl+z,bg等命令

7.UDEV的配置文件详解

1.使用 udev 的好处

我们都知道,所有的设备在 Linux 里都是以设备文件的形式存在。

在早期的 Linux 版本中,/dev 目录包含了所有可能出现的设备的设备文件。很难想象 Linux 用户如何在这些大量的设备文件中找到匹配条件的设备文件。

现在 udev 只为那些连接到 Linux 操作系统的设备产生设备文件。并且 udev 能通过定义一个 udev 规则(rule)来产生匹配设备属性的设备文件,这些设备属性可以是内核设备名称、总线路径、厂商名称、型号、序列号或者磁盘大小等等。

  • 动态管理:当设备添加/删除时,udev 守护进行帧听来自内核的 uevent,以此添加或者删除 /dev 下的设备文件,所以 udev 只为已经连接的设备产生设备文件,而不会在 /dev 下产生大量虚无的设备文件。
  • 自定义命名规则:通过 Linux 默认的规则文件,udev 在 /dev/ 里为所有的设备定义了内核设备名称,比如 /dev/sda、/dev/hda、/dev/fd 等等。由于 udev 是在用户空间(user space)运行,Linux 用户可以通过自定义的规则文件,灵活地产生标识性强的设备文件名,比如 /dev/boot_disk、/dev/root_disk、/dev/color_printer 等等。
  • 设定设备的权限和所有者/组:udev 可以按一定的条件来设置设备文件的权限和设备文件所有者/组。在不同的 udev 版本中,实现的方法不同。

2.udev 添加/删除 设备文件的过程

img

规则文件是 udev 里最重要的部分,默认是存放在 /etc/udev/rules.d/ 下。所有的规则文件必须以".rules" 为后缀名。
下面是一个简单的规则:

KERNEL=="sda", NAME="my_root_disk", MODE="0660"

KERNEL 是匹配键,NAME 和 MODE 是赋值键。这条规则的意思是:如果有一个设备的内核名称为sda,则该条件生效,

执行后面的赋值:在 /dev 下产生一个名为my_root_disk 的设备文件,并把设备文件的权限设为 0660


3.手动去编写一个规则

当我使用dmesg命令时,可以看到手机接入的信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//rules.d//1.USB子系统       2.环境变量:USB设备                  3.权限
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"

1.将之前的手机规则文件删除,打开adb devices调试,依旧是udev出现问题

2.我们可以在/dev/bus/usb/001目录下,看到一些手机接入的信息

002并非固定号,当我拔掉重新插入时,会变化,是随机分配的

查看更为详细的信息输入指令 udevadm info --attribute-walk --name=/dev/bus/usb/001/002

udevadm info --attribute-walk --name=/dev/设备名字

3.根据这些信息,写出我们的一个规则匹配文件

cd  /etc/udev/rules.d/
sudo vi oppo-android.rules

//rules.d
SUBSYSTEM=="usb", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="0002",MODE="0666"

4.重新拔插之后,再次adb devices,可以看到能正常识别到了

4.实战:自动挂载U盘

1.插入U盘,dmesg,存放于/dev/sda文件里面(这个有时候是sda,有时sdb

2.查看U盘里面的数据:

					//挂到mut根目录底下
sudo mount /dev/sda /mnt/
//进入目录
cd /mnt 
//打开
ls

输入命令udevadm info --attribute-walk --name=/dev/sdb查看更为详细的信息

可以查看到U盘的更多数据

udevadm info --attribute-walk --name=/dev/设备名字


拓展知识:卸载操作(umount)

sudo umount /mnt

命令umount 已挂载的设备源(/dev/sdb1) 或已挂载目的点(/mnt)
命令umount 文件系统/挂载点

umount /dev/sdb1 或者 umount /mnt

如果出现device is busy报错,表示该文件系统正在被使用;


1.如果报错mount:unknown filesystem type ‘exfat‘,是因为在 ubuntu下,由于版权的原因,默认不支持 exfat 格式的 u 盘,

USB 连接硬盘时无法正常映射,添加如下命令,并重新插拔硬盘。

sudo apt-get install exfat-utils exfat-fuse

2.如果报错fuse: mountpoint is not empty fuse: if you are sure this is safe, use the ‘nonempty’ mount option

这是因为当挂载路径下已经有这个同名路径时,为了避免冲突,会报这个信息,并且新的文件还挂载不上去。

此时需要添加参数 -o nonempty


3.编写规则自动挂载U盘

第二点所描述的,每次插U盘都需要手动的去挂载U盘,很麻烦,那么现在我们需要编写一个udev规则,当每次U盘插进来时,都能够识别到

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/390093.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

武汉流星汇聚:亚马逊平台消费者众多,助力中国卖家销售额大幅增长

在全球电商的浩瀚星空中&#xff0c;亚马逊凭借其庞大的消费者规模和强大的市场影响力&#xff0c;为无数商家特别是中国卖家提供了前所未有的发展机遇。近年来&#xff0c;越来越多的中国卖家选择通过亚马逊平台&#xff0c;将优质产品直接送达全球消费者的手中&#xff0c;并…

精选3款国内wordpress 主题,建站首选

WordPress作为一款功能强大且易于使用的建站平台&#xff0c;已经成为了许多企业和个人搭建网站的首选。为了帮助大家更好地选择适合自己的WordPress主题&#xff0c;小编将为大家推荐三款国内优秀的WordPress主题&#xff1a;子比主题、OneNav主题和RiTheme主题。 1.子比主题…

JavaScript ES6语法详解(下)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是码喽的自我修养&#xff01;今天给大家分享JavaScript ES6语法详解(下)&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到带大家&#xff0c;欢迎收藏关注…

Qt Creator 与 ESP-IDF QEMU 模拟器使用指南

标题: Qt Creator 与 ESP-IDF QEMU 模拟器使用指南 概要: 本文为开发者提供了使用 Qt Creator 和 ESP-IDF QEMU 模拟器进行 ESP32 开发的详细指南&#xff0c;包括环境准备、项目创建和编译、模拟器设置、编程和调试等方面的内容。通过本指南&#xff0c;可以快速上手 Qt Crea…

Learning vtkjs之Calculator

过滤器 公式计算器 Calculator 介绍 The Calculator filter is a fast way to add derived data arrays to a dataset. These arrays can be defined over points, cells, or just field data that is “uniform” across the dataset (i.e., constant over all of space). Va…

手把手教你实现日期类

目录 前言 1.头文件的实现 2.日期类函数各项功能实现 2.1 初始化和打印&#xff08;比较简单&#xff09; 2.2日期大小判断 2.3日期的加减运算 3.日期类的输入输出 4.测试代码参考 结束语 前言 前面我们讲解了类的对象的大部分知识&#xff0c;例如拷贝构造&#xff0c…

优化数据处理效率,解读 EasyMR 大数据组件升级

EasyMR 作为袋鼠云基于云原生技术和 Hadoop、Hive、Spark、Flink、Hbase、Presto 等开源大数据组件构建的弹性计算引擎。此前&#xff0c;我们已就其展开了多方位、多角度的详尽介绍。而此次&#xff0c;我们成功接入了大数据组件的升级和回滚功能&#xff0c;能够借助 EasyMR …

乐乐音乐Kotlin版

简介 乐乐音乐Kotlin版&#xff0c;主要是基于ExoPlayer框架开发的Android音乐播放器&#xff0c;它支持lrc歌词和动感歌词(ksc歌词、krc歌词、trc歌词、zrce歌词和hrc歌词等)、多种格式歌词转换器及制作动感歌词、翻译歌词和音译歌词。 编译环境 Android Studio Jellyfish | …

canvas-视频绘制

通过Canvas元素来实时绘制一个视频帧&#xff0c;并在视频帧上叠加一个图片的功能可以当作水印。 获取Canvas元素&#xff1a; let canvas document.getElementById(canvas) 通过getElementById函数获取页面中ID为canvas的Canvas元素&#xff0c;并将其存储在变量canvas中。 …

【C++】C++11(可变参数模板、lambda表达式、包装器)

文章目录 1. 可变参数模板1.1 介绍1.2 emplace系列接口实现 2. lambda表达式2.1 语法介绍2.2 原理 3. 包装器4. bind 1. 可变参数模板 1.1 介绍 可变参数我们在C语言阶段已经了解过了&#xff0c;C语言中叫做可变参数列表&#xff0c;其中使用 ... 代表可变参数。 C语言中的可…

【给嵌入式新人的几条建议(共勉):三-C语言基础怎么补?】

给嵌入式新人的几条建议&#xff08;共勉&#xff09;&#xff1a;三-C语言基础怎么补&#xff1f; 前言1、先回答一个问题&#xff0c;对C语言的害怕到底在哪&#xff1f;&#xff08;纠正认知&#xff09;2、C语言基础&#xff0c;要补全部吗&#xff1f;No2.1 先看下自己属于…

企业个人信息安全保护实践

在数字化浪潮的推动下&#xff0c;个人信息安全问题日益凸显&#xff0c;企业如何在合规的框架下保护个人信息安全&#xff0c;成为了一项重要课题。结合国家标准的个人信息合规审计要求&#xff0c;以下为企业个人信息安全保护的最佳实践路径。 一、构建合规的个人信息保护体…

【文件解析漏洞】

使用windows2003sever服务器 第一个&#xff1a;目录解析 1、打开网站目录&#xff0c;右键打开资源管理器 新建一个1.asp文件 在1.asp目录下新建一个2.txt&#xff0c;输入asp的语句 2、使用本机访问windows2003的IP地址 访问http://192.168.189.155/1.asp/2.txt即可 第…

论文翻译:Large Language Models in Education: Vision and Opportunities

Large Language Models in Education: Vision and Opportunities 文章目录 教育中的大型语言模型&#xff1a;愿景与机遇摘要1 引言2. 教育与LLMsA. 教育背景B. LLMs背景C. 智能教育D. 教育中的LLMs 3. EduLLMs的关键技术4. LLM赋能教育A. LLMs在教育中的应用B. LLMs下教育的特…

Netty4自学笔记 (3) - Netty NIO Server和Client 样例说明

全文详见个人独立博客&#xff1a;Netty4自学笔记 (3) - Netty NIO Server和Client 样例说明 Netty4自学笔记 (3) - Netty NIO Server和Client 样例说明更新节奏缓慢&#xff0c;因为每晚学习注意力不够集中&#xff0c;学习进展缓慢。本还给自己找了一大堆其他理由&#xff0…

适用于个人使用的十大数据恢复工具:综合指南

有许多数据恢复工具和软件可用于帮助恢复丢失或损坏的文件。通过了解您的需求并考虑这里探讨的工具&#xff0c;您将能够选择最佳的数据恢复软件&#xff0c;并希望找回您丢失的宝藏。在本综合指南中&#xff0c;我们将探索个人使用的十大数据恢复工具&#xff0c;重点介绍它们…

自定义CustomRatingBar控件

通过自定义RatingBar的样式实现⭐️⭐️⭐️指示器的方式功能过于受限&#xff0c;而且显示的样式阴影会受到影响。 系统自带显示&#xff1a; 自定义样式&#xff1a; 因此简单自一个符合要求的 CustomRatingBar 支持设置星星数量支持设置星星Rating(float)支持设置空显示…

NICE Seminar(2023-07-16)|演化算法的理论研究到底有什么用?(南京大学钱超教授)

模式定理&#xff08;Schema Theorem&#xff09; 模式定理&#xff08;Schema Theorem&#xff09;是遗传算法&#xff08;Genetic Algorithm, GA&#xff09;的重要理论基础&#xff0c;由约翰霍兰德&#xff08;John Holland&#xff09;在1975年提出。它描述了具有特定模式…

CSS mask-image 实现边缘淡出过渡效果

使用场景 在生产环境中&#xff0c;遇到一个需求&#xff0c;需要在一个深色风格的大屏页面中&#xff0c;嵌入 Google Maps。为了减少违和感&#xff0c;希望地图四边能够淡出过渡。 这里的“淡出过渡”&#xff0c;关键是淡出&#xff0c;而非降低透明度。 基于 Google Ma…

Tecplot安装error找不到指定模块之解决方案

最近有小伙伴反应&#xff0c;在安装Tecplot 2023版本时&#xff0c;参考教程来操作很顺利&#xff0c;但是在开启软件后&#xff0c;有一个error弹窗&#xff0c;内容如下&#xff1a; 随后用中英文翻译&#xff1a;找不到指定模块 同时&#xff0c;软件内部的Tool工具栏打不…