6.S081的Lab学习——Lab10: mmap

news2024/9/22 13:45:30

文章目录

  • 前言
  • mmap(hard)
    • 提示:
    • 解析
  • 总结


前言

一个本硕双非的小菜鸡,备战24年秋招。打算尝试6.S081,将它的Lab逐一实现,并记录期间心酸历程。
代码下载

官方网站:6.S081官方网站

安装方式:
通过 APT 安装 (Debian/Ubuntu)
确保你的 debian 版本运行的是 “bullseye” 或 “sid”(在 ubuntu 上,这可以通过运行 cat /etc/debian_version 来检查),然后运行:

sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu 

(“buster”上的 QEMU 版本太旧了,所以你必须单独获取。

qemu-system-misc 修复
此时此刻,似乎软件包 qemu-system-misc 收到了一个更新,该更新破坏了它与我们内核的兼容性。如果运行 make qemu 并且脚本在 qemu-system-riscv64 -machine virt -bios none -kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 之后出现挂起

则需要卸载该软件包并安装旧版本:

  $ sudo apt-get remove qemu-system-misc
  $ sudo apt-get install qemu-system-misc=1:4.2-3ubuntu6

在 Arch 上安装

sudo pacman -S riscv64-linux-gnu-binutils riscv64-linux-gnu-gcc riscv64-linux-gnu-gdb qemu-arch-extra

测试您的安装
若要测试安装,应能够检查以下内容:

$ riscv64-unknown-elf-gcc --version
riscv64-unknown-elf-gcc (GCC) 10.1.0
...

$ qemu-system-riscv64 --version
QEMU emulator version 5.1.0

您还应该能够编译并运行 xv6: 要退出 qemu,请键入:Ctrl-a x。

# in the xv6 directory
$ make qemu
# ... lots of output ...
init: starting sh
$

在本实验中,您将获得重新设计代码以提高并行性的经验。多核机器上并行性差的一个常见症状是频繁的锁争用。提高并行性通常涉及更改数据结构和锁定策略以减少争用。您将对xv6内存分配器和块缓存执行此操作。

切换分支执行操作

git stash
git fetch
git checkout mmap
make clean

mmap(hard)

mmap和munmap系统调用允许UNIX程序对其地址空间进行详细控制。它们可用于在进程之间共享内存,将文件映射到进程地址空间,并作为用户级页面错误方案的一部分,如本课程中讨论的垃圾收集算法。在本实验室中,您将把mmap和munmap添加到xv6中,重点关注内存映射文件(memory-mapped files)。

手册页面(运行man 2 mmap)显示了mmap的以下声明:

void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);

可以通过多种方式调用mmap,但本实验只需要与内存映射文件相关的功能子集。您可以假设addr始终为零,这意味着内核应该决定映射文件的虚拟地址。mmap返回该地址,如果失败则返回0xffffffffffffffff。length是要映射的字节数;它可能与文件的长度不同。prot指示内存是否应映射为可读、可写,以及/或者可执行的;您可以认为prot是PROT_READ或PROT_WRITE或两者兼有。flags要么是MAP_SHARED(映射内存的修改应写回文件),要么是MAP_PRIVATE(映射内存的修改不应写回文件)。您不必在flags中实现任何其他位。fd是要映射的文件的打开文件描述符。可以假定offset为零(它是要映射的文件的起点)。

允许进程映射同一个MAP_SHARED文件而不共享物理页面。

munmap(addr, length)应删除指定地址范围内的mmap映射。如果进程修改了内存并将其映射为MAP_SHARED,则应首先将修改写入文件。munmap调用可能只覆盖mmap区域的一部分,但您可以认为它取消映射的位置要么在区域起始位置,要么在区域结束位置,要么就是整个区域(但不会在区域中间“打洞”)。

int munmap(void *addr, size_t len);

您应该实现足够的mmap和munmap功能,以使mmaptest测试程序正常工作。如果mmaptest不会用到某个mmap的特性,则不需要实现该特性。

完成后,您应该会看到以下输出:

$ mmaptest
mmap_test starting
test mmap f
test mmap f: OK
test mmap private
test mmap private: OK
test mmap read-only
test mmap read-only: OK
test mmap read/write
test mmap read/write: OK
test mmap dirty
test mmap dirty: OK
test not-mapped unmap
test not-mapped unmap: OK
test mmap two files
test mmap two files: OK
mmap_test: ALL OK
fork_test starting
fork_test OK
mmaptest: all tests succeeded
$ usertests
usertests starting
...
ALL TESTS PASSED
$

提示:

  1. 首先,向UPROGS添加_mmaptest,以及mmap和munmap系统调用,以便让user/mmaptest.c进行编译。现在,只需从mmap和munmap返回错误。我们在kernel/fcntl.h中为您定义了PROT_READ等。运行mmaptest,它将在第一次mmap调用时失败。
  2. 惰性地填写页表,以响应页错误。也就是说,mmap不应该分配物理内存或读取文件。相反,在usertrap中(或由usertrap调用)的页面错误处理代码中执行此操作,就像在lazy page allocation实验中一样。惰性分配的原因是确保大文件的mmap是快速的,并且比物理内存大的文件的mmap是可能的。
  3. 跟踪mmap为每个进程映射的内容。定义与第15课中描述的VMA(虚拟内存区域)对应的结构体,记录mmap创建的虚拟内存范围的地址、长度、权限、文件等。由于xv6内核中没有内存分配器,因此可以声明一个固定大小的VMA数组,并根据需要从该数组进行分配。大小为16应该就足够了。
  4. 实现mmap:在进程的地址空间中找到一个未使用的区域来映射文件,并将VMA添加到进程的映射区域表中。VMA应该包含指向映射文件对应struct file的指针;mmap应该增加文件的引用计数,以便在文件关闭时结构体不会消失(提示:请参阅filedup)。运行mmaptest:第一次mmap应该成功,但是第一次访问被mmap的内存将导致页面错误并终止mmaptest。
  5. 添加代码以导致在mmap的区域中产生页面错误,从而分配一页物理内存,将4096字节的相关文件读入该页面,并将其映射到用户地址空间。使用readi读取文件,它接受一个偏移量参数,在该偏移处读取文件(但必须lock/unlock传递给readi的索引结点)。不要忘记在页面上正确设置权限。运行mmaptest;它应该到达第一个munmap。
  6. 实现munmap:找到地址范围的VMA并取消映射指定页面(提示:使用uvmunmap)。如果munmap删除了先前mmap的所有页面,它应该减少相应struct file的引用计数。如果未映射的页面已被修改,并且文件已映射到MAP_SHARED,请将页面写回该文件。查看filewrite以获得灵感。
  7. 理想情况下,您的实现将只写回程序实际修改的MAP_SHARED页面。RISC-V PTE中的脏位(D)表示是否已写入页面。但是,mmaptest不检查非脏页是否没有回写;因此,您可以不用看D位就写回页面。
  8. 修改exit将进程的已映射区域取消映射,就像调用了munmap一样。运行mmaptest;mmap_test应该通过,但可能不会通过fork_test。
  9. 修改fork以确保子对象具有与父对象相同的映射区域。不要忘记增加VMA的struct file的引用计数。在子进程的页面错误处理程序中,可以分配新的物理页面,而不是与父级共享页面。后者会更酷,但需要更多的实施工作。运行mmaptest;它应该通过mmap_test和fork_test。

运行usertests以确保一切正常。

解析

好综合的一道题,几乎把所有之前学到的知识都用上了!

首先还是老套路,在三小只添加mmap和munmap

//user/user.h
void *mmap(void*, int, int, int, int, int);
int munmap(void*, int);

//user/usys.pl
entry("mmap");
entry("munmap");

//makefile里面添加 symlinktest
。。。
        $U/_mmaptest\
。。。

内核中也需要添加

//kernel/syscall.h
#define SYS_mmap   22
#define SYS_munmap 23

//kernel/syscall.c
extern uint64 sys_mmap(void);
extern uint64 sys_munmap(void);

。。。
[SYS_mmap]    sys_mmap,
[SYS_munmap]   sys_munmap,

准备工作做完了,可以正式开始了。
首先按照提示3,定义VMA对应的结构体。

//kernel/proc.h
struct vm_area {
  int used;           // 是否已被使用
  uint64 addr;        // 起始地址
  int len;            // 长度
  int prot;           // 权限
  int flags;          // 标志位
  int vfd;            // 对应的文件描述符
  struct file* vfile; // 对应文件
  int offset;         // 文件偏移,本实验中一直为0
};

// Per-process state
struct proc {
  struct spinlock lock;
  struct vm_area vma[16];    // 虚拟内存区域
  // p->lock must be held when using these:
  enum procstate state;        // Process state
  struct proc *parent;         // Parent process
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  int xstate;                  // Exit status to be returned to parent's wait
  int pid;                     // Process ID

  // these are private to the process, so p->lock need not be held.
  uint64 kstack;               // Virtual address of kernel stack
  uint64 sz;                   // Size of process memory (bytes)
  pagetable_t pagetable;       // User page table
  struct trapframe *trapframe; // data page for trampoline.S
  struct context context;      // swtch() here to run process
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
};

然后我们需要将vma数组初始化为0(这步好像可不加)

//kernel/proc.c
static struct proc*
allocproc(void)
{
。。。

found:
。。。

  memset(&p->vma, 0, sizeof(p->vma));
  return p;
}

接下来是核心,实现sys_mmap()

  1. 首先进行参数的提取: 由于 xv6 中参数类型只有整数 int 类型, 因此 len, offset 参数此处都定义为 int 类型。
  2. 其次进行类型检查,先判断是否有小于0的参数,再按要求prot是PROT_READ或PROT_WRITE或两者兼有。flags要么是MAP_SHARED(映射内存的修改应写回文件),要么是MAP_PRIVATE(映射内存的修改不应写回文件)。addr和offset为0
  3. 分配之前先判断进程的地址空间有没有足够大小的虚拟空间
  4. 开始分配,首先要找到一个未使用的区域来映射文件
  5. 找到了就设置其使用标志,并初始化其属性
  6. 引用计数增加,当前进程的虚拟地址空间大小增加,返回新创建的虚拟内存区域的起始地址。
//kernel/sysfile.c
uint64
sys_mmap(void) {
  //首先进行参数的提取: 由于 xv6 中参数类型只有整数 int 类型, 因此 len, offset 参数此处都定义为 int 类型。
  uint64 addr;        // 起始地址
  int len;            // 长度
  int prot;           // 权限
  int flags;          // 标志位
  int vfd;            // 对应的文件描述符
  struct file* vfile; // 对应文件
  int offset;         // 文件偏移,本实验中一直为0
  uint64 err = 0xffffffffffffffff;  //失败则返回
  
  //其次进行类型检查,先判断是否有小于0的参数,再按要求prot是PROT_READ或PROT_WRITE或两者兼有。flags要么是MAP_SHARED(映射内存的修改应写回文件),要么是MAP_PRIVATE(映射内存的修改不应写回文件)。addr和offset为0
  // 获取系统调用参数
  if(argaddr(0, &addr) < 0 || argint(1, &len) < 0 || argint(2, &prot) < 0 ||
    argint(3, &flags) < 0 || argfd(4, &vfd, &vfile) < 0 || argint(5, &offset) < 0)
    return err;

  // 实验提示中假定addr和offset为0,简化程序可能发生的情况
  if(addr != 0 || offset != 0 || len< 0)
    return err;

  // 文件不可写则不允许拥有PROT_WRITE权限时映射为MAP_SHARED
  if(vfile->writable == 0 && (prot & PROT_WRITE) != 0 && flags != MAP_SHARED)
    return err;
    
  //分配之前先判断进程的地址空间有没有足够大小的虚拟空间
  //获取当前进程的指针
  struct proc* p = myproc();
  //映射长度加上当前进程的虚拟地址空间大小是否超出了最大虚拟地址空间 MAXVA
  if(p->sz + len > MAXVA)
    return err;
    
  //遍历查找未使用的VMA结构体
  for(int i = 0; i < 16; ++i) {
    if(p->vma[i].used == 0) {
      p->vma[i].used = 1;
      p->vma[i].addr = p->sz;
      p->vma[i].len = length;
      p->vma[i].flags = flags;
      p->vma[i].prot = prot;
      p->vma[i].vfile = vfile;
      p->vma[i].vfd = vfd;
      p->vma[i].offset = offset;

      // 增加文件的引用计数
      filedup(vfile);

      //更新当前进程的虚拟地址空间大小
      p->sz += length;
      return p->vma[i].addr;
    }
  }
  return err;
}

由于在 sys_mmap() 中对文件映射的内存采用的是懒分配,因此需要修改 kernel/trap.c 中 usertrap() 的代码。
我们可以回忆以下当时懒分配中实现的流程

  1. 捕捉异常,懒分配当时是13和15
  2. 首先通过遍历当前进程的 VMA 数组中找对应的 VMA 结构体,查找包含发生页面错误的虚拟地址属于哪块VMA
  3. 然后先进行页表项的权限设置和文件可访问性的检查
  4. 使用 kalloc 分配一个物理页面,并使用 memset 清零
  5. 使用 readi() 根据发生 page fault 的地址从文件的相应部分读取内容到分配的物理页
  6. 最后即可使用 mappages() 将物理页映射到用户进程的页面
//kernel/trap.c
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "spinlock.h"
#include "proc.h"
#include "defs.h"
#include "fcntl.h"
//这块如果还不好使就把file.h中的file结构体粘过来,他这个头文件很烦,也有可能是我电脑的问题
。。。
} else if((which_dev = devintr()) != 0){
    // ok
  } else if (r_scause() == 13 || r_scause() == 15) {
        if(mmap_handler(r_stval(), r_scause()) != 0) 
          p->killed = 1;
       else
        p->killed = 1;
  } else {
。。。
int mmap_handler(int va, int cause) {
  //首先通过遍历当前进程的 VMA 数组中找对应的 VMA 结构体,查找包含发生页面错误的虚拟地址
  int i;
  struct proc* p = myproc();
  // 根据地址查找属于哪一个VMA
  for(i = 0; i < 16; ++i) {
    if(p->vma[i].used && p->vma[i].addr <= va && va <= p->vma[i].addr + p->vma[i].len - 1) {
      break;
    }
  }
  if(i == 16)
    return -1;
  
  //然后先进行页表项的权限设置和文件可访问性的检查
  // 设置pte的标志位
  int pte_flags = PTE_U;
  if(p->vma[i].prot & PROT_READ) pte_flags |= PTE_R;
  if(p->vma[i].prot & PROT_WRITE) pte_flags |= PTE_W;
  if(p->vma[i].prot & PROT_EXEC) pte_flags |= PTE_X;
  struct file* vf = p->vma[i].vfile;
  // 读导致的页面错误
  if(cause == 13 && vf->readable == 0) return -1;
  // 写导致的页面错误
  if(cause == 15 && vf->writable == 0) return -1;
  
  //使用 kalloc 分配一个物理页面,并使用 memset 清零
  void* pa = kalloc();
  if(pa == 0)
    return -1;
  memset(pa, 0, PGSIZE);
  
  
  //读取文件内容
  ilock(vf->ip);
  // 计算当前页面读取文件的偏移量,实验中p->vma[i].offset总是0
  // 要按顺序读读取,例如内存页面A,B和文件块a,b
  // 则A读取a,B读取b,而不能A读取b,B读取a
  int offset = p->vma[i].offset + PGROUNDDOWN(va - p->vma[i].addr);
  int readbytes = readi(vf->ip, 0, (uint64)pa, offset, PGSIZE);
  // 什么都没有读到
  if(readbytes == 0) {
    iunlock(vf->ip);
    kfree(pa);
    return -1;
  }
  iunlock(vf->ip);

  //最后即可使用 mappages() 将物理页映射到用户进程的页面值
  if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)pa, pte_flags) != 0) {
    kfree(pa);
    return -1;
  }
  return 0;
}

//kernel/defs.h
// trap.c
extern uint     ticks;
void            trapinit(void);
void            trapinithart(void);
extern struct spinlock tickslock;
void            usertrapret(void);
int             mmap_handler(int, int);

然后目标是实现sys_munmap()
由于lab对munmap的情况做了简化,只会从头开始munmap,因此在将数据写回磁盘时,直接调用filewrite函数即可,它在内部会自动调用file的偏移

  1. 首先也是对参数的提取, munmap 只有 addr 和 length 两个参数
  2. 其次进行类型检查,二者都要大于0
  3. 接下来根据 addr 和 length ,遍历虚拟内存区域,找到包含要取消映射的地址范围的 VMA
  4. 如果找到的 VMA 的起始地址与 addr 相匹配,并且其长度大于等于 length,则将 VMA 的起始地址向后移动 length,并相应减少其长度;如果找到的 VMA 的结束地址与 addr + length 相匹配,也相应减少其长度。(这块真的是仁慈了。。。)
  5. 如果未映射的页面已被修改,并且文件已映射到MAP_SHARED, 将MAP_SHARED页面写回文件系统
  6. 最后调用 uvmunmap 函数取消页面映射并清理 VMA
//kernel/sysfile.c
uint64
sys_munmap(void) {
  //首先也是对参数的提取, munmap 只有 addr 和 length 两个参数
  uint64 addr;
  int length;
  
  //其次进行类型检查,二者都要大于0
  if(argaddr(0, &addr) < 0 || argint(1, &length) < 0)
    return -1;
    
  //接下来根据 addr 和 length ,遍历虚拟内存区域,找到包含要取消映射的地址范围的 VMA
    int i;
  struct proc* p = myproc();
  for(i = 0; i < 16; ++i) {
    if(p->vma[i].used && p->vma[i].len >= length) {
      // 根据提示,munmap的地址范围只能是
      // 1. 起始位置
      if(p->vma[i].addr == addr) {
        p->vma[i].addr += length;
        p->vma[i].len -= length;
        break;
      }
      // 2. 结束位置
      if(addr + length == p->vma[i].addr + p->vma[i].len) {
        p->vma[i].len -= length;
        break;
      }
    }
  }
  if(i == 16)
    return -1;
    
  // 如果未映射的页面已被修改,并且文件已映射到MAP_SHARED, 将MAP_SHARED页面写回文件系统
  if(p->vma[i].flags == MAP_SHARED && (p->vma[i].prot & PROT_WRITE) != 0) {
    filewrite(p->vma[i].vfile, addr, length);
  }
  
  // 取消页面映射
  uvmunmap(p->pagetable, addr, length / PGSIZE, 1);


  // 当前VMA中全部映射都被取消
  if(p->vma[i].len == 0) {
    fileclose(p->vma[i].vfile);
    p->vma[i].used = 0;
  }
  
  return 0;
}

懒分配中需要修改uvmunmap和uvmcopy检查PTE_V后不再panic

//kernel/vm.c
#include "fcntl.h"
//uvmunmap()
......
    if((*pte & PTE_V) == 0)
      //panic("uvmunmap: not mapped");
      continue;
......
//uvmcopy()
......
    if((*pte & PTE_V) == 0)
      //panic("uvmcopy: page not present");
      continue;
......

终于到最后了,根据提示修改exit将进程的已映射区域取消映射

//kernel/proc.c
......
  // 将进程的已映射区域取消映射
  for(int i = 0; i < 16; ++i) {
    if(p->vma[i].used) {
      if(p->vma[i].flags == MAP_SHARED && (p->vma[i].prot & PROT_WRITE) != 0) {
        filewrite(p->vma[i].vfile, p->vma[i].addr, p->vma[i].len);
      }
      fileclose(p->vma[i].vfile);
      uvmunmap(p->pagetable, p->vma[i].addr, p->vma[i].len / PGSIZE, 1);
      p->vma[i].used = 0;
    }
  }
......

最后修改fork,复制父进程的VMA并增加文件引用计数,以确保子对象具有与父对象相同的映射区域

//kernel/proc.c
......
  // 复制父进程的VMA
  for(i = 0; i < 16; ++i) {
    if(p->vma[i].used) {
      memmove(&np->vma[i], &p->vma[i], sizeof(p->vma[i]));
      filedup(p->vma[i].vfile);
    }
  }
......

感觉还是有点问题,解决了一堆头文件包含才勉强算是通过
在这里插入图片描述

总结

这个实验是真的全,从添加配置,到内存分配,还有锁的运用,最后包括了虚拟空间的映射。说实话难是真的难,做完了爽也是真的爽。感觉对整个的流程有了进一步的理解和运用,也同时感谢几位参考了的大佬,非常感谢前人栽树,我们这些后人也算是乘了凉。

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

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

相关文章

Re:从零开始的C++世界——(一)入门基础

文章目录 C发展历史1.命名空间1.1 namespace的价值1.2 namespace的定义1.3 命名空间使⽤ 2.C输⼊&输出3.缺省参数3.1 缺省参数的概念3.2 缺省参数的分类 4.函数重载5.引⽤5.1引⽤的概念和定义5.2 引⽤的特性5.3 const引⽤5.4 使用场景5.5 指针和引⽤的关系 6.内联函数6.1内…

RDNet实战:使用RDNet实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

从零开始做题:迷失幻境

题目 给出一个磁盘虚拟文件 解题 下载附件然后解压,得到一个虚拟机文件,使用的是DiskGenius磁盘工具打开 这样他里面的文件就全部展现出来了 我们可以看到有很多图片&#xff0c;和一个txt文档&#xff0c;还有几个没有后缀的文件&#xff0c;图片这么多&#xff0c;所以我…

自动驾驶中的人机互相接管问题讨论

一、背景 人机接管&#xff08;human takeover&#xff09;是指在自动驾驶过程中&#xff0c;当系统遇到超出其处理能力或预设安全阈值的情况时&#xff0c;将控制权交还给驾驶员的过程。这一环节的设计直接关系到自动驾驶技术的实用性与安全性&#xff0c;是目前研究和实践中…

idea启动ssm项目详细教程

前言 今天碰到一个ssm的上古项目&#xff0c;项目没有使用内置的tomcat作为服务器容器&#xff0c;这个时候就需要自己单独设置tomcat容器。这让我想起了我刚入行时被外置tomcat配置支配的恐惧。现在我打算记录一下配置的过程&#xff0c;希望对后面的小伙伴有所帮助吧。 要求…

计算机视觉之Vision Transformer图像分类

Vision Transformer&#xff08;ViT&#xff09;简介 自注意结构模型的发展&#xff0c;特别是Transformer模型的出现&#xff0c;极大推动了自然语言处理模型的发展。Transformers的计算效率和可扩展性使其能够训练具有超过100B参数的规模空前的模型。ViT是自然语言处理和计算…

第零章 HCIA复习

目录 HCIA复习 一、OSI七层模型 二、TCP/UDP协议 传输层协议 TCP/IP协议簇 封装/解封装 模型区别 协议号和类型字段 类型字段 协议号 UDP协议头部&#xff1a; TCP协议头部&#xff1a; IP报文参数&#xff1a; 三、DHCP协议 定义 PC端初次获取IP地址 交换机转…

Linux vim的使用(一键安装则好用的插件_forcpp),gcc的常见编译链接操作

vim 在Linux系统上vim是个功能还比较完善的软件。但是没装插件的vim用着还是挺难受的&#xff0c;所以我们直接上一款插件。 我们只需要在Linux上执行这个命令就能安装(bite提供的) curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh …

基于1bitDAC的MU-MIMO的非线性预编码算法matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 基于1-bit DAC的非线性预编码背景 4.2 ZF&#xff08;Zero-Forcing&#xff09; 4.3 WF&#xff08;Water-Filling&#xff09; 4.3 MRT&#xff08;Maximum Ratio Transmission&…

昇思25天学习打卡营第09天|保存与加载

在训练网络模型的过程中&#xff0c;实际上我们希望保存中间和最后的结果&#xff0c;用于微调&#xff08;fine-tune&#xff09;和后续的模型推理与部署&#xff0c;本章节我们将介绍如何保存与加载模型。 import numpy as np import mindspore from mindspore import nn fr…

【云岚到家】-day05-6-项目迁移-门户-CMS

【云岚到家】-day05-6-项目迁移-门户-CMS 4 项目迁移-门户4.1 迁移目标4.2 能力基础4.2.1 缓存方案设计与应用能力4.2.2 静态化技术应用能力 4.3 需求分析4.3.1 界面原型 4.4 系统设计4.4.1 表设计4.4.2 接口与方案4.4.2.1 首页信息查询接口4.4.3.1 数据缓存方案4.4.3.2 页面静…

C++相关概念和易错语法(20)(赋值兼容转换、多继承、继承与组合)

1.赋值兼容转换 赋值兼容转换有一点易混&#xff0c;先看一下下面的代码&#xff0c;想想a、b、c对象里面存的什么&#xff0c;顺便结合以前的知识&#xff0c;对继承加深理解。 #include <iostream> using namespace std;class A { protected:A(int a):_a(a){}int _a; …

PostgreSQL 中如何解决因频繁的小事务导致的性能下降?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中解决因频繁小事务导致性能下降的方法 PostgreSQL 中解决因频繁小事务导致性能下降的方法…

使用机器学习 最近邻算法(Nearest Neighbors)进行点云分析 (scikit-learn Open3D numpy)

使用 NearestNeighbors 进行点云分析 在数据分析和机器学习领域&#xff0c;最近邻算法&#xff08;Nearest Neighbors&#xff09;是一种常用的非参数方法。它广泛应用于分类、回归和聚类分析等任务。下面将介绍如何使用 scikit-learn 库中的 NearestNeighbors 类来进行点云数…

C++ | Leetcode C++题解之第233题数字1的个数

题目&#xff1a; 题解&#xff1a; class Solution { public:int countDigitOne(int n) {// mulk 表示 10^k// 在下面的代码中&#xff0c;可以发现 k 并没有被直接使用到&#xff08;都是使用 10^k&#xff09;// 但为了让代码看起来更加直观&#xff0c;这里保留了 klong l…

优化器算法

优化器算法 梯度下降算法 首先引用动手学深度学习中对梯度下降算法的直观理解与推导。说明了不断的迭代可能会使得f(x)的值不断下降&#xff0c;从直观上解释了梯度下降的可能性。 将损失函数在x点处一阶泰勒展开。 f ( x ϵ ) f ( x ) ϵ f ′ ( x ) O ( ϵ 2 ) . f(x\eps…

在InternStudio上创建一台GPU服务器

填写配置 创建完成 ssh连接&#xff0c;并测试常用指令 查看开发机信息 查看gpu信息 创建conda环境 跑个test

可重入锁深入学习(有码)

【摘要】 ​今天&#xff0c;梳理下java中的常用锁&#xff0c;但在搞清楚这些锁之前&#xff0c;先理解下 “临界区”。临界区在同步的程序设计中&#xff0c;临界区段活称为关键区块&#xff0c;指的是一个访问共享资源&#xff08;例如&#xff1a;共享设备或是共享存储器&a…

9. Python的魔法函数

Python中的魔法函数 在Python中魔法函数是在为类赋能&#xff0c;使得类能够有更多操作。通过重写类中的魔法函数&#xff0c;可以完成很多具体的任务 1. __str__ 通过str魔法函数&#xff0c;可以设置对类的实例的 print() 内容 2. __len__ 通过len魔法函数&#xff0c;可…

tessy 集成测试:小白入门指导手册

目录 1,创建集成测试模块且分析源文件 2,设置测试环境 3,TIE界面设置相关函数 4,SCE界面增加用例 5,编辑数据 6,用例所对应的测试函数序列 7,添加 work task 函数 8,为测试场景添加函数 9,为函数赋值 10,编辑时间序列的数值 11,执行用例 12,其他注意事项…