MIT6.828 Lab1 Xv6 and Unix utilities

2023MIT6.828 lab-1

官方地址

一、sleep

实验内容

  • 调用sleep(系统调用)编写用户级别程序
  • 能暂停特定时常的系统滴答
  • 程序保存在user/sleep.c

实验过程

xv6的参数传递

查看官方文档提示的文件中,多采用如下定义:

int main(int argc, char *argv[])
在xv6操作系统中:
1、argc 是一个常见的参数,用于表示传递给程序的命令行参数的数量。 
2、argv(argument vector)是一个指向字符指针数组的指针,该数组中的每个元素都是一个指向命令行参数的字符串的指针。
3、程序的内部可通过argc和argv来访问外界输入的参数
注意:
1、argc指示出程序执行时传入的参数个数,常可用来判断输入是否符合要求
2、argv[]为存放字符指针的数组,我们需要的是其指向地址存放的数据,若要当整型数据使用还需进行相应转换

实现代码

#include "kernel/types.h"     //调用相应的头文件
#include "kernel/stat.h"
#include "user/user.h"
int main( int argc ,char *argv[])  //通过argc和argv传入参数
{if(argc!=2)   //传入参数数量不符合{fprintf(2,"usage:sleep time\n");exit(1);}
sleep(atoi(argv[1]));  //由于argc[]指向字符型数据,调用atoi()进行转换
fprintf(2,"nothing happens for a little while\n");
exit(0);  //退出程序
}

编译准备

在make qemu之前,还需进行将编写的sleep()程序加入编译文件中:

1、进入lab目录下
vi Makefile  //打开文件
: ?UPROGS   //vim下搜索关键词2、定位到UPROGS,在最后一处按格式补入sleep():
UPROGS=\$U/_cat\$U/_echo\$U/_forktest\$U/_grep\$U/_init\$U/_kill\$U/_ln\$U/_ls\$U/_mkdir\$U/_rm\$U/_sh\$U/_stressfs\$U/_usertests\$U/_grind\$U/_wc\$U/_zombie\$U/_sleep\3、保存退出

编译运行

make qemuxv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$ sleep 10
nothing happens for a little while  //停顿后显示,达到实验效果

工具测试

在lab目录下执行:
$ ./grade-lab-util sleep
或
$ make GRADEFLAGS=sleep grade

输出:
在这里插入图片描述
测试通过

二、pingpong

实验内容

  • 在父进程中创建子进程
  • 编写用户级程序调用系统调用通过一对pipes在两个进程间实现’‘ping-pong’’ 一个字节
  • 父进程给子进程发送一字节,子进程收到后print pid: received ping 这里的pid is its process ID,并通过pipes给父进程传递一字节,然后exit
  • 父进程读取来自子进程的字节,并print pid: received pong,然后exit
  • 程序保存在user/pingpong.c

实验过程

fork用法

打开/kernel/proc.c查看fork()源码

// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
{int i, pid;struct proc *np;struct proc *p = myproc();// Allocate process.if((np = allocproc()) == 0){return -1;}// Copy user memory from parent to child.if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){freeproc(np);release(&np->lock);return -1;}np->sz = p->sz;// copy saved user registers.*(np->trapframe) = *(p->trapframe);// Cause fork to return 0 in the child.np->trapframe->a0 = 0;// increment reference counts on open file descriptors.for(i = 0; i < NOFILE; i++)if(p->ofile[i])np->ofile[i] = filedup(p->ofile[i]);np->cwd = idup(p->cwd);safestrcpy(np->name, p->name, sizeof(p->name));pid = np->pid;release(&np->lock);acquire(&wait_lock);np->parent = p;release(&wait_lock);acquire(&np->lock);np->state = RUNNABLE;release(&np->lock);return pid;
}

该函数的作用:
1、创建一个新进程,并复制父进程的内容
2、为子进程(child process)设置内核栈(kernel stack),以便在子进程执行完毕后,其行为表现得就像是从 fork() 系统调用返回一样
3、父子进程内容相同(除个别数据),但内存空间不同,修改互不影响
4、在父进程中,fork()返回新创建的子进程的进程ID;在子进程中,fork()返回0;如果发生错误,则返回-1

结构体proc的声明
该结构体存储了每一个进程的信息

// Per-process state
struct proc {struct spinlock lock;// p->lock must be held when using these:enum procstate state;        // Process statevoid *chan;                  // If non-zero, sleeping on chanint killed;                  // If non-zero, have been killedint xstate;                  // Exit status to be returned to parent's waitint pid;                     // Process ID// wait_lock must be held when using this:struct proc *parent;         // Parent process// these are private to the process, so p->lock need not be held.uint64 kstack;               // Virtual address of kernel stackuint64 sz;                   // Size of process memory (bytes)pagetable_t pagetable;       // User page tablestruct trapframe *trapframe; // data page for trampoline.Sstruct context context;      // swtch() here to run processstruct file *ofile[NOFILE];  // Open filesstruct inode *cwd;           // Current directorychar name[16];               // Process name (debugging)
};

pipes用法

pipe()函数定义

int pipe(int p[])  Create a pipe, put read/write file descriptors in p[0] and p[1]. 

1、调用pipe函数来创建一个新的管道。如果成功,pipe函数返回0,并将两个文件描述符分别存放在p[0]和p[1]中。如果失败,返回-1。
2、pipe为半双工通信,要根据实际指定通信方向,关闭管道的读或写,保留另一功能
3、可采用两条pipe实现互通
在这里插入图片描述

getpid用法

获取当前进程PID
int getpid()  Return the current process’s PID. 

实现代码

注意:读写顺序
#include "kernel/types.h"
#include "user/user.h"int main(int argc,int *argv[])
{int ptc_pipe[2];int ctp_pipe[2];pipe(ptc_pipe); //父to子pipe(ctp_pipe);//子to父int pid;pid = fork(); //新建子进程if(pid==0) //处于子进程{//设定管道通信方向close(ptc_pipe[1]); //子读取close(ctp_pipe[0]); //子写入char buff[16];if( read( ptc_pipe[0],buff,1) ==1 ) //子进程收到父进程的一字节{printf("%d: received ping\n",getpid() );}write( ctp_pipe[1],"p",1 ); //往父进程发送一字节exit(0);}else //处于父进程{//设置管道方向close( ptc_pipe[0] );// 父写入close( ctp_pipe[1] );// 父读取write( ptc_pipe[1],"p",1 ); //往子进程发送一字节char buff[16];if( read( ctp_pipe[0],buff,1)==1 )//父进程读取到一字节{printf("%d: received pong\n",getpid() );}}
exit(0);
}

编译准备

往Makefile中UPROGS=\项目添加  $U/_pingpong\

运行结果

在这里插入图片描述

工具测试

make GRADEFLAGS=pingpong grade

在这里插入图片描述
测试通过

三、primes

实验内容

1、在xv6中使用pipe编写一个并发的素数筛选程序
2、使用pipe和fork来建立管道
3、第一个进程向管道内输入数字2到35
4、对于每个素数,创建一个进程来从管道左侧读取,并传输给另一条管道右侧
5、受限于xv6的有限文件描述符和进程,第一个进程将在35处停止
6、程序位于user/primes.c

实验过程

过程分析

1、采用pipe进行数字传递,用fork创建下一级,直到结束
2、每级中挑选出一个素数,把剩余经过此素数处理过的传递到下一级
3、传递后父进程要执行等待子进程结束的操作

实现代码

注意:
1、仔细关闭程序不需要的文件描述符,否则在35前xv6的资源会耗尽
2、pipe用完关闭,避免拥塞
3、注意pipe和fork执行顺序

#include "kernel/types.h"
#include "user/user.h"int children( int ptc_pipe[])
{//设置管道方向close( ptc_pipe[1] );//子读取,不需要写入//对父到子管道进行检查int num;if( read( ptc_pipe[0] ,&num,sizeof(num)) == 0 ) //管道空{close( ptc_pipe[0]);exit(0);}else{printf("prime %d\n",num); //打印素数}//创建孙进程int pid;int ctg_pipe[2];pipe( ctg_pipe );//子进程到孙进程管道pid=fork();if( pid==0 ) //孙子进程{children( ctg_pipe );  //递推}else  //子进程{//设置子到孙管道方向close( ctg_pipe[0] );//子写孙读int i;while( read( ptc_pipe[0],&i,sizeof(i))>0 ) //管道还有数据{if( i % num !=0 )//有余数write( ctg_pipe[1],&i,sizeof(i) );//发送到下一级}close( ctg_pipe[1]);//关闭写,避免读拥塞wait(0); //等待孙进程结束}exit(0);//结束返回}
int main(int argc,int *argv[])
{int ptc_pipe[2];//建立第一个管道pipe(ptc_pipe);int pid;pid =fork(); //创建子进程if( pid==0 ) //位于子进程{children(ptc_pipe); //函数反复调用}else //位于父进程{//管道设置close(ptc_pipe[0]); //父写入,不需要读取for(int i=2;i<=35;i++) //往管道输入数字{write(ptc_pipe[1],&i,sizeof(i));}close( ptc_pipe[1]);//关闭写,避免读拥塞wait(0);//等待子进程}
exit(0);
}

编译准备

往Makefile中UPROGS=\项目添加  $U/_primes\

运行结果

在这里插入图片描述

工具测试

 make GRADEFLAGS=primes grade

在这里插入图片描述
测试通过

四、find

实验内容

  1. 为Xv6编写一个简单版本的UNIX查找程序
  2. 通过目录树的形式找到特定的名称
  3. 程序保存在user/find.c

实验过程

线索提示

  1. 查看user/ls.c如何实现读目录
  2. 使用递归操作时find能深入子目录
  3. 使用make clean去获得干净的文件系统,然后make qemu
  4. 使用C语言的字符串
  5. 使用strcmp()对比字符串,而不是==

ls.c源码

fmtname用于格式化文件名
char*
fmtname(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++;// Return blank-padded name.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;if((fd = open(path, O_RDONLY)) < 0){fprintf(2, "ls: cannot open %s\n", path);return;}if(fstat(fd, &st) < 0){fprintf(2, "ls: cannot stat %s\n", path);close(fd);return;}switch(st.type){case T_DEVICE: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;}strcpy(buf, path);p = buf+strlen(buf);*p++ = '/';while(read(fd, &de, sizeof(de)) == sizeof(de)){if(de.inum == 0)continue;memmove(p, de.name, DIRSIZ);p[DIRSIZ] = 0;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);
}
 ls函数中使用的结构体信息
//用于表示一个目录项
struct dirent { ushort inum; // 本目录下该文件所在的inode号 char name[DIRSIZ]; // 该文件的文件名 };
struct stat { short type; // 文件类型 int dev; // 设备号 uint ino; // inode编号 short nlink; // 链接数 uint size; // 文件大小(字节) time_t atime; // 最后访问时间 time_t mtime; // 最后修改时间 time_t ctime; // 最后状态改变时间 int uid; // 用户ID int gid; // 组ID short rdev; // 特殊设备类型 uint flags; // 文件标志 uint pad[4]; // 填充字段,确保结构大小对齐 };
memmove函数
void *memmove(void *dest, const void *src, size_t n);
//dest:指向目标内存区域的指针,即要将数据移动到的位置。
//src:指向源内存区域的指针,即要从中复制数据的起始位置。
//n:要复制的字节数。
 open函数
int open(const char *path, int flags);
//`path`:要打开的文件或设备的路径名。
//`flags`:指定打开文件的方式,例如 `O_RDONLY`(只读)、`O_WRONLY`(只写)、`O_RDWR`(读写)等。
//返回值:非负文件描述符
//       失败返回-1
 fstat函数
int fstat(int fd, struct stat *buf);
//fd:文件描述符,即之前通过 open 系统调用获得的用于标识文件的整数。
//buf:一个指向 stat 结构体的指针,该结构体用于存储获取到的文件状态信息。
//返回值:0成功,-1失败

实现代码

实现思路:

  • 通过层层搜索
  • 遇到文件则判断输出
  • 遇到目录则延长到子目录采用递归搜寻
  • 在ls.c基础上修改
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
//#include "kernel/fcntl.h"char*
fmtname(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++; //+1为'/'后第一个字符// Return blank-padded name.if(strlen(p) >= DIRSIZ)return p;memmove(buf, p, strlen(p)+1); //注意结束符return buf;
}void
findname(char *path,char *findName)
{char buf[512], *p; //buf用于存储绝对路径,p用于指向buf操作int fd;            //存储文件标识符struct dirent de;  //表示目录项struct stat st;    //存储文件信息//只读打开path路径if((fd = open(path, 0)) < 0)return; //打开错误if(fstat(fd, &st) < 0) //打开错误{close(fd);return;}switch(st.type)  //顺利开启{case T_DEVICE:case T_FILE:   //path路径为文件if( strcmp( fmtname(path),findName)==0) //抽取名称并对比,名称一致{printf("%s\n", path); //打印路径}break;case T_DIR: //path路径为目录//目录名过长if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){printf("find: path too long\n");break;}//复制path到buf//buf记录此刻path的绝对路径strcpy(buf, path);p = buf+strlen(buf);*p++ = '/'; //末尾加分隔符//注意此处指针p的指向buf末尾while(read(fd, &de, sizeof(de)) == sizeof(de)) //读取path路径目录内容//read会遍历一遍此目录下的文件{if(de.inum == 0)  //inode==0continue;//不正常的inode,跳出//跳过. ..目录if(strcmp(de.name, ".") == 0 || strcmp(de.name, "..")==0 )continue;//复制名称到p头地址的空间  //即在buf后面补名字memmove(p, de.name, DIRSIZ);p[DIRSIZ] = 0; //地址结束标识符findname(buf,findName); //递归查询}break;}close(fd);
}int
main(int argc, char *argv[])
{//参数不符合if(argc <=2){printf("err\n");exit(0);}findname(argv[1],argv[2]);exit(0);
}

编译准备

往Makefile中UPROGS=\项目添加  $U/_find\

运行结果

make qemu echo > b
mkdir a
echo > a/b
mkdir a/aa
echo > a/aa/b
find . b
root@QQQ:/home/MIT6.828/xv6-labs-2023# make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -global virtio-mmio.force-legacy=false -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0xv6 kernel is bootinghart 1 starting
hart 2 starting
init: starting sh
$ echo > b
mkdir a
echo > a/b
mkdir a/aa
echo > a/aa/b
find . b$ $ $ $ $
./b
./a/b
./a/aa/b
$
符合预期

工具测试

make GRADEFLAGS=find grade
no-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -march=rv64g -nostdinc -I. -Ikernel -c user/initcode.S -o user/initcode.o
riscv64-unknown-elf-ld -z max-page-size=4096 -N -e start -Ttext 0 -o user/initcode.out user/initcode.o
riscv64-unknown-elf-objcopy -S -O binary user/initcode.out user/initcode
riscv64-unknown-elf-objdump -S user/initcode.o > user/initcode.asm
riscv64-unknown-elf-ld -z max-page-size=4096 -T kernel/kernel.ld -o kernel/kernel kernel/entry.o kernel/kalloc.o kernel/string.o kernel/main.o kernel/vm.o kernel/proc.o kernel/swtch.o kernel/trampoline.o kernel/trap.o kernel/syscall.o kernel/sysproc.o kernel/bio.o kernel/fs.o kernel/log.o kernel/sleeplock.o kernel/file.o kernel/pipe.o kernel/exec.o kernel/sysfile.o kernel/kernelvec.o kernel/plic.o kernel/virtio_disk.o kernel/start.o kernel/console.o kernel/printf.o kernel/uart.o kernel/spinlock.o
riscv64-unknown-elf-objdump -S kernel/kernel > kernel/kernel.asm
riscv64-unknown-elf-objdump -t kernel/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel/kernel.sym
make[1]: Leaving directory '/home/MIT6.828/xv6-labs-2023'
== Test find, in current directory == find, in current directory: OK (3.1s)
== Test find, recursive == find, recursive: OK (0.9s)
测试通过

五、xargs

实验内容

  • 为xv6编写一个简单版本的uinx xargs程序
  • 它的参数描述了要运行的命令
  • 它从标准输入中读取行
  • 它运行每一行的命令
  • 将此行文本添加到命令参数中
  • 程序存放在user/xargs.c
  • 一个实例:
    • $ echo hello too | xargs echo bye
    • bye hello too
    • $

实验过程

实现方法

  • 从标准输入中读取命令并执行
  • 设置两个数组,一个用于从标准输入中获取每一行的命令及其参数,一个用于将获取的一行指令传递给子程序执行
  • 不断读取标准输入,遇到换行符就执行一次命令,直到标准输入中无数据

exec函数

int exec(char *file, char *argv[]) Load a file and execute it with arguments; only returns if error.
  • 加载一个文件,并按给定参数执行

实现代码

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"#define MAXN 1024int
main(int argc, char *argv[])
{if (argc < 2){fprintf(2, "usage: xargs command\n");exit(1);}char *argv_c[MAXARG];    // 存放子进程exec的参数//MAXARG是一个宏定义,用于限制一个进程可以从其命令行参数中获取的最大字节for (int i = 1; i < argc; i++)  //去除xargs,保留余下部分argv_c[i - 1] = argv[i];char buf[MAXN]; // 存放从标准输入转化而来的参数char c = 0;int stat = 1;   // 从标准输入read返回的状态while (stat) // 标准输入中还有数据{int buf_tail = 0;    // buf尾指针int arg_begin = 0;  // 当前这个参数在buf中开始的位置int argv_tail = argc - 1;//argv本身还有参数while (1)   // 读取一行 //每行执行一次{stat = read(0, &c, 1);      //从标准输入中读取一个字符//标准输入的文件描述符通常为0if (stat == 0) // 标准输入中没有数据,exitexit(0);if (c == ' ' || c == '\n')  //此字符为空格或换行//意味着单个参数输入结束或开始换行{buf[buf_tail++] = 0;    //最后一个参数必需为0,否则会执行失败//先0后自增argv_c[argv_tail++] = &buf[arg_begin]; //往argv_c中拼接一个参数//字符串操作arg_begin = buf_tail;   //更新下一个参数的起点位置if (c == '\n')          //每此换行执行一次break;}else //拼接读取的单个字符{buf[buf_tail++] = c;}}argv_c[argv_tail] = 0;  //结束标志if (fork() == 0){exec(argv_c[0], argv_c); //加载命令并执行}else{wait(0);}}exit(0);
}

编译准备

往Makefile中UPROGS=\项目添加  $U/_xargs\

运行结果

root@QQQ:/home/MIT6.828/xv6-labs-2023# make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -global virtio-mmio.force-legacy=false -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0xv6 kernel is bootinghart 2 starting
hart 1 starting
init: starting sh
$ echo hello too | xargs echo bye
bye hello too
$ find . b | xargs grep hello
hello
hello
hello
$ sh < xargstest.sh
$ mkdir: a failed to create
$ $ mkdir: c failed to create
$ $ $ hello
hello
hello
$ $

工具测试

riscv64-unknown-elf-ld -z max-page-size=4096 -N -e start -Ttext 0 -o user/initcode.out user/initcode.o
riscv64-unknown-elf-objcopy -S -O binary user/initcode.out user/initcode
riscv64-unknown-elf-objdump -S user/initcode.o > user/initcode.asm
riscv64-unknown-elf-ld -z max-page-size=4096 -T kernel/kernel.ld -o kernel/kernel kernel/entry.o kernel/kalloc.o kernel/string.o kernel/main.o kernel/vm.o kernel/proc.o kernel/swtch.o kernel/trampoline.o kernel/trap.o kernel/syscall.o kernel/sysproc.o kernel/bio.o kernel/fs.o kernel/log.o kernel/sleeplock.o kernel/file.o kernel/pipe.o kernel/exec.o kernel/sysfile.o kernel/kernelvec.o kernel/plic.o kernel/virtio_disk.o kernel/start.o kernel/console.o kernel/printf.o kernel/uart.o kernel/spinlock.o
riscv64-unknown-elf-objdump -S kernel/kernel > kernel/kernel.asm
riscv64-unknown-elf-objdump -t kernel/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel/kernel.sym
make[1]: Leaving directory '/home/MIT6.828/xv6-labs-2023'
== Test xargs == xargs: OK (4.2s)
root@QQQ:/home/MIT6.828/xv6-labs-2023#
测试通过

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

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

相关文章

Idea打包jar包的多种方式(解决MANIFEST.MF被覆盖的问题)

目录 生成jar文件 方式一&#xff1a;打包成一个总的jar文件 方式二&#xff1a;打包的jar文件和第三方jar文件分开 生成jar文件 打开“文件->项目结构-构建” 点击“”选择“jar->from modules ....” 弹出“从模块创建 jar” 方式一&#xff1a;打包成一个总的jar…

海纳斯删除广告位

找到文件 vim /var/www/html/home.php 删除代码段 <div class"adleft" id"adleftContainer"><button onclick"closeAd()">关闭</button><a href"https://www.ecoo.top/ad.html" target"_blank">&l…

JAVA—抽象—定义抽象类Converter及其子类WeightConverter

同样&#xff0c;我们由这道题引出抽象类&#xff0c;抽象方法这个概念。 按下面要求定义类Converter及其子类WeightConverter 定义抽象类&#xff1a;Converter&#xff1a; 定义一个抽象类Converter&#xff0c;表示换算器&#xff0c;其定义的如下&#xff1a; 一个私有…

海外仓的出入库流程有什么痛点?位像素海外仓系统怎么提高出入库效率?

随着跨境电商的蓬勃发展&#xff0c;海外仓是其中不可或缺的一个关键环节。而货物的出库与入库则是海外仓管理中的一个核心业务流程&#xff0c;它的运作效率直接影响到整个跨境物流的效率和客户体验。今天&#xff0c;让我们具体来看一看关于海外仓出入库的流程&#xff0c;其…

JVM内存性能调优思路之:通过GC log、Thread Dump 、Heap Dump分析内存使用说明

文章目录 一. 各日志概述1. Garbage Collection Log - 找到GC规律2. 线程转储(Thread dump) - 分析&#xff08;快照&#xff09;线程状态3. 堆转储(Heap dump) - APP某刻内存使用全貌 二. 命令1. 程序的gc日志2. 线程转储3. 堆转储 概述 在 Java 虚拟机中&#xff0c;(GC) Gar…

C++ 类和对象(初篇)

类的引入 C语言中&#xff0c;结构体中只能定义变量&#xff0c;在C中&#xff0c;结构体内不仅可以定义变量&#xff0c;也可以定义函数。 而为了区分C和C我们将结构体重新命名成class去定义 类的定义 标准格式&#xff1a; class className {// 类体&#xff1a;由成员函…

【计算机毕业设计】计算机考试系统设计——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…

华为openEuler-22.03-LTS-SP3配置yum源

先有华为后有天&#xff0c;遥遥领先&#xff01; 1 确定使用的OS版本 # cat /etc/os-release NAME"openEuler" VERSION"22.03 (LTS-SP3)" ID"openEuler" VERSION_ID"22.03" PRETTY_NAME"openEuler 22.03 (LTS-SP3)" ANSI…

【NLP】关于BERT模型的一些认知

BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;模型是由Google在2018年提出的预训练Transformer模型&#xff0c;用于自然语言处理任务。 一. BERT模型的架构 1.1 输入表示 / Encoder模块 BERT中的Encoder模块是由三种Embedding&…

GD32F470_ DS18B20温度传感器模块移植

DS18B20温度传感器 DS18B20数字温度传感器提供9位至12位精度的温度测量&#xff0c;并具有非易失性用户可编程上下触发点报警功能。DS18B20通过单总线通信&#xff0c;根据定义&#xff0c;只需要一条数据线(和地线)即可与单片机通信。此外&#xff0c;DS18B20可以直接从数据线…

vue2开发好还是vue3开发好vue3.0开发路线

Vue 2和Vue 3都是流行的前端框架&#xff0c;它们各自有一些特点和优势。选择Vue 2还是Vue 3进行开发&#xff0c;主要取决于你的项目需求、团队的技术栈、以及对新特性的需求等因素。以下是一些关于Vue 2和Vue 3的比较&#xff0c;帮助你做出决策&#xff1a; Vue 2&#xff1…

docker安装nacos,单例模式(standalone),使用mysql数据库

文章目录 前言安装创建文件夹"假装"安装一下nacos拷贝文件夹删除“假装”安装的nacos容器生成nacos所需的mysql表获取mysql-schema.sql文件创建一个mysql的schema 重新生成新的nacos容器 制作docker-compose.yaml文件查看网站 前言 此处有本人写得简易版本安装&…

log4j 集成 ELK环境搭建

一、前言 1.需要准备一台linux服务器&#xff08;最好是CentOS7&#xff09;,内存至少4g以上&#xff08;三个组件都比较占用内存&#xff09; 2.需要有docker使用经验 3. 三个软件的版本要一致 二、安装ElasticSearch 这里先创建一个网络&#xff1a;因为我们还需要部署k…

品牌定位升级|飞雕开关如何从家庭作坊走上国际之路?

飞雕电器,这个名字在中国开关插座行业中如同一面旗帜,自1987年起就扬帆在电工领域的大海中。它不仅见证了这个行业的起起伏伏,还始终以其创新的姿态站在浪尖之上。 飞雕的产品线丰富多彩,除主营的墙壁开关插座领域外,飞雕电器还涉足了与墙壁开关紧密相关的其它领域,现已推出移…

蓝桥杯第十四届C++C组

目录 三国游戏 填充 翻转 【单调队列优化DP】子矩阵 【快速幂、欧拉函数】互质数的个数 【tire树】异或和之差 【质因数分解】公因数匹配 子树的大小 三国游戏 题目描述 小蓝正在玩一款游戏。游戏中魏蜀吴三个国家各自拥有一定数量的士兵X, Y, Z (一开始可以认为都…

Linux中安装nacos

Linux中安装nacos 一、前言二、准备1、下载2、上传到服务器3、解压 三、配置1、备份配置文件2、导入sql3、修改前4、修改后 四、使用1、启动2、关闭 一、前言 最近国外的docker似乎是出什么问题&#xff0c;试过很多手段都拉不下 nacos 镜像&#xff0c;就打算在服务器装一下 …

能否安全地删除 Mac 资源库中的文件?

在管理Mac电脑存储空间时&#xff0c;用户确实可能考虑对资源库&#xff08;Library&#xff09;文件夹进行清理以释放空间。Mac资源库是一个系统及应用程序存放重要支持文件的地方&#xff0c;其中包括但不限于配置文件、临时文件、缓存、插件、偏好设置、应用程序支持数据等。…

Rustdesk二次编译,新集成AI功能开源Gpt小程序为远程协助助力,全网首发

环境&#xff1a; Rustdesk1.1.9 sciter版 问题描述&#xff1a; Rustdesk二次编译&#xff0c;新集成AI功能开源Gpt小程序为远程协助助力,全网首发 解决方案&#xff1a; Rustdesk二次编译&#xff0c;新集成开源AI功能Gpt小程序&#xff0c;为远程协助助力&#xff0c…

【蓝桥杯嵌入式】六、真题演练(三)-2研究篇:第13届第一场真题

温馨提示&#xff1a; 真题演练分为模拟篇和研究篇。本专栏的主要作用是记录我的备赛过程&#xff0c;我打算先自己做一遍&#xff0c;把遇到的问题和不同之处记录到演练篇&#xff0c;然后再返回来仔细研究一下&#xff0c;找到最佳的解题方法记录到研究篇。题目在&#xff1a…

远程过程调用(远程调用)

远程过程调用&#xff08;远程调用&#xff09; 1、什么是分布式计算 在计算机科学中&#xff0c;分布式计算&#xff08;英语&#xff1a;Distributed computing&#xff09;&#xff0c;又译为分散式运算。这个研究领域&#xff0c;主要研究分布式系统&#xff08;Distribu…