鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄
句柄 | handle
int open(const char* pathname,int flags);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int close(int fd);
只要写过应用程序代码操作过文件不会陌生这几个函数,文件操作的几个关键步骤嘛,跟把大象装冰箱分几步一样.先得把冰箱门打开,再把大象放进去,再关上冰箱门.其中最重要的一个参数就是fd,应用程序所有对文件的操作都基于它.fd可称为文件描述符,或者叫文件句柄(handle),个人更愿意称后者. 因为更形象,handle英文有手柄的意思,跟开门一样,握住手柄才能开门,手柄是进门关门的抓手.映射到文件系统,fd是应用层出入内核层的抓手.句柄是一个数字编号, open | creat去申请这个编号,内核会创建文件相关的一系列对象,返回编号,后续通过编号就可以操作这些对象.原理就是这么的简单,本篇将从fd入手,跟踪文件操作的整个过程.
请记住,鸿蒙内核中,在不同的层面会有两种文件句柄:
- 系统文件句柄(
sysfd),由内核统一管理,和进程文件句柄形成映射关系,一个sysfd可以被多个profd映射,也就是说打开一个文件只会占用一个sysfd,但可以占用多个profd,即一个文件被多个进程打开. - 进程文件句柄(
profd),由进程管理的叫进程文件句柄,内核对不同进程中的fd进行隔离,即进程只能访问本进程的fd.举例说明之间的关系:
文件 sysfd profd吃个桃桃.mp4 10 13(A进程)吃个桃桃.mp4 10 3(B进程)容嬷嬷被冤枉.txt 12 3(A进程)容嬷嬷被冤枉.txt 12 3(C进程)
进程文件句柄
在鸿蒙一个进程默认最多可以有256个fd,即最多可打开256个文件.文件也是资源的一种,系列篇多次说过进程是管理资源的,所以在进程控制块中能看到文件的影子files_struct. files_struct可理解为进程的文件管理器,里面只放和本进程相关的文件,线程则共享这些文件.另外子进程也会拷贝一份父进程的files_struct到自己的files_struct上,在父子进程篇中也讲过fork的本质就是拷贝资源,其中就包括了文件内容.
//进程控制块
typedef struct ProcessCB {//..#ifdef LOSCFG_FS_VFSstruct files_struct *files; /**< Files held by the process */ //进程所持有的所有文件,注者称之为进程的文件管理器#endif //每个进程都有属于自己的文件管理器,记录对文件的操作. 注意:一个文件可以被多个进程操作
} LosProcessCB;
struct files_struct {//进程文件表结构体int count; //持有的文件数量struct fd_table_s *fdt; //持有的文件表unsigned int file_lock; //文件互斥锁unsigned int next_fd; //下一个fd
#ifdef VFS_USING_WORKDIRspinlock_t workdir_lock; //工作区目录自旋锁char workdir[PATH_MAX]; //工作区路径,最大 256个字符
#endif
};
fd_table_s为files_struct的成员,负责记录所有进程文件句柄的信息,个人觉得鸿蒙这块的实现有点乱,没有封装好.
struct fd_table_s {//进程fd表结构体unsigned int max_fds;//进程的文件描述符最多有256个struct file_table_s *ft_fds; /* process fd array associate with system fd *///系统分配给进程的FD数组 ,fd 默认是 -1fd_set *proc_fds; //进程fd管理位,用bitmap管理FD使用情况,默认打开了 0,1,2 (stdin,stdout,stderr)fd_set *cloexec_fds;sem_t ft_sem; /* manage access to the file table */ //管理对文件表的访问的信号量
};
file_table_s 记录进程fd和系统fd之间的绑定或者说映射关系
struct file_table_s {//进程fd <--> 系统fd绑定intptr_t sysFd; /* system fd associate with the tg_filelist index */
};
fd_set实现了进程fd按位图管理,系列操作为 FD_SET,FD_ISSET,FD_CLR,FD_ZERO
除以8是因为 char类型占8个bit位.请尝试去理解下按位操作的具体实现.
typedef struct fd_set
{unsigned char fd_bits [(FD_SETSIZE+7)/8];
} fd_set;
#define FD_SET(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] = (u8_t)((p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] | (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))))
#define FD_CLR(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] = (u8_t)((p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7))))
#define FD_ISSET(n,p) FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
#define FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p)))
vfs_procfd.c 为进程文件句柄实现文件,每个进程的 0,1,2 号 fd是由系统占用并不参与分配,即为大家熟知的:
STDIN_FILENO(fd = 0)标准输入 接收键盘的输入STDOUT_FILENO(fd = 1)标准输出 向屏幕输出STDERR_FILENO(fd = 2)标准错误 向屏幕输出
/* minFd should be a positive number,and 0,1,2 had be distributed to stdin,stdout,stderr */if (minFd < MIN_START_FD) {minFd = MIN_START_FD;}
//分配进程文件句柄
static int AssignProcessFd(const struct fd_table_s *fdt, int minFd)
{if (fdt == NULL) {return VFS_ERROR;}if (minFd >= fdt->max_fds) {set_errno(EINVAL);return VFS_ERROR;}//从表中搜索未使用的 fd/* search unused fd from table */for (int i = minFd; i < fdt->max_fds; i++) {if (!FD_ISSET(i, fdt->proc_fds)) {return i;}}set_errno(EMFILE);return VFS_ERROR;
}
//释放进程文件句柄
void FreeProcessFd(int procFd)
{struct fd_table_s *fdt = GetFdTable();if (!IsValidProcessFd(fdt, procFd)) {return;}FileTableLock(fdt);FD_CLR(procFd, fdt->proc_fds); //相应位清0FD_CLR(procFd, fdt->cloexec_fds);fdt->ft_fds[procFd].sysFd = -1; //解绑系统文件描述符FileTableUnLock(fdt);
}
- 分配和释放的算法很简单,由位图的相关操作完成.
fdt->ft_fds[i].sysFd中的i代表进程的fd,-1代表没有和系统文件句柄绑定.- 进程文件句柄和系统文件句柄的意义和关系在 (VFS篇)中已有说明,此处不再赘述,请自行前往翻看.
系统文件句柄
系统文件句柄的实现类似,但它并不在鸿蒙内核项目中,而是在NuttX项目的 fs_files.c 中, 因鸿蒙内核项目中使用了其他第三方的项目,所以需要加进来一起研究才能看明白鸿蒙整个内核的完整实现.具体涉及的子系统仓库如下:
- 子系统注解仓库
在给鸿蒙内核源码加注过程中发现仅仅注解内核仓库还不够,因为它关联了其他子系统,若对这些子系统不了解是很难完整的注解鸿蒙内核,所以也对这些关联仓库进行了部分注解,这些仓库包括:
- 同样由位图来管理系统文件句柄,具体相关操作如下
//用 bitmap 数组来记录文件描述符的分配情况,一位代表一个SYS FD
static unsigned int bitmap[CONFIG_NFILE_DESCRIPTORS / 32 + 1] = {0};
//设置指定位值为 1
static void set_bit(int i, void *addr)
{unsigned int tem = (unsigned int)i >> 5; /* Get the bitmap subscript */unsigned int *addri = (unsigned int *)addr + tem;unsigned int old = *addri;old = old | (1UL << ((unsigned int)i & 0x1f)); /* set the new map bit */*addri = old;
}
//获取指定位,看是否已经被分配
bool get_bit(int i)
{unsigned int *p = NULL;unsigned int mask;p = ((unsigned int *)bitmap) + (i >> 5); /* Gets the location in the bitmap */mask = 1 << (i & 0x1f); /* Gets the mask for the current bit int bitmap */if (!(~(*p) & mask)){return true;}return false;
}
tg_filelist是全局系统文件列表,统一管理系统fd,其中的关键结构体是file,这才是内核对文件对象描述的实体,是本篇最重要的内容.
#if CONFIG_NFILE_DESCRIPTORS > 0struct filelist tg_filelist; //全局统一管理系统文件句柄#endifstruct filelist{sem_t fl_sem; /* Manage access to the file list */struct file fl_files[CONFIG_NFILE_DESCRIPTORS];};struct file{unsigned int f_magicnum; /* file magic number */int f_oflags; /* Open mode flags */struct Vnode *f_vnode; /* Driver interface */loff_t f_pos; /* File position */unsigned long f_refcount; /* reference count */char *f_path; /* File fullpath */void *f_priv; /* Per file driver private data */const char *f_relpath; /* realpath */struct page_mapping *f_mapping; /* mapping file to memory */void *f_dir; /* DIR struct for iterate the directory if open a directory */const struct file_operations_vfs *ops;int fd;};
* `f_magicnum`魔法数字,每种文件格式不同魔法数字不同,`gif`是`47 49 46 38`,`png`是`89 50 4e 47`
* `f_oflags` 操作文件的权限模式,读/写/执行
* `f_vnode` 对应的`vnode`
* `f_pos` 记录操作文件的当前位置
* `f_refcount` 文件被引用的次数,即文件被所有进程打开的次数.
* `f_priv` 文件的私有数据
* `f_relpath` 记录文件的真实路径
* `f_mapping` 记录文件和内存的映射关系,这个在文件映射篇中有详细介绍.
* `ops` 对文件内容的操作函数
* `fd` 文件句柄编号,系统文件句柄是唯一的,一直到申请完为止,当`f_refcount`为0时,内核将回收`fd`.
open | creat | 申请文件句柄
通过文件路径名pathname获取文件句柄,鸿蒙实现过程如下
SysOpen //系统调用AllocProcessFd //分配进程文件句柄do_open //向底层打开文件fp_open //vnode 层操作files_allocatefilep->ops->open(filep) //调用各文件系统的函数指针AssociateSystemFd //绑定系统文件句柄
建一个file对象,i即为分配到的系统文件句柄.
//创建系统文件对象及分配句柄
int files_allocate(struct Vnode *vnode_ptr, int oflags, off_t pos, void *priv, int minfd)//...while (i < CONFIG_NFILE_DESCRIPTORS)//系统描述符{p = ((unsigned int *)bitmap) + (i >> 5); /* Gets the location in the bitmap */mask = 1 << (i & 0x1f); /* Gets the mask for the current bit int bitmap */if ((~(*p) & mask))//该位可用于分配{set_bit(i, bitmap);//占用该位list->fl_files[i].f_oflags = oflags;list->fl_files[i].f_pos = pos;//偏移位list->fl_files[i].f_vnode = vnode_ptr;//vnodelist->fl_files[i].f_priv = priv;//私有数据list->fl_files[i].f_refcount = 1; //引用数默认为1list->fl_files[i].f_mapping = NULL;//暂无映射list->fl_files[i].f_dir = NULL;//暂无目录list->fl_files[i].f_magicnum = files_magic_generate();//魔法数字process_files = OsCurrProcessGet()->files;//获取当前进程文件管理器return (int)i;}i++;}// ...
}
read | write
SysRead //系统调用|读文件:从文件中读取nbytes长度的内容到buf中(用户空间)fd = GetAssociatedSystemFd(fd); //通过进程fd获取系统fdread(fd, buf, nbytes); //调用系统fd层的读函数fs_getfilep(fd, &filep); //通过系统fd获取file对象file_read(filep, buf, nbytes) //调用file层的读文件ret = (int)filep->ops->read(filep, (char *)buf, (size_t)nbytes);//调用具体文件系统的读操作
SysWrite //系统调用|写文件:将buf中(用户空间)nbytes长度的内容写到文件中fd = GetAssociatedSystemFd(fd); //通过进程fd获取系统fdwrite(sysfd, buf, nbytes); //调用系统fd层的写函数fs_getfilep(fd, &filep); //通过系统fd获取file对象file_seek64file_write(filep, buf, nbytes);//调用file层的写文件ret = filep->ops->write(filep, (const char *)buf, nbytes);//调用具体文件系统的写操作
此处仅给出 file_write 的实现
ssize_t file_write(struct file *filep, const void *buf, size_t nbytes)
{int ret;int err;if (buf == NULL){err = EFAULT;goto errout;}/* Was this file opened for write access? */if ((((unsigned int)(filep->f_oflags)) & O_ACCMODE) == O_RDONLY){err = EACCES;goto errout;}/* Is a driver registered? Does it support the write method? */if (!filep->ops || !filep->ops->write){err = EBADF;goto errout;}/* Yes, then let the driver perform the write */ret = filep->ops->write(filep, (const char *)buf, nbytes);if (ret < 0){err = -ret;goto errout;}return ret;errout:set_errno(err);return VFS_ERROR;
}
close
//关闭文件句柄
int SysClose(int fd)
{int ret;/* Process fd convert to system global fd */int sysfd = DisassociateProcessFd(fd);//先解除关联ret = close(sysfd);//关闭文件,个人认为应该先 close - > DisassociateProcessFd if (ret < 0) {//关闭失败时AssociateSystemFd(fd, sysfd);//继续关联return -get_errno();}FreeProcessFd(fd);//释放进程fdreturn ret;
}
- 解除进程
fd和系统fd的绑定关系 close时会有个判断,这个文件的引用数是否为0,只有为0才会真正的执行_files_close
int files_close_internal(int fd, LosProcessCB *processCB){//...list->fl_files[fd].f_refcount--;if (list->fl_files[fd].f_refcount == 0){#ifdef LOSCFG_KERNEL_VMdec_mapping_nolock(filep->f_mapping);#endifret = _files_close(&list->fl_files[fd]);if (ret == OK){clear_bit(fd, bitmap);}}// ... }static int _files_close(struct file *filep){struct Vnode *vnode = filep->f_vnode;int ret = OK;/* Check if the struct file is open (i.e., assigned an vnode) */if (filep->f_oflags & O_DIRECTORY){ret = closedir(filep->f_dir);if (ret != OK){return ret;}}else{/* Close the file, driver, or mountpoint. */if (filep->ops && filep->ops->close){/* Perform the close operation */ret = filep->ops->close(filep);if (ret != OK){return ret;}}VnodeHold();vnode->useCount--;/* Block char device is removed when close */if (vnode->type == VNODE_TYPE_BCHR){ret = VnodeFree(vnode);if (ret < 0){PRINTK("Removing bchar device %s failed\n", filep->f_path);}}VnodeDrop();}/* Release the path of file */free(filep->f_path);/* Release the file descriptor */filep->f_magicnum = 0;filep->f_oflags = 0;filep->f_pos = 0;filep->f_path = NULL;filep->f_priv = NULL;filep->f_vnode = NULL;filep->f_refcount = 0;filep->f_mapping = NULL;filep->f_dir = NULL;return ret;}
- 最后
FreeProcessFd负责释放该文件在进程层面占用的资源
鸿蒙全栈开发全新学习指南
也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH

相关文章:
鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄
句柄 | handle int open(const char* pathname,int flags); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd);只要写过应用程序代码操作过文件不会陌生这几个函数,文件操作的几个关键步骤嘛,跟把大…...
2024.5.8 关于 SpringCloud —— Ribbon 的基本认知
目录 Ribbon 负载均衡原理 工作流程 Ribbon 负载均衡规则 Ribbon 负载均衡自定义化 代码方式修改规则 配置文件方式修改规则 小总结 Ribbon 设定饥饿加载 Ribbon 负载均衡原理 工作流程 order-service 使用 RestTemplate 发送请求,随后该请求将会被 Ribbon 所…...
Lua 协程模拟 Golang 的 go defer 编程模式
封装go函数用于创建并启动一个协程: ---go函数创建并启动一个协程 ---param _co_task function 函数原型 fun(_co:thread) function go(_co_task)local co coroutine.create(_co_task) -- 创建一个暂停的协程coroutine.resume(co, co) -- 调用coroutine.resume激活…...
maven的安装与配置(超详细)
在Java开发中,配置Maven环境有几个重要的原因: 依赖管理:Maven 是一个强大的依赖管理工具,它能够帮助开发人员轻松地管理项目所需的各种第三方库和组件。通过在项目的 Maven 配置文件(pom.xml)中定义依赖&…...
springCloud服务降级使用到的组件
服务降级在Spring Cloud中通常使用的组件包括断路器(Circuit Breaker)和降级处理器(Fallback)。以下是它们的概念表述: 断路器(Circuit Breaker):断路器是一种设计模式,…...
Spring框架学习-详细
文章目录 1. Spring简介1.1 面向接口编程1.2 Spring简介1.3 Spring体系结构 2 Spring IoC - 基于XML2.1 Sping框架部署(IoC)2.2 Spring IoC使用2.3 IoC和DI2.4 DI依赖注入Spring容器通过反射方法实现属性注入有三种方式1. set方法注入2. 构造器注入 2.5 …...
fatal: fetch-pack: invalid index-pack output
解决方案:git clone --depth1 要克隆的git地址 下载最近一次提交的代码 其他分支的内容都不下载 这样整体下载体量就变小了 执行命令:git clone --depth 1 https://gitlab.scm321.com/ufx/xxxx.git...
相机购买指南
佳能1000D 上市时间:2008年6月 简介: 佳能1000D具有1010万有效像素和7点宽区域自动对焦系统。DIGIC III影像处理器的应用使高ISO画质得到提升。小巧的机身和优质的成像质量可以满足初级用户对旅游便携与高画质的要求。使用了DIGIC III影像处理器&#x…...
STM32微秒级别延时--F407--TIM1
基本配置: TIM1挂载在APB2总线上,150MHz经过15分频,得到10MHz计数频率,由于disable了自动重装载,所以只需要看下一次计数值是多少即可。 void TIM1_Delay_us(uint16_t us) //使用阻塞方式进行延时,ARR值不…...
AI图书推荐:杀手级ChatGPT提示词——利用人工智能实现成功与盈利
《杀手级ChatGPT提示词——利用人工智能实现成功与盈利》(Killer ChatGPT Prompts_ Harness the Power of AI for Success and Profit )一书是作者Guy Hart-Davis关于ChatGPT的指南,ChatGPT是OpenAI开发的大语言模型。这本书提供了各种职业角…...
AI时代:低代码与人工智能引领科技创造新时代
随着科技的飞速发展,我们步入了一个崭新的时代——AI时代。在这个时代,低代码和人工智能技术如日中天,成为引领科技创造的新引擎。本文将围绕这一主题,探讨低代码和人工智能如何在各个领域发挥巨大作用,推动科技创造迈…...
1.基于python的单细胞数据预处理-降维可视化
目录 降维的背景PCAt-sneUMAP检查质量控制中的指标 参考: [1] https://github.com/Starlitnightly/single_cell_tutorial [2] https://github.com/theislab/single-cell-best-practices 降维的背景 虽然特征选择已经减少了维数,但为了可视化࿰…...
【快捷部署】023_HBase(2.3.6)
📣【快捷部署系列】023期信息 编号选型版本操作系统部署形式部署模式复检时间023HBase2.3.6Ubuntu 20.04tar包单机2024-05-07 注意:本脚本非全自动化脚本,有2次人工干预,第一次是确认内网IP,如正确直接回车即可&#…...
Nginx配置项详解
Nginx,以其高性能、稳定性强、资源消耗低的特性,成为众多网站和应用首选的Web服务器及反向代理服务器。其配置文件的灵活性和丰富性是其强大功能的关键所在。本文将深入解析Nginx配置文件中的核心概念与关键配置项,帮助您更好地理解和定制Ngi…...
解决iview(view ui)中tabs组件中使用图片预览组件ImagePreview,图片不显示问题
同学们可以私信我加入学习群! 正文开始 前言一、问题描述二、原因分析三、解决方案总结 前言 最近在写个人项目的web端和浏览器插件,其中一个功能是base64和图片的转换。因为分成四个小功能,所以使用的iview的tabs来展示不同功能,…...
R2S+ZeroTier+Trilium
软路由使用ZeroTier搭建远程笔记 软路由使用ZeroTier搭建远程笔记 环境部署 安装ZeroTier安装trilium 环境 软路由硬件:友善 Nanopo R2S软路由系统:OpenWrt,使用第三方固件nanopi-openwrt。内网穿透:ZeroTier。远程笔记&…...
10 华三vlan技术介绍
AI 解析 -Kimi-ai Kimi.ai - 帮你看更大的世界 (moonshot.cn) 虚拟局域网(VLAN)技术是一种在物理网络基础上创建多个逻辑网络的技术。它允许网络管理员将一个物理网络分割成多个虚拟的局域网,这些局域网在逻辑上是隔离的,但实际…...
实现一个聊天室可发送消息语音图片视频表情包(任意文件)
文章目录 如何跑通代码仓库地址客户端登录发送消息接受消息发送文件接受文件 服务端接受消息并发送给各个客户端接受文件并发送给各个客户端 如何跑通 将手机和电脑都连自己的热点先运行服务器得到可监听的地址更新客户端安卓消息线程和文件线程的socker目标地址为可监听地址然…...
【SpringMVC 】什么是SpringMVC(一)?如何创建一个简单的springMvc应用?
文章目录 SpringMVC第一章1、什么是SpringMVC2、创建第一个SpringMVC的应用1-3步第4步第5步第6步7-8步3、基本语法1、进入控制器类的方式方式1:方式2:方式3:方式4:方式5:2、在控制器类中取值的方式方式1:方式2:方式3:方式4:方式5:方式6:超链接方式7:日期方式8:aja…...
【配置】IT-Tools部署
github地址 docker运行如下,记得打开云服务器的9090端口 docker run -d --name it-tools --restart unless-stopped -p 9090:80 corentinth/it-tools:latestip:9090查看,很香大部分工具都有...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
