MIT 6.S081 | 操作系统 | Lab1: Xv6 and Unix utilities
Lab1: Xv6 and Unix utilities
文章目录
- Lab1: Xv6 and Unix utilities
- 实验任务
- 1.启动XV6(easy)
- 2.Sleep(easy)-练手的,就是熟悉一下怎么在xv6项目中加.c文件,生成可执行程序并进行测试的
- 1.解析rm.c
- 2.argc 如何被赋值
- 3.Sleep代码
- 4.makefile编辑
- 5.通过make qemu生成sleep并执行
- 6.测试sleep
- 3. pingpong(难度:Easy)
- 1.注意
- 2.代码
- 3.make qemu
- 4.pingpong命令执行
- 5. 测试该命令 ./grade-lab-util pingpong
- 4.Primes(素数,难度:Moderate/Hard)
- 1.思路
- 难点:
- 注意:
- 2.代码
- 3.make qemu
- 4.primes执行
- 5.测试该命令 ./grade-lab-util primes
- 5.find(难度:Moderate)
- 1.ls.c解析
- 2.代码
- 难点讲解
- 0.问题:p++后不应该去到了无效地址吗?为什么de.name还可以赋值给p?p[strlen(de.name)] = 0;这句话是在干什么?
- 1. buf 数组的定义
- 2. 计算指针 p
- 3. 插入路径分隔符
- 4. 内存管理
- 5. 再次拼接 de.name
- 结论
- 3.make qemu
- 4.find执行
- 5.测试该命令 ./grade-lab-util find
- 6.xargs(难度:Moderate)
- 1.思路
- 2.代码
- 3.make qemu
- 4.xargs执行
- 5.测试该命令 ./grade-lab-util xargs
- image-20241122194130827
实验任务
1.启动XV6(easy)
安装xv6和qemu部分请看环境搭建部分Mit6.S081-实验环境搭建_mit 6.s081-CSDN博客
启动qemu
执行ls命令
2.Sleep(easy)-练手的,就是熟悉一下怎么在xv6项目中加.c文件,生成可执行程序并进行测试的
实现xv6的UNIX程序sleep
:您的sleep
应该暂停到用户指定的计时数。一个滴答(tick)是由xv6内核定义的时间概念,即来自定时器芯片的两个中断之间的时间。您的解决方案应该在文件user/sleep.c中
提示:
- 在你开始编码之前,请阅读《book-riscv-rev1》的第一章
- 看看其他的一些程序(如**/user/echo.c, /user/grep.c, /user/rm.c**)查看如何获取传递给程序的命令行参数
- 如果用户忘记传递参数,
sleep
应该打印一条错误信息 - 命令行参数作为字符串传递; 您可以使用
atoi
将其转换为数字(详见**/user/ulib.c**) - 使用系统调用
sleep
- 请参阅kernel/sysproc.c以获取实现
sleep
系统调用的xv6内核代码(查找sys_sleep
),user/user.h提供了sleep
的声明以便其他程序调用,用汇编程序编写的user/usys.S可以帮助sleep
从用户区跳转到内核区。 - 确保
main
函数调用exit()
以退出程序。 - 将你的
sleep
程序添加到Makefile中的UPROGS
中;完成之后,make qemu
将编译您的程序,并且您可以从xv6的shell运行它。 - 看看Kernighan和Ritchie编著的《C程序设计语言》(第二版)来了解C语言。
1.解析rm.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
- 这三行代码引入了必要的头文件。其中:
- kernel/types.h 包含了基本数据类型的定义。
- kernel/stat.h 包含了文件状态的信息。
- user/user.h 包含了用户级别的函数和常量,包括输出和进程控制的相关功能。
int main(int argc, char *argv[]){
- 这里定义了main函数,argc表示命令行参数的数量,argv是一个字符串数组,用来保存每个参数的值。
int i; if(argc < 2){ fprintf(2, "Usage: rm files...\n"); exit(1); }
- 定义了一个整数变量i用于循环。
- 接下来检查命令行参数的数量。如果传入的参数数量少于2(即程序名加上至少一个文件名),则打印出用法提示信息到标准错误输出,并以状态码1退出程序。
for(i = 1; i < argc; i++){ if(unlink(argv[i]) < 0){ fprintf(2, "rm: %s failed to delete\n", argv[i]); break; } }
- 这个循环从1开始遍历传入的文件名参数(argv[0]是程序名,argv[1]是第一个要删除的文件名)。
- unlink系统调用用于删除指定的文件名。如果unlink返回的结果小于0,表示删除文件失败,则在标准错误输出中打印出相应的错误信息,并跳出循环。
exit(0);}
- 最后,调用exit(0)正常退出程序,状态码0表示程序执行成功。
总结
这段代码实现了一个基本的文件删除功能,类似于Unix/Linux系统中的rm命令。它首先检查输入参数的有效性,然后尝试逐个删除指定的文件。如果某个文件的删除操作失败,会输出错误信息并结束程序。
2.argc 如何被赋值
argc 的值是由操作系统的 shell(命令行解释器)给赋值的。
具体过程如下:
argc 如何被赋值
- 用户输入命令:当你在命令行中输入一个命令(例如 ./rm file1.txt file2.txt file3.txt)并按下回车,shell 开始处理你输入的命令。
- 解析命令:shell 解析你输入的字符串,将其分解成程序名和参数。它将程序名(./rm)和所有后续的参数(file1.txt、file2.txt、file3.txt)提取出来。
- 构建参数列表:
- argc 被设置为参数的总数,包括程序名。
- argv被构建为一个指针数组,其中每个元素指向相应的字符串:
- argv[0]: ./rm
- argv[1]: file1.txt
- argv[2]: file2.txt
- argv[3]: file3.txt
- 创建进程:shell 使用系统调用(通常是 exec 系列的函数)来创建一个新的进程,并将 argc 和 argv 参数传递给它。这些参数传递给程序的入口点(即 main 函数)。
- 程序开始执行:当新的进程启动后,它的 main 函数会使用 argc 和 argv 来访问命令行传递的参数。
示例
例如,你在终端中输入的完整命令:
./rm file1.txt file2.txt file3.txt
操作系统中的处理流程可以描述为:
- argc 被设置为 4(因为有 4 个参数:./rm、file1.txt、file2.txt、file3.txt)。
- argv被设置为一个数组,有 4 个元素:
- argv[0] = ./rm
- argv[1] = file1.txt
- argv[2] = file2.txt
- argv[3] = file3.txt
3.Sleep代码
根据提示写就行,注意要把sleep.c放到user目录下
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"int main(int argc,char *argv[])
{if(argc!=2){//在标准输出,输出错误信息fprintf(2,"sleep命令缺少参数\n");exit(1);}int sec=atoi(argv[1]);sleep(sec);exit(0);
}
4.makefile编辑
152行处加上$U/_sleep\
作用是通过make qemu生成可执行文件sleep的
5.通过make qemu生成sleep并执行
执行
6.测试sleep
在xv6目录下执行命令
./grade-lab-util sleep
如果出现
把grade-lab-util第一行的python改成python3即可
#!/usr/bin/env python3
3. pingpong(难度:Easy)
1.注意
1.在父子进程中要关闭两个管道不用的一端
2.父子进程都要把两个管道全部关闭
3.打印的信息冒号后面有个空格,不打会报错
2.代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"int main(int argc,char *argv[])
{int p1[2];//父到子方向int p2[2];//子到父方向pipe(p1);pipe(p2);char *buffer="buffer";if(argc!=1){printf("pingpong命令有多余的参数");}if(fork()==0){close(p1[1]);close(p2[0]);if(read(p1[0],&buffer,sizeof(char))!=1){printf("子进程读错误\n");exit(0);}if(write(p2[1],&buffer,sizeof(char))!=1){printf("子进程写错误\n");exit(0);}else{printf("%d: received ping\n",getpid());exit(0);}close(p1[0]);close(p2[1]);}else{close(p1[0]);close(p2[1]);if(write(p1[1],&buffer,sizeof(char))!=1){printf("父写进程写错误\n");exit(0);}if(read(p2[0],&buffer,sizeof(char))!=1){printf("父进程读错误\n");exit(0);}else{printf("%d: received pong\n",getpid());exit(0);}close(p1[1]);close(p2[0]);}exit(0);
}
3.make qemu
4.pingpong命令执行
5. 测试该命令 ./grade-lab-util pingpong
4.Primes(素数,难度:Moderate/Hard)
1.思路
考虑所有小于1000的素数的生成。Eratosthenes的筛选法可以通过执行以下伪代码的进程管线来模拟:
p = get a number from left neighbor
print p
loop:n = get a number from left neighborif (p does not divide n)send n to right neighbor
p = 从左邻居中获取一个数
print p
loop:n = 从左邻居中获取一个数if (n不能被p整除)将n发送给右邻居
文档中的这个资料很重要,需要好好理解
在这里就是用一个递归函数去实现它的loop循环操作
每一层递归函数就是一个子进程,即就把图上的方框当做一层递归函数或者一个子进程
每一个子进程做的事情就是把自己进程的管道内的数字进行筛选,用第一个数筛选剩下的其他数字
比如在第一个子进程就是用2筛选2-11,只要是2的倍数就给剔除,所以在子进程的子进程就只剩下了3,5,7,9,11
再用3筛选剩下的四个数字就好
难点:
1.我如何存储2,3,4,5,6,…35这些数字?
用管道存储,管道可以看做一个文件,在linux系统中最大可以放65536字节,存几个数字还是轻轻松松的
2.对不用的管道的读端或者写端要关闭,在什么时候关闭
这个看下面代码部分
3.那我递归函数的终止条件又是什么呢?
管道里面没有数字了,那这个进程就结束了,因为我们不需要再次创建子进程进行筛选了
注意:
递归函数所有代码执行完了要在结尾加上exit(0)作为子进程的结束
2.代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"//无限递归报错时添加
__attribute__((noreturn))
void dfs(int p[2])
{close(p[1]);int num;//读第一个数//读到数字才会继续往下执行,否则不执行 if(read(p[0],&num,sizeof(int))==sizeof(int)){printf("prime %d\n",num);}else{exit(0);}int son_fork_p[2];pipe(son_fork_p);int res;//用第一个数字筛选其他的数字,满足条件才会写进子进程的管道while(read(p[0],&res,sizeof(int))==sizeof(int)){if(res%num!=0){write(son_fork_p[1],&res,sizeof(int));}}close(son_fork_p[1]);close(p[0]);//给子进程传入筛选后的管道if(fork()==0){dfs(son_fork_p);}else{close(son_fork_p[0]);wait(0);}//不要忘记这个,我们需要结束子进程exit(0);
}int main(int argc,int argv[])
{if(argc!=1){printf("不必要的参数,参数过多\n");exit(0);}int p[2];pipe(p);int i;for(i=2;i<=35;i++){write(p[1],&i,sizeof(int));}//调用递归函数if(fork()==0){dfs(p);}else{close(p[1]);close(p[0]);wait(0);}exit(0);
}
3.make qemu
如果碰到无限递归的错误,那就在dfs前加个
_attribute_((noreturn)) 就行
4.primes执行
5.测试该命令 ./grade-lab-util primes
5.find(难度:Moderate)
1.ls.c解析
struct dirent {ushort inum;//索引,可以通过这个找到具体的文件信息char name[DIRSIZ];//文件名
};
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"//这个 fmtname 函数的主要功能是将给定的路径字符串格式化成一个指定长度的名称(这里的DIRSIZ是14),具体是将路径中最后一个斜杠后的部分提取出来并进行处理
char*
fmtname(char *path)
{static char buf[DIRSIZ+1];char *p;// p从path末尾从后往前遍历,将p移动到碰到的第一个'/'的位置for(p=path+strlen(path); p >= path && *p != '/'; p--);//移动到最后的文件名的开头的部分 后面就是具体的文件名了p++;// 如果文件名大小比DIRSIZ大的话那就直接用p就行,如果大小比DIRSIZ小的话,那缺少的部分就用空格补齐,下面的代码就干这个事情if(strlen(p) >= DIRSIZ)return p;memmove(buf, p, strlen(p));memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));return buf;
}//ls的具体功能实现 大体是先看是不是文件,是文件就输出详细信息,是目录就遍历它所有的子目录显示具体的信息
void
ls(char *path)
{char buf[512], *p;int fd;//存储目录项struct dirent de; //存储文件信息的结构体struct stat st;//打开pathif((fd = open(path, 0)) < 0){fprintf(2, "ls: cannot open %s\n", path);return;}//把fd所指文件的信息(大小,名称,类型之类的)存储到st结构体中if(fstat(fd, &st) < 0){fprintf(2, "ls: cannot stat %s\n", path);close(fd);return;}switch(st.type){//普通文件case T_FILE:printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);break;//目录case T_DIR:if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){printf("ls: path too long\n");break;}//移动p到buf的末尾,方便后续拼接路径,通俗的说就是往输入路径后面加了个/strcpy(buf, path);p = buf+strlen(buf);*p++ = '/';//如果是目录(T_DIR),则使用 read 函数读取目录中的每个目录项,利用 struct dirent 读取信息,并进行处理。while(read(fd, &de, sizeof(de)) == sizeof(de)){if(de.inum == 0)continue;//将目录项名称复制到 buf 中,这样可以构建文件路径memmove(p, de.name, DIRSIZ);p[DIRSIZ] = 0;//对每个目录项,使用 stat 获取状态信息,如文件类型、inode 和大小。if(stat(buf, &st) < 0){printf("ls: cannot stat %s\n", buf);continue;}printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);}break;}close(fd);
}int
main(int argc, char *argv[])
{int i;//默认是当前目录if(argc < 2){ls(".");exit(0);}//可以一条命令查询多个文件或者目录的状态for(i=1; i<argc; i++)ls(argv[i]);exit(0);
}
感觉这个最多可以搞abcd/abd.c这种目录的,ab/abcd/abd.c估计就不太行了,因为没有递归。
2.代码
大多数都是ls.c里面的代码,功能一样的就不说了,其他的主要看注释把
#include "kernel/types.h"
#include "kernel/fcntl.h"
#include "kernel/stat.h"
#include "kernel/fs.h"
#include "user/user.h"/*将路径格式化为文件名,就是提取路径中的文件名
*/
char* fmt_name(char *path){static char buf[DIRSIZ+1];char *p;// Find first character after last slash.for(p=path+strlen(path); p >= path && *p != '/'; p--);p++;memmove(buf, p, strlen(p)+1);return buf;
}
/*系统文件名与要查找的文件名,若一致,打印系统文件完整路径
*/
void eq_print(char *fileName, char *findName){if(strcmp(fmt_name(fileName), findName) == 0){printf("%s\n", fileName);}
}
/*在某路径中查找某文件
*/
void find(char *path, char *findName){int fd;struct stat st; if((fd = open(path, O_RDONLY)) < 0){fprintf(2, "find: cannot open %s\n", path);return;}if(fstat(fd, &st) < 0){fprintf(2, "find: cannot stat %s\n", path);close(fd);return;}char buf[512], *p; struct dirent de;switch(st.type){ case T_FILE:eq_print(path, findName); break;case T_DIR:if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){printf("find: path too long\n");break;}strcpy(buf, path);p = buf+strlen(buf);*p++ = '/';while(read(fd, &de, sizeof(de)) == sizeof(de)){if(de.inum == 0 ||strcmp(de.name, ".")==0 || strcmp(de.name, "..")==0)continue;//把新得到的子目录拼接到buf后面//比如我们的find . b,就是在./后面拼接了a,然后在递归中传入的是./amemmove(p, de.name, strlen(de.name));p[strlen(de.name)] = 0;find(buf, findName);}break;}close(fd);
}int main(int argc, char *argv[]){if(argc < 3){printf("find: find <path> <fileName>\n");exit(0);}find(argv[1], argv[2]);exit(0);
}
难点讲解
0.问题:p++后不应该去到了无效地址吗?为什么de.name还可以赋值给p?p[strlen(de.name)] = 0;这句话是在干什么?
char buf[512], *p;
...
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';
1. buf 数组的定义
- char buf[512]; 定义了一个字符数组 buf,大小为 512 字节。
- 该数组用于存储路径名。
2. 计算指针 p
- strcpy(buf, path); 将路径 path 复制到 buf 中。
- p = buf + strlen(buf); 设置指针 p 指向 buf 的末尾(即原始字符串的末尾)。
3. 插入路径分隔符
- *p++ = ‘/’; 在 buf 的末尾添加了一个斜杠 /,并将指针 p 移动到下一个位置。此时,p 指向了 buf 中新添加的 / 的下一个位置。
4. 内存管理
此时,buf 中的内容如下(假设 path 是 “.”):
- buf 的内容为 “./”,即字符串结束后有一个斜杠 /。
5. 再次拼接 de.name
memmove(p, de.name, strlen(de.name));
p[strlen(de.name)] = 0;
- memmove(p, de.name, strlen(de.name)); 将目录项的名称 de.name 复制到 p 指向的内存地址。这是有效的,因为 p 在这里指向 buf 的末尾,即 ‘/’ 之后的位置,足够容纳 de.name 的字符串。
- 复制后,紧接着通过 p[strlen(de.name)] = 0; 在 p 后面添加一个空字符 ‘\0’,以正确地终止这个拼接后的字符串,使其成为一个有效的 C 字符串。
结论
- 在这段代码中没有出现内存访问错误,因为 buf 数组是充分分配的(大小为 512 字节),并且 p 最初被设置为指向数组 buf 的有效内存区域的末尾。
- 通过指针管理,程序能够在原有路径后面正确拼接额外的目录名 de.name。
3.make qemu
注:qemu会保留上次的结果,更改find.c以后最好make clean一下再make qemu,不然有可能一直报错
4.find执行
5.测试该命令 ./grade-lab-util find
6.xargs(难度:Moderate)
1.思路
笔者真是一点头绪也没有,这个是转载自这位大佬MIT 6.S081 Operating System - 知乎
第五个utility是xargs. 将标准输入里每一个以’\n’分割的行作为单独1个额外的参数, 传递并执行下一个命令. 这题主要感觉是考察fork + exec的使用. 但真正实现的时候, 麻烦点在于滑动窗口buffer的管理. 有以下这几个点需要思考的:
# 1. 什么时候知道不会有更多的行输入了?
当从file descriptor 0读的时候, 读到返回值为0# 2. 怎么能抓出每一个以'\n'分割的行?
我们不能方便地从file descriptor里读到空行符为止, xv6没有这样的库函数支持.
我们需要自己管理一个滑动窗口buffer, 如下:假设我们有1个长度为10的buffer, 以 . 代表为空
buf = [. . . . . . . . . .]
我们read(buf, 10)读进来6个bytes
buf = [a b \n c d \n . . . .]这时我们需要做的是,
1. 找到第一个'\n'的下标, 用xv6提供的strchr函数, 得到下标为2
2. 把下标0~1的byte转移到另一个buffer去作为额外参数
3. 执行fork+exec+wait组合拳去执行真正执行的程序, 使用我们parse出来的额外的参数
4. 修建我们的buffer, 把0~2的byte移除, 把3~9的byte移到队头
此时buffer变成:
buf = [c d \n . . . . . . .]# 3. 上述操作要一直循环直到
file descriptor 0 已经关闭了 && buffer里的所有读入的bytes都处理完了
2.代码
#include "kernel/param.h"
#include "kernel/types.h"
#include "user/user.h"#define buf_size 512int main(int argc, char *argv[]) {char buf[buf_size + 1] = {0};uint occupy = 0;char *xargv[MAXARG] = {0};int stdin_end = 0;for (int i = 1; i < argc; i++) {xargv[i - 1] = argv[i];}while (!(stdin_end && occupy == 0)) {// try read from left-hand programif (!stdin_end) {int remain_size = buf_size - occupy;int read_bytes = read(0, buf + occupy, remain_size);if (read_bytes < 0) {fprintf(2, "xargs: read returns -1 error\n");}if (read_bytes == 0) {close(0);stdin_end = 1;}occupy += read_bytes;}// process lines readchar *line_end = strchr(buf, '\n');while (line_end) {char xbuf[buf_size + 1] = {0};memcpy(xbuf, buf, line_end - buf);xargv[argc - 1] = xbuf;int ret = fork();if (ret == 0) {// i am childif (!stdin_end) {close(0);}if (exec(argv[1], xargv) < 0) {fprintf(2, "xargs: exec fails with -1\n");exit(1);}} else {// trim out line already processedmemmove(buf, line_end + 1, occupy - (line_end - buf) - 1);occupy -= line_end - buf + 1;memset(buf + occupy, 0, buf_size - occupy);// harvest zombieint pid;wait(&pid);line_end = strchr(buf, '\n');}}}exit(0);
}
3.make qemu
4.xargs执行
5.测试该命令 ./grade-lab-util xargs
最后,笔者比较菜,完成的时间大概在10h以上了。磨叽磨叽再加上中间有个javaweb考试,不知道隔了多久了才完成,有些.c程序也是真的没有头绪,所以还是得继续努力,大家加油。
相关文章:
MIT 6.S081 | 操作系统 | Lab1: Xv6 and Unix utilities
Lab1: Xv6 and Unix utilities 文章目录 Lab1: Xv6 and Unix utilities实验任务1.启动XV6(easy)2.Sleep(easy)-练手的,就是熟悉一下怎么在xv6项目中加.c文件,生成可执行程序并进行测试的1.解析rm.c2.argc 如何被赋值3.Sleep代码4.makefile编辑5.通过make…...
04 - 尚硅谷 - MQTT 客户端编程
1.在Java中使用MQTT 1.1 Eclipse Paho Java Client 具体步骤: 1、创建一个Spring Boot项目,添加如下依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>…...
C语言基础学习:抽象数据类型(ADT)
基础概念 抽象数据类型(ADT)是一种数据类型,它定义了一组数据以及可以在这组数据上执行的操作,但隐藏了数据的具体存储方式和实现细节。在C语言中,抽象数据类型(ADT)是一种非常重要的概念&…...
蓝桥杯每日真题 - 第16天
题目:(卡牌) 题目描述(13届 C&C B组C题) 解题思路: 题目分析: 有 n 种卡牌,每种卡牌的现有数量为 a[i],所需的最大数量为 b[i],还有 m 张空白卡牌。 每…...
【Docker】快速部署 Pikachu:一个包含常见 Web 安全漏洞的渗透测试练习靶场
系统介绍 Pikachu是一个带有漏洞的Web应用系统,在这里包含了常见的web安全漏洞。 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习,那么Pikachu可能正合你意。 Pikachu上的漏洞类型列表如下: Burt Force(暴力破解漏洞) XSS…...
【解决】Unity TMPro字体中文显示错误/不全问题
问题描述:字体变成方块 原因:字体资源所承载的长度有限 1.找一个中文字体放入Assets中 2.选中字体创建为TMPro 字体资源 3.选中创建好的字体资源(蓝色的大F) 在右边的属性中找到Atlas Width h和 Atlas Heigth,修改的大一点&…...
速通前端篇 —— CSS
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏:速通前端 目录 CSS的介绍 基本语法规范 CSS选择器 标签选择器 class选择器 id选择器 复合选择器 通配符选择器 CSS常见样式 颜…...
【案例】泛微.齐业成助力北京中远大昌汽车实现数电票全流程管理
中远大昌统一发票共享平台上线三个多月以来,实现: 5000份 60000元 发票开具 成本节约 客户简介及需求分析 北京中远大昌汽车服务有限公司(以下简称“中远大昌”)成立于2002年,是中远海运集团所属香远(北…...
Windows系统运行库软件游戏修复工具
本页面下载的资源包包括PC电脑常用的运行库和电脑必备组件,如您的电脑出现应用打不开,缺少dll链接库、闪退等现象可以尝试用下面软件修复。 本资源永久有效。 软件安装基本常识科普: 为什么要安装运行库?运行库默认安装到C盘&…...
解决.DS_Store 在项目一致无法排除,.gitignore里也不生效
.DS_Store 是 macOS 操作系统创建的隐藏文件,通常用于存储目录的属性,比如视图设置、图标位置等。它通常不应包含在代码仓库中,因此需要排除它。你提到即使将其添加到 .gitignore 文件中,仍然无法排除它,可能是由于以下…...
C#里怎么样判断文件是否存在?
C#里怎么样判断文件是否存在? 判断文件是否存在,也是一个常用的功能。 因为文件如果不存,直接去操作,就会抛出异常。 比如要拷贝一个文件到另外一个目录里,如果文件已经存在那个文件夹里,也会抛出异常。 所以提前判断,就可以减少很多不必要的异常抛出,同时程序写得更加…...
汽车免拆诊断案例 | 2012款路虎揽胜运动版柴油车加速无力
故障现象 一辆2012款路虎揽胜运动版车,搭载3.0T柴油发动机(型号为306DT),累计行驶里程约为10.2万km。车主进厂反映,车辆行驶中加速无力,且发动机故障灯异常点亮。 故障诊断 接车后试车,发动…...
JAVA基础
JAVA基础 JAVA的变量 变量是什么 内存中存储的,数值可以改变的数据 定义变量 语法 数据类型 变量名 值; 变量的命名: 必须由字母、数字、下划线和$组成;开头不能是数字规范的命名是小驼峰命令:studentName要做…...
ssh无法连接Ubuntu
试了多次ssh都无法连接,明明可以上网 网卡、防火墙、端口都没有问题,就是连接不上 结果是这个版本Ubuntu镜像默认没有安装ssh服务 安装SSH服务:apt-get install openssh-server 开启SSH服务:/etc/init.d/ssh start 就可以连接…...
spi 回环
///tx 极性0 (sclk信号线空闲时为低电平) /// 相位0 (在sclk信号线第一个跳变沿进行采样) timescale 1ns / 1ps//两个从机 8d01 8d02 module top(input clk ,input rst_n,input [7:0] addr ,input …...
MongoDB 更新集合名
MongoDB 更新集合名 MongoDB 是一个流行的 NoSQL 数据库,它使用集合(collections)来存储文档(documents)。集合在 MongoDB 中相当于关系型数据库中的表。在 MongoDB 中,集合名是可以在某些情况下进行更新的…...
常见面试题----深入源码理解MQ长轮询优化机制
引言 在分布式系统中,消息队列(Message Queue, MQ)扮演着至关重要的角色。MQ不仅实现了应用间的解耦,还提供了异步消息处理、流量削峰等功能。而在MQ的众多特性中,长轮询(Long Polling)机制因其…...
Firewall防火墙配置
文章目录 一、firewalld简介二、firewalld特性三、firewalld相关文件及目录四、firewalld配置五、firewalld配置实例一、firewalld简介 firewalld 提供了支持网络/防火墙区域(zone)定义网络链接以及接口安全等级的动态防火墙管理工具。它支持 ipv4, ipv6 防火墙设置以及以太网…...
我做了一份斯坦福CS229吴恩达机器学习笔记
吴恩达教授的机器学习课程,可以说是AI领域的一块金字招牌。这门在斯坦福大学开设的课程,历经十余年,依旧是机器学习入门的经典之作。 记得当年,这门课火爆到吴恩达教授不得不将其搬到线上,结果不仅在斯坦福,…...
TESSY单元测试工具详解与操作演示:ISO 26262合规性、自定义测试用例、详细测试报告等
在嵌入式系统开发中,安全性和可靠性至关重要,尤其是在汽车、医疗和工业控制等高风险行业。为了确保代码在实际运行中满足安全标准,开发者需要一套完善全面的测试工具,来严格检测代码的安全性和可靠性。 TESSY作为一款已获得IEC 61…...
C++语言之函数对象与算法
在 C 中,函数对象(Function Object)也叫仿函数(Functor),是一个类,这个类重载了()运算符。从概念上讲,它的行为类似于一个函数,可以像调用函数一样来调用这个类的对象。 …...
Fakelocation Server服务器/专业版 Windows11
前言:需要Windows11系统 Fakelocation开源文件系统需求 Windows11 | Fakelocation | 任务一 打开 PowerShell(以管理员身份)命令安装 Chocolatey Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProto…...
java使用itext生成pdf
一、利用Adobe Acrobat DC软件创建pdf模板 备好Adobe Acrobat DC软件 1.excel/jpg/png文件转pdf文件 右击打开我们要转换的文件 2.然后点击 添加 域 3.可以看到域的名字 4.调整字体大小/对齐方式等 5.保存 二,代码部分 首先 上依赖 <dependency><group…...
【PPTist】添加PPT模版
前言:这篇文章来探索一下如何应用其他的PPT模版,给一个下拉菜单,列出几个项目中内置的模版 PPT模版数据 (一)增加菜单项 首先在下面这个菜单中增加一个“切换模版”的菜单项,点击之后在弹出框中显示所有的…...
AmazonS3集成minio实现https访问
最近系统全面升级到https,之前AmazonS3大文件分片上传直接使用http://ip:9000访问minio的方式已然行不通,https服务器访问http资源会报Mixed Content混合内容错误。 一般有两种解决方案,一是升级minio服务,配置ssl证书,…...
适配屏幕px、rem单位换算, 将 pxToRem 函数设置为一个全局工具如:在 utils.js 文件、SCSS/Mixin 定义
页面的宽度适配: 假设页面的根元素(html)的字体大小设置为动态值(常用 rem 单位适配时的做法), 比如 html { font-size: (屏幕宽度 / 设计稿宽度) }。如果根元素的字体大小为 1rem 屏幕宽度 / 1920px&…...
H.265流媒体播放器EasyPlayer.js播放器提示MSE不支持H.265解码可能的原因
随着人工智能和机器学习技术的应用,流媒体播放器将变得更加智能,能够根据用户行为和偏好提供个性化的内容推荐。总体而言,流媒体播放器的未来发展将更加注重技术创新和用户互动,以适应不断变化的市场需求和技术进步。 提示MSE不支…...
医学图像语义分割:前列腺肿瘤、颅脑肿瘤、腹部多脏器 MRI、肝脏 CT、3D肝脏、心室
医学图像语义分割:前列腺肿瘤、颅脑肿瘤、腹部多脏器 MRI、肝脏 CT、3D肝脏、心室 语义分割网络FCN:通过将全连接层替换为卷积层并使用反卷积上采样,实现了第一个端到端的像素级分割网络U-Net:采用对称的U形编解码器结构ÿ…...
16. 指针类型和步长概念问题
1. 项目场景: ➣ Jack Qiao对米粒说:“今天有道友遇到一个问题,举个栗子数组 arr[5] { 0 };道友发现&arr[0] 1与&arr 1打印出来的地址竟然不同。”米粒测试后果然是这样。 2. 问题描述 ☑ 举个栗子:数组 arr[5] { 0…...
【电路笔记】-布尔逻辑AND函数
逻辑AND函数 文章目录 逻辑AND函数1、概述2、逻辑 AND 函数 仅当所有输入均为 true 时,逻辑与函数输出才为 true,否则输出为 false。 1、概述 布尔代数基于逻辑函数,其中每个布尔函数(例如逻辑 AND 函数)通常具有一个…...
数据结构C语言描述3(图文结合)--双链表、循环链表、约瑟夫环问题
前言 这个专栏将会用纯C实现常用的数据结构和简单的算法;有C基础即可跟着学习,代码均可运行;准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言…...
wend看源码-APISJON
项目地址 腾讯APIJSON官方网站 定义 APIJSON 可以定义为一个面向HTTP 协议的JSON 规范,一个面向数据访问层的ORM 框架。其主要工作流程包括:前端按照既定格式组装 JSON 请求报文,通过 APIJSON-ORM 将这些报文直接转换为 SQL 语句,…...
CSS(8):盒子阴影与文字阴影
一:盒子阴影text-shadow属性 1.box-shadow:h-shadow v-shadow blur spread color inset; 默认的是外部阴影outset,不能写在代码上 2.鼠标经过盒子后的阴影 rgba透明度 3.文字阴影 text-shadow:水平偏移 垂直偏移 模糊度 阴影颜色; 注意点…...
Hadoop 系列 MapReduce:Map、Shuffle、Reduce
文章目录 前言MapReduce 基本流程概述MapReduce 三个核心阶段详解Map 阶段工作原理 Shuffle 阶段具体步骤分区(Partition)排序(Sort)分组(Combine 和 Grouping) Reduce 阶段工作原理 MapReduce 应用场景Map…...
web——sqliabs靶场——第十三关——报错注入+布尔盲注
发现是单引号加括号闭合的 尝试联合注入 发现不太行,那尝试报错注入。 测试报错注入 unameadmin) and updatexml(1,0x7e,3) -- &passwdadmin&submitSubmit 爆数据库 unameadmin) and updatexml(1,concat(0x7e,database(),0x7e),3) -- &passwdadmin&a…...
调大Vscode资源管理器字体
对于调整资源管理器字体大小(也就是下图红框),查找了网上很多方法。要么介绍的方法是调整了代码字体,要么是调节了终端字体,要么是通过整体放缩实现的调整,总之都不合适。 唯一的调整方法是在几篇CSDN里看到…...
【新人系列】Python 入门(十一):控制结构
✍ 个人博客:https://blog.csdn.net/Newin2020?typeblog 📝 专栏地址:https://blog.csdn.net/newin2020/category_12801353.html 📣 专栏定位:为 0 基础刚入门 Python 的小伙伴提供详细的讲解,也欢迎大佬们…...
后端开发详细学习框架与路线
🚀 作者 :“码上有前” 🚀 文章简介 :后端开发 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 为帮助你合理安排时间,以下是结合上述学习内容的阶段划分与时间分配建议。时间安排灵活&a…...
类文件结构详解.上
字节码 在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题&…...
【K8S问题系列 |18 】如何解决 imagePullSecrets配置正确,但docker pull仍然失败问题
如果 imagePullSecrets 配置正确,但在执行 docker pull 命令时仍然失败,可能存在以下几种原因。以下是详细的排查步骤和解决方案。 1. 检查 Docker 登录凭证 确保你使用的是与 imagePullSecrets 中相同的凭证进行 Docker 登录: 1.1 直接登录…...
Vue+Springboot用Websocket实现协同编辑
1. 项目介绍 在本文中,我们将介绍如何使用Vue.js和Spring Boot实现一个支持多人实时协同编辑的Web应用。通过WebSocket技术,我们可以实现文档的实时同步,让多个用户同时编辑同一份文档。 2. 技术栈 前端:Vue.js 3 Vuex后端&am…...
高阶C语言补充:柔性数组
C99中,结构体中最后一个元素允许时未知大小的数组,这就叫做柔性数组成员。 vs编译器也支持柔性数组。 之所以把柔性数组单独列出,是因为: 1、柔性数组是建立在结构体的基础上的。 2、柔性数组的使用用到了动态内存分配。 这使得柔…...
MYSQL——多表查询、事务和索引
概括 出现查询结果个数为笛卡尔积的原因是sql语句: select * from tb_emp,tb_dept; 没有加上where tb_emp.dept_id tb_dept.id;(where条件可以消除笛卡尔积) select * from tb_emp,tb_dept where tb_emp.dept_id tb_dept.id; 查询类型 …...
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
文章目录 前言摘要描述题解答案题解代码题解代码分析示例测试及结果时间复杂度空间复杂度总结 前言 本题由于没有合适答案为以往遗留问题,最近有时间将以往遗留问题一一完善。 LeetCode - #140 单词拆分 II 不积跬步,无以至千里;不积小流&…...
Unity类银河战士恶魔城学习总结(P132 Merge skill tree with skill Manager 把技能树和冲刺技能相组合)
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了解锁技能后才可以使用技能,先完成了冲刺技能的锁定解锁 Dash_Skill.cs using System.Collections; using System…...
JavaScript 中 arguments、类数组与数组的深入解析
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 💯前言💯什么是 arguments 对象2.1 arguments 的定义2.2 arguments 的特性2.3 使用场景 💯深入了解 arguments 的结构3.1 arguments 的内部结构arguments 的关键属性…...
【java-Neo4j 5开发入门篇】-最新Java开发Neo4j
系列文章目录 前言 上一篇文章讲解了Neo4j的基本使用,本篇文章对Java操作Neo4j进行入门级别的阐述,方便读者快速上手对Neo4j的开发。 一、开发环境与代码 1.docker 部署Neo4j #这里使用docker部署Neo4j,需要镜像加速的需要自行配置 docker run --name…...
摄影:相机控色
摄影:相机控色 白平衡(White Balance)白平衡的作用: 白平衡的使用环境色温下相机色温下总结 白平衡偏移与包围白平衡包围 影调 白平衡(White Balance) 人眼看到的白色:会自动适应环境光线。 相…...
UI自动化测试中公认最佳的设计模式-POM
一、概念 什么是POM? POM是PageObjectModule(页面对象模式)的缩写,其目的是为了Web UI测试创建对象库。在这种模式下,应用涉及的每一个页面应该定义为一个单独的类。类中应该包含此页面上的页面元素对象和处理这些元…...
数字图像处理(2):Verilog基础语法
(1)Verilog常见数据类型: reg型、wire型、integer型、parameter型 (2)Verilog 常见进制:二进制(b或B)、十进制(d或D)、八进制(o或O)、…...