【Linux】进程(第九篇)

news2024/11/15 20:05:41

目录

1.进程概述

2.进程的内存布局

3.Linux和Windows空间对比

4.进程控制块(PCB)

5.进程的状态

6.进程的状态转换

7.进程源语

8.fork()


1.进程概述

进程是操作系统进行资源分配的最小单位,而内存是进程运行必不可少的资源。那么,为什么需要内存呢?CPU 太快,但其数据容量极小且功能单一,而其他 I/O 等硬件功能多样,只是相对于 CPU 来说它们又太慢,所以便需要内存在 CPU 与 I/O 设备间进行缓冲。

现代操作系统均支持多任务,操作系统一般会为每个进程分配 独享的内存空间,这个独享的内存空间只是在进程自己看来是独享的,实际上其只是操作系统为其分配的 虚拟内存空间,虚拟内存在真正被使用时才映射到物理内存上。进程每次访问自己内存空间的某个地址 (虚拟地址)时,操作系统都需要把地址翻译成实际物理内存地址。

虚拟内存是操作系统里的概念。由于数据存储的基本单位都是 Byte(字节),如果将每一个虚拟内存的 Byte 都对应到物理内存的地址,每个条目最少需要 8 字节(32 位虚拟地址 -> 32 位物理地址),如果在 4G 内存的情况下,就需要 32GB 的空间来存放虚拟内存地址与物理内存地址的对照表,那么这张表就大得真正的物理地址也放不下,于是操作系统引入了 页(Page) 的概念。

操作系统将整个物理内存划分为很多的页(页的大小一般为 4K,不同操作系统实现可能会不同),之后在给进程进行内存分配时,都以页为单位。这样虚拟内存页对应物理内存页的映射表就大大减小了,4G 内存时只需要 8M 的映射表即可。操作系统虚拟内存到物理内存的映射表,就被称为页表。

由于分页技术的广泛使用,现代的 CPU 设计中便多了一种硬件,即 内存管理单元 MMU(Memory Management Unit),专门用来将翻译虚拟内存地址。

Linux 的虚拟地址空间采用 “分段+分页” 结合的方式实现。分段是将内存划分成各个段落(Segment),每个段落的长度可以不同,且虚拟地址空间中未使用的空间不会映射到物理内存中,所以操作系统不会为这段空间分配物理内存。这样的话,内核为刚创建的进程分配的物理内存可以很小,随着进程运行不断使用内存,内核再为进程按需分配物理内存。也就是说,尽管地址空间的范围和物理内存大小一样,但不会将全部空间映射到物理内存。

2.进程的内存布局

在 32 操作系统中,其虚拟地址为 32 位长度,因此其虚拟地址空间的范围为 2 * 32 = 4GB。Linux 系统将地址空间按 3:1 比例划分,其中用户空间(user space)占 3GB,内核空间(kernel space)占 1GB。Linux 系统进程的虚拟内存地址空间布局如下图所示:

各个分段的含义:

  • 文本段(Text):也称为代码段。进程启动时会将程序的代码加载到物理内存中,文本段映射到这片物理内存。

  • 数据段(Data):包含程序显式初始化的全局变量和静态变量,即已初始化且初值不为0的全局变量(也包括静态全局变量)和静态局部变量,这些数据是在程序真正运行前就已经确定的数据,所以可以提前加载到内存保存好。

  • 未初始化数据(BSS):未初始化的全局变量和静态变量,这些变量的值是在程序真正运行起来并为其赋值后才能确定的,所以程序加载之初,只需要记录它的内存地址和所需大小。出于历史原因,这段空间也称为 BSS 段。

  • 栈(Stack):位于用户空间的顶部,是一个可以动态增长和收缩的内存段落,由栈帧(Stack Frames)组成,进程每调用一次函数,都将为该函数分配一个栈帧,栈帧中保存了该函数的局部变量、参数值和返回值。栈帧会在函数返回时被清理掉。注意,编译器会将函数参数放入寄存器来优化程序,只有寄存器放不下的参数才使用栈帧来保存。由于栈中数据严格的遵守 FIFO 的顺序,这个简单的设计意味着不必使用复杂的数据结构来追踪栈中的内容,只需要一个简单的指针指向栈的顶端即可,因此压栈(pushing)和出栈(popping)过程非常迅速、准确。

  • 堆(Heap):与栈一样,堆用于运行时内存分配;但不同的是,堆用于存储那些生存期与函数调用无关的数据。如用系统调用 malloc 申请的内存便在堆上,这些申请的内存在不需要时必须手动释放,否则便会出现内存泄漏。

    栈的内存地址向下增长,堆得内存地址向上增长。

  • 内存映射段(Memory Mapping):在栈与堆之间,有一个内存映射端。内核通过这一段将文件的内容直接映射到内存,进程可以通过 mmap 系统调用请求这种映射。内存映射是一种方便高效的文件 I/O 方式,所以它也被用来加载动态库。

  • 内核段(Kernel):这部分是操作系统内核运行时所占用内存在各进程虚拟地址空间中的映射。所有进程都有,且映射地址相同,因为都映射到内核使用的内存。这段内存只有内核能访问,用户进程无法访问到该段落。

3.Linux和Windows空间对比

对于Windows系统,理论上是16TB

对于Linux 64位系统,理论上,64bit内存地址可用空间为0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF(16位十六进制数),这是个相当庞大的空间,Linux实际上只用了其中一小部分(256T)。

Linux64位操作系统仅使用低47位,高17位做扩展(只能是全0或全1)。所以,实际用到的地址为空间为0x0000000000000000 ~ 0x00007FFFFFFFFFFF(user space)和0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF(kernel space),其余的都是unused space。

user space 也就是用户区由以下几部分组成:代码段,数据段,BSS段,heap,stack

4.进程控制块(PCB)

每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息。Linux内核的进程控制块是task_struct结构体。task_struct是Linux内核的一种数据结构,它会被装载到RAM并且包含着进程的信息。每个进程都把它的信息放在task_struct这个数据结构里,task_struct包含了许多内容: 标示符:描述本进程的唯一标示符,用来区别其他进程。

状态:任务状态,退出代码,退出信号等。

优先级:相对于其他进程的优先级。

程序计数器:程序中即将被执行的下一条指令的地址。

内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。

上下文环境:进程执行时处理器的寄存器中的数据。

I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

记账信息:可能包括处理器时间总和,使用的时钟总和,时间限制,记账号等。

保存在进程信息的数据结构叫做task_struct,并且可以在include/linux/sched.h里找到它。所有运行在系统里的 进程都以task_struct链表的形式存在内核里。

struct task_struct  
{  
    volatile long state; //说明了该进程是否可以执行,还是可中断等信息  
    unsigned long flags; // flags 是进程号,在调用fork()时给出  
    int sigpending; // 进程上是否有待处理的信号  
  
     mm_segment_t addr_limit;  //进程地址空间,区分内核进程与普通进程在内存存放的位置不同  //0-0xBFFFFFFF for user-thead    //0-0xFFFFFFFF for kernel-thread  
     //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度  
     volatile long need_resched;  
     int lock_depth;    //锁深度  
     long nice;       //进程的基本时间片  
  
     //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER  
     unsigned long policy;  
     struct mm_struct *mm;    //进程内存管理信息  
  
     int processor;  
     //若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新  
     unsigned long cpus_runnable, cpus_allowed;  
     struct list_head run_list;   //指向运行队列的指针  
     unsigned long sleep_time;   //进程的睡眠时间  
  
     //用于将系统中所有的进程连成一个双向循环链表, 其根是init_task  
     struct task_struct *next_task, *prev_task;  
     struct mm_struct *active_mm;  
     struct list_head local_pages;      //指向本地页面        
     unsigned int allocation_order, nr_local_pages;  
     struct linux_binfmt *binfmt;      //进程所运行的可执行文件的格式  
     int exit_code, exit_signal;  
     int pdeath_signal;           //父进程终止时向子进程发送的信号  
     unsigned long personality;  
     //Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序  
     int did_exec:1;   
     pid_t pid;          //进程标识符,用来代表一个进程  
     pid_t pgrp;        //进程组标识,表示进程所属的进程组  
     pid_t tty_old_pgrp;      //进程控制终端所在的组标识  
     pid_t session;             //进程的会话标识  
     pid_t tgid;  
     int leader;    //表示进程是否为会话主管  
     struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;  
     struct list_head thread_group;          //线程链表  
     struct task_struct *pidhash_next;    //用于将进程链入HASH表  
     struct task_struct **pidhash_pprev;  
     wait_queue_head_t wait_chldexit;      //供wait4()使用  
     struct completion *vfork_done;         //供vfork() 使用  
  
  
     unsigned long rt_priority;       //实时优先级,用它计算实时进程调度时的weight值  
  
  
     //it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value  
  
     //设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据  
  
     //it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。  
  
     //当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送  
  
     //信号SIGPROF,并根据it_prof_incr重置时间.  
     //it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种  
  
     //状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据  
  
     //it_virt_incr重置初值。  
  
     unsigned long it_real_value, it_prof_value, it_virt_value;  
     unsigned long it_real_incr, it_prof_incr, it_virt_value;  
     struct timer_list real_timer;        //指向实时定时器的指针  
     struct tms times;                      //记录进程消耗的时间  
     unsigned long start_time;          //进程创建的时间  
  
     //记录进程在每个CPU上所消耗的用户态时间和核心态时间  
     long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];   
  
  
     //内存缺页和交换信息:  
  
     //min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换  
  
     //设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。  
     //cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。  
  
     //在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中  
     unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;  
     int swappable:1; //表示进程的虚拟地址空间是否允许换出  
     //进程认证信息  
     //uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid  
  
     //euid,egid为有效uid,gid  
     //fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件  
  
     //系统的访问权限时使用他们。  
     //suid,sgid为备份uid,gid  
     uid_t uid,euid,suid,fsuid;  
     gid_t gid,egid,sgid,fsgid;  
     int ngroups;                  //记录进程在多少个用户组中  
     gid_t groups[NGROUPS];      //记录进程所在的组  
  
     //进程的权能,分别是有效位集合,继承位集合,允许位集合  
     kernel_cap_t cap_effective, cap_inheritable, cap_permitted;  
  
     int keep_capabilities:1;  
     struct user_struct *user;  
     struct rlimit rlim[RLIM_NLIMITS];    //与进程相关的资源限制信息  
     unsigned short used_math;         //是否使用FPU  
     char comm[16];                      //进程正在运行的可执行文件名  
     int link_count, total_link_ count;  //文件系统信息  
  
     //NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空  
     struct tty_struct *tty;  
     unsigned int locks;  
     //进程间通信信息  
     struct sem_undo *semundo;       //进程在信号灯上的所有undo操作  
     struct sem_queue *semsleeping;   //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作  
     //进程的CPU状态,切换时,要保存到停止进程的task_struct中  
     struct thread_struct thread;  
     struct fs_struct *fs;           //文件系统信息  
     struct files_struct *files;    //打开文件信息  
     spinlock_t sigmask_lock;   //信号处理函数  
     struct signal_struct *sig;   //信号处理函数  
     sigset_t blocked;                //进程当前要阻塞的信号,每个信号对应一位  
     struct sigpending pending;      //进程上是否有待处理的信号  
     unsigned long sas_ss_sp;  
     size_t sas_ss_size;  
     int (*notifier)(void *priv);  
     void *notifier_data;  
     sigset_t *notifier_mask;  
     u32 parent_exec_id;  
     u32 self_exec_id;  
  
     spinlock_t alloc_lock;  
     void *journal_info;  
};  

5.进程的状态

运行态R(TASK_RUNNING) 进程获取到CPU使用权并得到响应的资源,正在执行系统任务 就绪态A 进程准备就绪,等待CPU使用权与资源 睡眠态S (TASK_INTERRUPTIBLE) 可中断的睡眠状态。

D (TASK_UNINTERRUPTIBLE) 不可中断的睡眠状态。

【睡眠态S 可以被中断 无法继续(可以被叫起来)D 不可被中断(不能被叫起来)】

暂停状态或跟踪状态 T (TASK_STOPPED or TASK_TRACED) 向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态(除非该进程本身处于TASK_UNINTERRUPTIBLE状态而不响应信号)。(SIGSTOP与SIGKILL信号一样,是非常强制的。不允许用户进程通过signal系列的系统调用重新设置对应的信号处理函数。) 向进程发送一个SIGCONT信号,可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态。

当进程正在被跟踪时,它处于TASK_TRACED这个特殊的状态。“正在被跟踪”指的是进程暂停下来,等待跟踪它的进程对它进行操作。比如在gdb中对被跟踪的进程下一个断点,进程在断点处停下来的时候就处于TASK_TRACED状态。而在其他时候,被跟踪的进程还是处于前面提到的那些状态。

退出状态X(TASK_DEAD - EXIT_DEAD) 进程即将被销毁。进程在退出过程中也可能不会保留它的task_struct

退出状态Z (TASK_DEAD - EXIT_ZOMBIE) 进程成为僵尸进程。

进程在退出的过程中,处于TASK_DEAD状态。

在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸。

< 高优先级(not nice to other users)

N 低优先级(nice to other users)

L 页面锁定在内存(实时和定制的IO)

s 一个信息头

l 多线程(使用 CLONE_THREAD,像NPTL的pthreads的那样)

+在前台进程组

其中这个参数来列出所有的信息以提供自己检查程序的问题!在上面的程序列出当中,说明如下:

USER:说明该程序是属于哪一个人的;

PID:该程序的代号;

%CPU:代表该程序使用了多少 CPU 资源;

%MEM:代表该程序使用了多少的 RAM ;

VSZ, RSS:占去的 ram 的大小( bytes );

TTY:是否为登入者执行的程序?若为 tty1-tty6 则为本机登入者,若为 pts/?? 则为远程登入者执行的程序

STAT:该程序的状态

START:该程序开始的日期;

TIME:该程序运行的时间?

COMMAND:该程序的内容

6.进程的状态转换

(1) 就绪→执行 处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。

(2) 执行→就绪 处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。

(3) 执行→阻塞 正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。

(4) 阻塞→就绪 处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。

7.进程源语

fork() #进程创建
exec()进程功能重载,
wait()  #进程回收
waitpid() #进程回收,wait函数的升级版

Linux操作系统,强调进程间的亲缘关系,所有的进程都有其父进程

父进程 -fork-> 子进程

Linux进程体系,所有的进程追溯其父进程,会追溯到pid=init进程,init进程是一切的起始

查看Linux父进程的方法

pstree-显示子进程的父进程

# -a 表示输出命令行选项
# p表PID
# s表示指定进程的父进程
pstree -aps 3080 # 3080 是子进程号
systemd,1
  └─dockerd,15006 -H fd://
      └─docker-containe,15024 --config /var/run/docker/containerd/containerd.toml
          └─docker-containe,2991 -namespace moby -workdir...
              └─app,3009
                  └─(app,2080)

在这个结构中,如果有子进程,必然是父进程创建出来的 调用fork为父进程,被fork创建的进程为子进程 调用一次fork 创建一个子进程

#include <stdio.h>
#include <unistd.h>
​
int main()
{
    printf("initing \n");
    pid_t pid = fork();#pid_t  进程ID
   if (pid >0)
    {
        /* code */
        printf("father process...\n");
         while(1) sleep(10);
    }
  
    
    return 0;
}

继承:默认情况下父进程的工作以及数据会继承给子进程,子进程直接使用

创建子进程流程:

ps aux 查看进程信息

fork 的核心任务:

1.create(进程创建)--------------> 创建一块虚拟地址空间(一个空壳)

2.clone(克隆,进程初始化)------->1.pcb 2.将父进程的资源继承拷贝给子进程

注意事项:

内核:PCB 是部分拷贝(pid 不拷贝),可以继承的拷贝给子进程一份,其他的重新生成

用户空间:完成拷贝

子进程将父进程的空间拷贝一份,无论如何修改资源与父进程无关

父子进程任务执行,父进程从代码段起始位置开始执行,子进程是从fork()之后的代码段开始执行的 区分父子进程工作区:利用fork返回pid区别父子进程。父进程中返回子进程pid(>0) ,子进程中返回0,失败返回-1

区分父子进程

示例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    printf("initing...\n");
    pid_t pid = fork();
    if(pid >0)
    {
        printf("parent process \n");
        while(1) sleep(10);
    }else if(pid ==0)
    {
        printf("child process \n");
        while(1) sleep(10);
    }else
    {
        perror("fork call failed");
        exit(0);
    }
    
}

创建多进程模型(一父多子):

示例:

 #include <stdio.h>
 #include <unistd.h>
 
 int main()
 {
 
      pid_t pid;
      int i;
      for( i =0 ;i <10;i++)
      {
            pid = fork();
            if(pid == 0) //如果不加pid ==0 循环会创建多少个子进程呢?
                  break;
      }
 }
​
8.fork()

fork函数的版本变化:

version 1:第一版的fork,开发者调用fork后,内核创建子进程,并将父进程资源拷贝给子进程,子进程继承所有父进程资源与任务。(内核空间部分拷贝)

问题:在某些特殊开发环境下和特殊要求下,继承是一个缺点。(比如继承父业,父进程通过内核将大部份资源继拷贝给子进程,但是子进程没有使用,导致内核克隆拷贝没有意义,造成资源浪费)

version 2:第二版vfork,开发者调vfork后,内核创建子进程,但是不会发生继承过程,不会将任何父进程资源拷贝给子进程,子进程需要重载自定义任务

问题:如果子进程需要继承资源,那么vfork()就不可用(比如 一份财产拿不到)

官方提供这两个版本,开发者根据需要自行选择

当fork 读时共享,写时拷贝技术 vfork已经逐渐废弃了

实际上,准确的来说,Linux的fork 是通过 写时拷贝 (copy-on-write)实现。写时拷贝是一种可以推迟甚至不用避免拷贝的技术。更具体来讲,在执行fork语句后,内核并不复制父进程的整个地址空间,而是父子进程共享父进程的地址空间(此时父子进程对于地址空间是只读指令),在父进程或者子进程进行写指令时,子进程才会复制一份地址空间,从而使得父子进程拥有自己的虚拟地址空间,在自己的地址空间进行写操作。也就是说,资源的复制是在需要写入时才会进行,在此之前,只会以只读方式进行共享。

对于文件资源,fork之后的父子进程共享文件,fork之后的父进程与子进程的文件描述符表指向相同的文件表,引用计数增加,共享文件偏移指针。

version 3:最终版fork,开发者调用fork后,内核创建子进程,而后使用读时共享写时复制的方式,解决继承困境。

读共享采用映射技术,将父进程的用户空间映射给子进程,子进程通过映射可以读访问父进程的资源,写的时候采用clone技术

示例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void joba()
{
   printf("joba....\n");
}
void jobb()
{
   printf("jobb....\n");
}
int main()
{
​
     pid_t pid;
     int i;
     for( i =0 ;i <2;i++)
     {
           pid = fork();
           if(pid == 0)
                 break;
     }
     if(pid > 0)
     {
          printf("parent process\n");
          while(1) sleep(1);
     }
     else if(pid ==0)
     {
            if(i)
             {
                   joba();
                   exit(0);
             }
             else
             {
                    jobb();
                    exit(0);
             }
     }
​
    return 0;
}
                         

多进程开发的注意事项:关于子进程工作范围 子进程不应该踏出自己的工作区域,工作区代码执行完毕立即结束

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

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

相关文章

鱼哥好书分享活动第30期:一本书看完教你学习如何做B端竞品分析?《有效竞品分享》

鱼哥好书分享活动第30期&#xff1a;一本书看完教你学习如何做B端竞品分析&#xff1f;《有效竞品分享》 01 明确目标&#xff1a;案例分享&#xff1a;案例背景&#xff1a; 02 选择竞品&#xff1a;2.1 竞品的分类2.2 如何找到B端的竞品&#xff1f;1.找售前/销售沟通。2.各个…

【自由能系列(中级),代码模拟】预测编码的核心:三个关键方程式的详解

预测编码的核心&#xff1a;三个关键方程式的详解 ——探索预测编码背后的数学原理与应用 核心结论&#xff1a;预测编码是一种基于贝叶斯定理的理论框架&#xff0c;它通过三个关键方程式描述了大脑如何处理和解释来自环境的信号。这些方程式分别建立了贝叶斯定理的简化形式、…

【机器学习】基扩展的基本概念以及其中的多项式回归、样条方法和广义可加模型的简单介绍(含python代码实例)

引言 基扩展是提升模型性能的重要工具&#xff0c;正确选择和应用基扩展方法可以显著提高模型的预测能力和解释性 文章目录 引言一、基扩展1.1 基扩展定义1.2 基扩展方法1.2.1 多项式基扩展1.2.2 样条基扩展1.2.3 径向基函数&#xff08;RBF&#xff09;1.2.4 傅里叶基扩展1.2.…

强制结束输入的方法

如果scanf要求输入两个数&#xff0c;现在只输入一个数&#xff0c;想要结束输入该怎么办呢&#xff1f; 此时如果按空格、TAB和Enter键都是无法结束输入的。 之前在“用恋爱脑搞懂scanf的返回值”一文中讲过&#xff0c; Windows的输入结束信号是Ctrl&#xff0b;Z&#xff…

ParallelsDesktop19可在任何Mac上运行Windows软件

ParallelsDesktop19是一款Mac虚拟机软件&#xff0c;可在任何Mac上运行Windows&#xff0c;体验不同操作系统之间的无缝集成&#xff0c;并具有创新设计和增强功能&#xff0c;如无密码登录与TouchID、支持macOSSonoma14和增强打印选项。此外&#xff0c;它还支持运行更多Windo…

day40(8/30)——使用docker部署project-exam-system

一、回顾 1、使用harbao仓库 1. Python -- version 2. yum -y update 3. yum -y install python2-pip 4. pip install --upgrade pip20.3 -i https://mirrors.aliyun.com/pypi/simple 5. pip install docker-compose -i https://mirrors.aliyun.com/pypi/simple 6. sou…

安宝特科技 | AR眼镜在安保与安防领域的创新应用及前景

随着科技的不断进步&#xff0c;增强现实&#xff08;AR&#xff09;技术逐渐在多个领域展现出其独特的优势&#xff0c;尤其是在安保和安防方面。AR眼镜凭借其先进的功能&#xff0c;在机场、车站、海关、港口、工厂、园区、消防局和警察局等行业中为安保人员提供了更为高效、…

KinectFusion

1.KinectFusion 笔记来源&#xff1a; 论文地址&#xff1a;KinectFusion: Real-time 3D Reconstruction and Interaction Using a Moving Depth Camera* 项目地址&#xff1a;github/KinectFusion [1] 截断符号距离 | TSDF, Truncated Signed Distance Function 本篇对Kinec…

零跑C11 S01 T03 路特斯EMEYA繁花ELETRE启辰D60EVPLUS维修手册电路图资料更新

经过整理&#xff0c;零跑C11 S01 T03 路特斯EMEYA繁花ELETRE启辰D60EVPLUS已经更新至汽修帮手资料库内&#xff0c;覆盖市面上99%车型&#xff0c;包括维修手册、电路图、新车特征、车身钣金维修数据、全车拆装、扭力、发动机大修、发动机正时、保养、电路图、针脚定义、模块传…

tomcat redis minio nginx windows开机自启

tomcat 开机自启 命令 service.bat install 控制台输入 service.bat install 再到服务中去查看有没有注册成功&#xff0c;minio,redis,nginx 也是一样在服务里查看注册成功没 redis 开机自启 命令 redis-server.exe --service-install redis.windows.conf --loglevel ve…

CT转化MR图像的算法及模型解决

将CT&#xff08;Computed Tomography&#xff09;图像转化为MR&#xff08;Magnetic Resonance&#xff09;图像是一个复杂的图像处理任务&#xff0c;因为CT和MR图像是基于完全不同的物理原理获取的。CT图像主要反映组织的密度差异&#xff0c;而MR图像则反映组织的质子密度、…

python 遍历文件夹中的文件

上代码&#xff1a; import os# 设置要遍历的文件夹路径 folder_path utils# 遍历文件夹 for dirname, subdirs, files in os.walk(folder_path):print(fFound directory: {dirname})for file in files:print(f{os.path.join(dirname, file)} is a file)# 如果需要遍历子文件…

探索存储世界:TF卡与SD卡的奥秘

在这个数字化时代&#xff0c;数据存储变得至关重要。TF卡&#xff08;TransFlash卡&#xff09;和SD卡&#xff08;Secure Digital卡&#xff09;作为两种常见的存储介质&#xff0c;它们在我们的日常生活中扮演着重要角色。MK米客方德将带您深入了解TF卡的基本概念&#xff0…

netcore高级知识点,内存对齐,原理与示例

最近几年一直从事物联网开发&#xff0c;与硬件打交道越来越多&#xff0c;发现越接近底层开发对性能的追求越高&#xff0c;毕竟硬件资源相对上层应用来实在是太缺乏了。今天想和大家一起分享关于C#中的内存对齐&#xff0c;希望通过理解和优化内存对齐&#xff0c;可以帮助大…

深度学习模型量化方法

深度学习模型量化方法 (qq.com)

【hot100篇-python刷题记录】【回文链表】

R7-链表篇 思路&#xff1a; 转回文数组法 链表转数组&#xff0c;再使用双指针判断是不是回文数组即可。 wkao?!根本不用双指针判断是否回文数组&#xff0c;只需要倒序判断布尔值即可。&#xff08;牛啊牛啊&#xff09; # Definition for singly-linked list. # class…

LIN总线CAPL函数—— 更新特定报文数据(linUpdateResponse)

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

激光测距模组光轴调试怎么调

激光测距模组作为精密的测量工具&#xff0c;其光轴调试是确保测量精度的重要环节。正确调试光轴不仅能够提升测量的准确性&#xff0c;还能延长设备的使用寿命。以下将由鑫优威给大家详细介绍激光测距模组光轴调试的步骤及注意事项。 一、准备工作 在进行光轴调试前&#xff0…

[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-24 TPG图像测试数据发生器设计

软件版本&#xff1a;VIVADO2021.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA 实验平台&#xff1a;米联客-MLK-H3-CZ08-7100开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 http…

非标机械设计项目“规范”笔记

2.自动化设备开发特点与技术文件输出 2.1自动化设备 自动化设备 工业自动化设备类型&#xff1a;标准自动化、非标自动化 载具和治具 焊接治具---汽车行业用的多 压装、压合治具---3C行业 治具种类&#xff1a; 电木&#xff1a;测试治具箱体&#xf…