6.s081/6.1810(Fall 2022)Lab2: System calls
文章目录
- 前言
- 其他篇章
- 参考链接
- 0. 前置准备
- 1. System call tracing (moderate)
- 1.1 简单分析
- 1.2 Hint 1
- 1.3 Hint 2
- 1.4 Hint 3
- 1.5 Hint 4
- 1.6 Hint 5
- 1.7 测试
- 2. Sysinfo (moderate)
- 2.1 声明
- 2.2 实现
- 2.2.1 框架
- 2.2.2 用户态与内核态交互
- 2.2.3 计算空闲内存的大小
- 2.2.4 计算非UNUSED进程的数量
- 2.3 测试
- 3. 总测试
前言
这个lab主要介绍了用户态到内核态的系统调用做了什么,并让我们照猫画虎完成了两个系统调用的实现。
其他篇章
环境搭建
Lab1: Utilities
Lab2: System calls
参考链接
官网链接
xv6手册链接,这个挺重要的,建议做lab之前最好读一读。
xv6手册中文版,这是几位先辈们的辛勤奉献来的呀!再习惯英文文档阅读我还是更喜欢中文一点,开源无敌!
OSTEP,对OS不熟悉的同学做之前可以看一下这本经典书籍,写得很好,也有中文版实体书。
官方文档
0. 前置准备
很惭愧,以前github用得少,这一步折腾了老半天,我再说一遍我个人的开发流程——先在windows下git一个本地仓库,然后用VS编辑,写完后git push
上去,在WSL的对应地方git pull
下来,然后编译运行。
前面环境配置中我为了连接到我个人的远程仓库,是直接把原本的远程仓库删了的,然后lab1做完做到lab2发现这个lab整体不是循序渐进的,而是彼此分离的,每个实验需要选择相应的分支,因此就要重新弄一下:
git remote add base git://g.csail.mit.edu/xv6-labs-2022git fetch basegit checkout syscallgit push --set-upstream origin syscall
当然,别忘了加.gitignore
1. System call tracing (moderate)
1.1 简单分析
gdb教学我就不说了,看看这个task。
先简单研究一下我们需求的这个trace是干什么的吧,trace顾名思义,tracing,追踪、寻迹的意思,比如ray tracing,就是光线追踪,这个命令接受一个传参mask,内涵是一个掩码,每一位对应一个系统调用的一个序号,比如传入32,代表 32 1<<SYS_read
,2147483647 代表追踪所有syscall,具体的这些值定义在了kernel/syscall.h
里,我们待会也会写
初步了解之后,就写实现吧,这个task按照hint的步骤来很清晰:
1.2 Hint 1
Add $U/_trace to UPROGS in Makefile
首先添加makefile,司空见惯了。
1.3 Hint 2
Run make qemu and you will see that the compiler cannot compile user/trace.c, because the user-space stubs for the system call don’t exist yet: add a prototype for the system call to user/user.h, a stub to user/usys.pl, and a syscall number to kernel/syscall.h. The Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs, which use the RISC-V ecall instruction to transition to the kernel. Once you fix the compilation issues, run trace 32 grep hello README; it will fail because you haven’t implemented the system call in the kernel yet.
然后说这个时候make,会找不到trace,我们要在用户态user/user.h
里加上trace的声明,根据原文 It should take one argument, an integer “mask”, whose bits specify which system calls to trace. 可知,这玩意应该接受一个int
,然后返回也是一个int
(返回值其实不影响来着):
然后我们在user/usys.pl
下添加这么一行,这是个Perl脚本,即使没有用过Perl的同学应该也能看出来这里的意思是声明了一个trace系统调用的入口,再通过上文展开为我们在usys.S
中生成一段汇编代码。
然后在内核syscall.h
中给它注册一个number
1.4 Hint 3
Add a sys_trace() function in kernel/sysproc.c that implements the new system call by remembering its argument in a new variable in the proc structure (see kernel/proc.h). The functions to retrieve system call arguments from user space are in kernel/syscall.c, and you can see examples of their use in kernel/sysproc.c.
然后模仿着添加原型?
这里简单解释一下后面这个syscalls数组,可能很多人没有看懂这,首先这是个static的不用说,然后这是个函数指针的数组(我一向很反感那些什么数组指针指针数组混着说的,直接说成装指针的数组不就一目了然了吗),函数返回值为uint64,参数为void,显然是为上面extern的那些函数准备的东西,这些都比较简单,后面的是个小feature了,它本身叫作指派初始化器(Designated Initializers),来自C99,意思就是给方括号里的那一位初始化为右边的值
但是可以看到,C99的指派初始化器的形式是[N] = expr
的,中间需要一个等号连接,这里没有,它是来自GCC私货,原文出现在介绍指定初始化器的时候:An alternative syntax for this that has been obsolete since GCC 2.5 but GCC still accepts is to write ‘[index]’ before the element value, with no ‘=’. 意味着大家在自己使用时加个等号是更符合standard的写法。
然后叫我们仿照着kernel/sysproc.c
里的其他函数给trace写一个定义进去:
uint64
sys_trace(void)
{return 0;
}
使用argint
从寄存器取出用户传入的参数:
int mask;argint(0, &mask); // 保存用户传入的参数
然后我们要把接到的这个mask保存到进程的元数据中,根据原文Add a sys_trace() function in kernel/sysproc.c that implements the new system call by remembering its argument in a new variable in the proc structure (see kernel/proc.h). T 我们在kernel/proc.h
中可以找到一个结构体struct proc
很显然这个结构体记录着一些元数据,我们在这个基础上再添加一条承载mask的:
int traceMask; // 用于接收trace的mask
显然每一个进程都有一个独属于自己的proc
对象,我们可以通过myproc()
来获取这个对象的指针,就此我们可以完成我们的sys_trace
定义:
uint64
sys_trace(void)
{argint(0, &myproc()->mask); // 尝试从用户空间读取参数return 0;
}
1.5 Hint 4
Modify fork() (see kernel/proc.c) to copy the trace mask from the parent to the child process.
我们知道fork出的子进程会复制父进程的内存空间,根据hint我们可以找到它的实现:
可以看到,这里明显是要做一个p
到np
的拷贝,p
指向的是父进程的proc
对象,np
则应该是new proc
的缩写了:
我们这个mask的修改不需要持有锁,因此只需要在alloc之后的合适时机将父进程的值赋出即可:
既然提到了alloc,这里刚好就可以想到一个问题——资源的分配与释放呢?我们知道C语言访问未初始化变量的行为是UB,那么我们默认状态下的mask进行初始化了吗?在上面那张图里我们可以清晰地看到(或者说猜到)内核依赖allocproc
分配内存,依赖freeproc
释放内存,因此我们可以直接F12
进去看一看实现:
如图,我们可以很容易地为mask
初始化以及释放时赋0值。
1.6 Hint 5
Modify the syscall() function in kernel/syscall.c to print the trace output. You will need to add an array of syscall names to index into.
然后我们为syscall
这个总体的函数实现我们的功能,也就是前文中的那些打印:
我们分析一下需要做的事情:当我们进行了trace
调用时,我们应当追踪mask
标记的所有调用,并打印出4: syscall close -> 0
这样的内容,不难看出,打印内容分为三部分:PID、系统调用的名称与系统调用的返回值,其中pid我们可以通过读取proc
来获取,返回值实际在框架中都告诉你了:
// and store its return value in p->trapframe->a0p->trapframe->a0 = syscalls[num]();
可以看到,系统调用的返回值被保存在了寄存器a0中,至于系统调用的名称呢?C语言中没有反射,我们就只好提前建立一张syscall的名称表,再根据mask
去寻址:
// 系统调用的名称
static const char *syscallnames[] = {
[SYS_fork] "fork",
[SYS_exit] "exit",
[SYS_wait] "wait",
[SYS_pipe] "pipe",
[SYS_read] "read",
[SYS_kill] "kill",
[SYS_exec] "exec",
[SYS_fstat] "fstat",
[SYS_chdir] "chdir",
[SYS_dup] "dup",
[SYS_getpid] "getpid",
[SYS_sbrk] "sbrk",
[SYS_sleep] "sleep",
[SYS_uptime] "uptime",
[SYS_open] "open",
[SYS_write] "write",
[SYS_mknod] "mknod",
[SYS_unlink] "unlink",
[SYS_link] "link",
[SYS_mkdir] "mkdir",
[SYS_close] "close",
[SYS_trace] "trace",
};
搞清楚并完成了所有前置工作我们就可以开始写逻辑了,最后的syscall
函数代码,很简单:
void
syscall(void)
{int num;struct proc *p = myproc();num = p->trapframe->a7;if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {// Use num to lookup the system call function for num, call it,// and store its return value in p->trapframe->a0p->trapframe->a0 = syscalls[num]();if ((p->mask >> num) & 1) { // 判断系统调用是否被跟踪printf("%d: syscall %s -> %d\n",p->pid, syscallnames[num], p->trapframe->a0);}} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->trapframe->a0 = -1;}
}
1.7 测试
到这里就基本完成了,还是老规矩,我们make qemu
编译,然后试一试文档中的几个命令:
trace 32 grep hello README
trace 2147483647 grep hello README
grep hello READMEtrace 2 usertests forkforkfork # 这一条输入之后貌似要等一会才会出一大坨
…
最后跑一下总体批分./grade-lab-syscall trace
成功通过!
2. Sysinfo (moderate)
然后让我们来完成一下task2,这个是也是添加一个系统调用,叫sysinfo
:
我们先搞清楚这个调用是干啥的,从介绍可以看到,这个sysinfo接收一个struct sysinfo
的指针,我们就是要写这个指针指向的对象,怎么写呢?就是将空闲的字节数存到对象里的freemem
字段,将state
不为UNUSED
的进程数量写到nproc
字段。
有一个初步的印象后就可以去写实现了,整体思路和上文trace
的步骤差不多:
首先是增加$U/_sysinfotest\
到Makefile:
2.1 声明
在user/user.h
里加声明:
syscall.h中:
syscall.c中(第三个是上面的那个名称表):
2.2 实现
2.2.1 框架
写完了声明,就可以写实现了,实现我们依旧写在sysproc.c
下:
#include "sysinfo.h" // 由于要接收sysinfo类型的结构体,我们先include一下uint64
sys_sysinfo(void)
{// TODO: 从用户态到内核态// TODO: 计算空闲内存的大小// TODO: 计算内存中非UNUSED的进程的数量// TODO: 从内核态到用户态return 0;
}
关于具体实现,文档提供了三个hint,我们还是按照这三个hint的步骤去做就行了:
2.2.2 用户态与内核态交互
sysinfo needs to copy a struct sysinfo back to user space; see sys_fstat() (kernel/sysfile.c) and filestat() (kernel/file.c) for examples of how to do that using copyout().
首先依旧是获取入参,hint给了我们两个参考范例,我们可以看一看:
可以看(猜)到,这两个文件以struct stat
类型为例子,分别向我们展示了获取类型指针的方法以及将既存对象写入获取到的指针的方法,分别使用argaddr
与copyout
函数实现,因此我们可以依葫芦画瓢写出以下代码:
uint64
sys_sysinfo(void)
{uint64 addr; // 指向sysinfo结构体的指针struct sysinfo info;argaddr(0, &addr); // 尝试从用户空间读取参数// TODO: 计算空闲内存的大小// TODO: 计算内存中非UNUSED的进程的数量if (copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) < 0) // 将内核空间的sysinfo结构体复制到用户空间return -1;return 0;
}
2.2.3 计算空闲内存的大小
To collect the amount of free memory, add a function to kernel/kalloc.c
hint提示我们想要计算空闲内存的大小,需要在kalloc.c
下添加一个函数,通过观察该文件的内容,我们不难发现,这个文件主要负责维护一个名为kmem
的对象,这个名称应该是kernel memory
的缩写,这个结构体内部有一个一看就是一把自旋锁的lock
字段和一个一看就是负责记录空闲page的链表的freelist
字段,通过综合观察我们可以知道freelist
确实维护的是空闲页的数量,因此我们想要找到空闲内存的总大小,只需要遍历整个freelist
,就可以找到总共空闲页的数量,而每个页有PGSIZE
即4096个字节,因此我们只需要将获得的页面数乘以PGSIZE
即可,于是不难写出以下代码:
// 计算空闲内存大小
uint64
kfree_mem_cnt(void)
{struct run *r;uint64 cnt = 0;acquire(&kmem.lock); // 由于kmem.freelist是全局变量,所以需要加锁r = kmem.freelist;while(r) {cnt++;r = r->next;}release(&kmem.lock);return cnt * PGSIZE;
}
值得一提的是,由于kmem是一个全局变量,属于临界资源,因此我们在访问时需要加锁。然后我们需要再kernel/defs.h
下添加这个函数的声明,才能为我们所调用:
然后在我们的sysinfo
中调用它:
uint64
sys_sysinfo(void)
{uint64 addr; // 指向sysinfo结构体的指针struct sysinfo info;argaddr(0, &addr); // 尝试从用户空间读取参数info.freemem = kfree_mem_cnt(); // 获取内存中空闲的内存大小// TODO: 计算内存中非UNUSED的进程的数量if (copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) < 0) // 将内核空间的sysinfo结构体复制到用户空间return -1;return 0;
}
2.2.4 计算非UNUSED进程的数量
To collect the number of processes, add a function to kernel/proc.c
这个函数提醒我们写在kernel/proc.c
中,这个文件我们上一个task其实已经接触过了,再来看一看吧。前文我们已经知道了每个进程的信息依赖proc
结构体维护,进一步阅读不难发现,这里用一个全局数组来维护了我们的所有进程,因此,我们只需要遍历一遍这个数组,然后给其中非空闲的进程计数即可。
同样值得一提的是,我们翻阅struct proc
的定义可以发现,注释中提示了我们,state
属于临界资源,访问需要加锁:
综合上面的内容,我们就可以比较轻松地写出如下代码:
// 计算非空闲进程的数量
uint64
get_free_proc_num(void)
{uint64 num = 0;for(struct proc* p = proc; p < &proc[NPROC]; p++){acquire(&p->lock); // state是临界资源,需要加锁if (p->state != UNUSED)num++;release(&p->lock);}return num;
}
我们同样需要为它在defs.h
中添加声明以供外部调用:
最后我们在sysinfo的实现中调用这个函数,完成了最终步骤:
uint64
sys_sysinfo(void)
{uint64 addr; // 指向sysinfo结构体的指针struct sysinfo info;argaddr(0, &addr); // 尝试从用户空间读取参数info.freemem = kfree_mem_cnt(); // 获取内存中空闲的内存大小info.nproc = get_free_proc_num(); // 获取内存中非UNUSED的进程的数量if (copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) < 0) // 将内核空间的sysinfo结构体复制到用户空间return -1;return 0;
}
2.3 测试
同样的,make qemu
后,按照文档中的提示运行sysinfotest
,成功:
退出终端后运行./grade-lab-syscall sysinfo
本地测试,成功:
3. 总测试
同样的,我们需要在根目录下创建一个time.txt
,里面写上本次lab用时,比如我这个lab不算写博客花了差不多4个小时,我就写个4,然后运行make grade
(跑到这一步的时候我发现gdb也叫我填一个东西在answers-syscall.txt
里,答案是usertrap()
),弄好后又出了个错误:
Timeout! trace children: FAIL (30.7s)
…
8: syscall fork -> -1
7: syscall fork -> -1
6: syscall fork -> -1
9: syscall fork -> -1
qemu-system-riscv64: terminating on signal 15 from pid 6958 (make)
MISSING ‘^ALL TESTS PASSED’
QEMU output saved to xv6.out.trace_children
这个主要是由于WSL性能损失的原因,之前文档也强调过这个问题了,解决方法是自己手动改测试脚本gradelib.py
,放宽时间(话说上面单独跑测试都25s过了,总的测试居然过不了,还得看运气呀):
然后再跑make grade
:
搞定!
相关文章:

6.s081/6.1810(Fall 2022)Lab2: System calls
文章目录 前言其他篇章参考链接0. 前置准备1. System call tracing (moderate)1.1 简单分析1.2 Hint 11.3 Hint 21.4 Hint 31.5 Hint 41.6 Hint 51.7 测试 2. Sysinfo (moderate)2.1 声明2.2 实现2.2.1 框架2.2.2 用户态与内核态交互2.2.3 计算空闲内存的大小2.2.4 计算非UNUSE…...

Git在VSCode中的使用
1.Git图像化界面进行项目初始化(git init) 2. Git图形化界面对文件进行操作 当我们创建一个文件时,该文件后面有一个U,表示文件未跟踪。 我们在管理工具中输入日志并提交代码,相当于做了两件事,将文件由“…...

【双指针_移动零_C++】
题目解析 移动零 nums [0,1,0,3,12] [1,3,12,0,0]算法原理 数组划分(数组分块) 双指针算法(利用数组下标来充当指针)使用两个指针的作用: cur指针:从左往右扫描数组,就是遍历数组。 dest指针…...

【网络安全】网络安全威胁实时地图 - 2023
文章目录 [TOC] ① 360 安全大脑360 APT全景雷达 ② 瑞星杀毒瑞星云安全瑞星网络威胁态势感知平台 ③ 比特梵德 Bitdefender④ 飞塔防火墙 FortiGuard⑤ 音墙网络 Sonicwall⑥ 捷邦 Check Point⑦ AO卡巴斯基实验室全球模拟隧道模拟 ⑧ 数字攻击地图⑨ Threatbutt互联网黑客攻击…...

视频过大如何压缩变小?文件压缩技巧分享
如何压缩视频是许多视频编辑者、视频上传者经常遇到的问题,如果你也遇到了这个问题,不用担心,下面将就给大家分享几个视频压缩方法,可以帮助大家轻松地压缩视频,同时保持视频的高清晰度和音频质量。 一、嗨格式压缩大师…...

组合模式(Composite)
组合模式是一种结构型设计模式,主要用来将多个对象组织成树形结构以表示“部分-整体”的层次结构,因此该模式也称为“部分-整体”模式。简言之,组合模式就是用来将一组对象组合成树状结构,并且能像使用独立对象一样使用它们。 Co…...

grid map学习笔记3之详解grid_map_pcl库实现point cloud点云转换成grid map栅格地图
文章目录 0 引言1 grid_map_pcl示例1.1 主要文件1.2 示例数据1.3 启动文件1.4 配置文件1.5 主要实现流程1.6 启动示例1.7 示例结果 2 D435i 点云生成栅格地图2.1 D435i 点云文件2.2 修改启动文件2.3 测试和结果2.4 修改配置文件2.5 重新测试和结果 0 引言 grid map学习笔记1已…...
ebpf开发问题汇总
不同Programs之间通信 用bpf_obj_get来获取MAP的描述符,然后用bpf_map_reuse_fd函数来在不同program之间复用 kernel 与 user space之间 需要pin the BPF MAP to the BPF Virtual File System (VFS),来持久化存储,否则如果map用不到会被destory 引用…...
认识 mysql 命令
文章目录 1.简介2.选项3.子命令4.小结参考文献 1.简介 mysql 是 MySQL 的命令行客户端工具,用于连接到 MySQL 服务器并执行 SQL 语句。 它支持交互式和非交互式两种使用方式。以交互方式使用时,查询结果以 ASCII 表格式呈现。 当以非交互方式使用时&am…...
IK(Inverse Kinematics,逆运动学)
介绍 在Unity中,IK(Inverse Kinematics,逆运动学)是一种用于控制角色或物体骨骼的技术。通过使用IK,可以实现更自然和真实的动画效果,特别是在处理复杂的角色动作时非常有用。 IK Pass是Unity中的一个功能…...

Cadence 小技巧系列(持续更新)
■ ADE setup simulator/directory/host 更改仿真路径,默认home路径空间太小了,改成当前路径就行。 瞬态tran仿真要用APS跑(setup--high...) 瞬态tran仿真精度设置,conservation,option--maxstep设为0.1n…...

【unity】Pico VR 开发笔记(基础篇)
Pico VR 开发笔记(基础篇) XR Interaction Tooikit 版本 2.3.2 一、环境搭建 其实官方文档已经写的很详细了,这里只是不废话快速搭建,另外有一项官方说明有误的,补充说明一下,在开发工具部分说明 插件安装——安装pico的sdk和XR…...

竞争之王CEO商战课,聚百家企业在京举行
竞争之王CEO商战课,于2023年7月29-31日在北京临空皇冠假日酒店举办,近百家位企业家齐聚一堂,共享饕餮盛宴。 竞争之王CEO商战课是打赢商战的第一课。 竞争环境不是匀速变化,而是加速变化。 在未来的市场环境中,企业间…...

【shell】获取ping的时延数据并分析网络情况及常用命令学习
文章目录 获取ping的时延数据并分析网络情况|、||、&、&&辨析teetailkillall 获取ping的时延数据并分析网络情况 网络情况经常让我们头疼,每次都需要手动在终端ping太麻烦了,不如写个脚本ping并将数据带上时间戳存入文件,然后也…...
石子合并一章通(环形石子合并,四边形不等式,GarsiaWachs算法)(内附封面)
[NOI1995] 石子合并 题目描述 在一个圆形操场的四周摆放 N N N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 2 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 试设计出一个算法,计算出将 …...

Docker快速入门笔记
Docker快速入门 前言 当今软件开发领域的一股热潮正在迅速兴起,它融合了便捷性、灵活性和可移植性,让开发者们欣喜若狂。它就是 Docker!无论你是一个初学者,还是一位经验丰富的开发者,都不能错过这个引领技术浪潮的工…...

【Excel】记录Match和Index函数的用法
最近一直用到的两个处理EXCEL表格数据的函数向大家介绍一下,写这篇博文的目的也是为了记录免得自己忘记了,嘻嘻。 先上百度的链接 Match函数的用法介绍:https://jingyan.baidu.com/article/2fb0ba40b4933941f3ec5f71.html 小结:…...

SolidUI社区-从开源社区角度思考苹果下架多款ChatGPT应用
文章目录 背景下架背景下架原因趋势SolidUI社区的未来规划结语如果成为贡献者 背景 随着文本生成图像的语言模型兴起,SolidUI想帮人们快速构建可视化工具,可视化内容包括2D,3D,3D场景,从而快速构三维数据演示场景。SolidUI 是一个创新的项目…...

插入排序讲解
插入排序(Insertion-Sort)一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表…...
杀疯了的ChatGPT——开启AI智能交流新纪元 「文末有彩蛋」
欢迎打开 ChatGPT 的新世纪大门 🌍 目录 😊 引言😎 ChatGPT 的高级之处1. 巨大的模型规模2. 广泛的知识覆盖3. 零样本学习4. 多语言支持5. 上下文感知对话 🤖 如何使用 ChatGPT1. 智能助手2. 个性化交互3. 语言学习伙伴4. 创造性写…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...