Linux多进程和多线程(一)-进程的概念和创建

news2024/11/25 23:40:42
  • 进程
    • 进程的概念
    • 进程的特点如下
    • 进程和程序的区别
    • LINUX进程管理
      • getpid()
      • getppid()
    • 进程的地址空间
    • 虚拟地址和物理地址
    • 进程状态管理
    • 进程相关命令
      • ps
    • top
    • pstree
    • kill
  • 进程的创建
    • 并发和并行
    • fork()
      • 父子进程执行不同的任务
      • 创建多个进程
  • 进程的退出
    • exit()和_exit()
      • exit()函数让当前进程退出,并且刷新缓冲区
      • _exit()
  • 进程的等待
    • wait()函数 和 waitpid()函数
      • wait()函数
        • 创建⼀个⼦进程, 延时 3s 后退出, ⽗进程等待⼦进程退出
      • waitpid()函数
        • 函数原型
        • 函数参数
  • 进程的替换
    • 在 Linux 系统中提供了⼀组⽤于进程替换的替换,共有 6 个函数
    • 示例:通过 execl 函数族执⾏ ls -l 命令

进程

进程的概念

进程(Process)是操作系统对一个正在运行的程序的一种抽象。它是系统运行程序的最小单位,是资源分配和调度的基本单位。

进程的特点如下

  1. 进程是⼀个独⽴的可调度的活动, 由操作系统进⾏统⼀调度, 相应的任务会被调度到cpu 中进⾏执⾏
    在这里插入图片描述

  2. 进程⼀旦产⽣,则需要分配相关资源,同时进程是资源分配的最⼩单位
    在这里插入图片描述

进程和程序的区别

  • 程序是静态的,它是⼀些保存在磁盘上的指令的有序集合,没有任何执⾏的概念
  • 进程是⼀个动态的概念,它是程序执⾏的过程,包括了动态创建、调度和消亡的整个过程
  • 并⾏执⾏ : 表示多个任务能够同时执⾏,依赖于物理的⽀持,⽐如 cpu是4核⼼,则可以同时执⾏4个任务
  • 并发执⾏ : 在同⼀时间段有多个任务在同时执⾏,由操作系统调度算法来实现,⽐较典型的就是时间⽚轮转
    在这里插入图片描述

LINUX进程管理

在 Linux 系统中管理进程使⽤树型管理⽅式

  • 每个进程都需要与其他某⼀个进程建⽴ ⽗⼦关系, 对应的进程则叫做 ⽗进程
  • Linux 系统会为每个进程分配 id , 这个 id 作为当前进程的唯⼀标识, 当进程结束, 则会回收

进程的 id 与 ⽗进程的 id 分别可以通过 getpid() 与 getppid() 来获取

getpid()

#include <unistd.h>

pid_t getpid(void);

该函数用来获取当前进程的 id

getppid()

#include <unistd.h>

pid_t getppid(void);

该函数用来获取当前进程的⽗进程的 id

进程的地址空间

  • ⼀旦进程建⽴之后, 系统则要为这个进程分配相应的资源, ⼀般系统会为每个进程分配 4G 的地址空间

  • 在这里插入图片描述

  • 4G 的进程地址空间主要分为两部分:
    0 - 3G : ⽤户空间
    3G - 4G : 内核空间

  • ⽤户空间⼜具体分为如下区间

stack : 存放⾮静态的局部变量

heap : 动态申请的内存

.bss : 未初始化过的全局变量(包括初始化为 0 的, 未初始化过的静态变量 (包括初始化为 0)

.data : 初始化过并且值不为 0 的全局变量, 初始化过的不为 0 静态变量

.rodata : 只读变量(字符串之类)

.text : 程序⽂本段(包括函数,符号常量)

  • 当⽤户进程需要通过内核获取资源时, 会切换到内核态运⾏, 这时当前进程会使⽤内核空间的资源⽤户需要切换到内核态运⾏时, 主要是通过 系统调⽤

虚拟地址和物理地址

  • 虚拟地址 : 程序运行时使用的地址, 由操作系统管理, 程序只能通过虚拟地址访问内存
  • 物理地址 : 实际物理内存中存储数据的地址, 由硬件管理, 程序只能通过物理地址访问内存
  • 虚拟地址和物理地址的转换关系由操作系统完成, 程序只能通过虚拟地址访问内存
  • 虚拟地址空间和物理地址空间的映射关系由操作系统完成, 程序只能通过虚拟地址访问内存

在 cpu 中有⼀个硬件 MMU(内存管理单元) , 负责虚拟地址与物理地址的映射管理以
及虚拟地址访问
操作系统可以设置 MMU 中的映射内存段

在这里插入图片描述

在操作系统中使⽤虚拟地址空间主要是基于以下原因:
直接访问物理地址, 会导致地址空间没有隔离, 很容易导致数据被修改
通过虚拟地址空间可以实现每个进程地址空间都是独⽴的,操作系统会映射到不⽤的
物理地址区间,在访问时互不⼲扰.

进程状态管理

进程是动态过程,操作系统内核在管理整个动态过程时会使⽤了
状态机,

  • 给不同时间节点设计⼀个状态,通过状态来确定当前的过程进度
  • 在管理动态过程时,使⽤状态机是⼀种⾮常好的⽅式

进程的状态⼀般分为如下

  • 运⾏态 (TASK_RUNNING) : 此时进程或者正在运⾏,或者准备运⾏, 就绪或者正在进⾏都属于运⾏态
  • 睡眠态 () : 此时进程在等待⼀个事件的发⽣或某种系统资源
    • 可中断的睡眠 (TASK_INTERRUPT) : 可以被信号唤醒或者等待事件或者资源就绪
    • 不可中断的睡眠 (TASK_UNTERRUPT) : 只能等待特定的事件或者资源就绪
  • 停⽌态 (TASK_STOPPED) : 进程暂停接受某种处理。例如:gdb 调试断点信息处理。
  • 僵死态(TASK_ZOMBIE):进程已经结束但是还没有释放进程资源

在这里插入图片描述

进程相关命令

ps

参数:

  • -e 显示所有进程
  • -f 显示进程详细信息
  • -l 显示进程详细信息,包括线程信息
  • -u 显示指定用户的进程
  • -aux 显示所有进程,包括其他用户的进程
ps -aux
ps -ef | grep " 进程名 "  # 查找进程

top

实时显示系统中进程的运行状态

top [-] [d delay] [q] [c] [S] [s] [i] [n] [b]

选项:

  • d : 改变显示的更新速度,或是在交谈式指令列 (interactive command) 按 s
  • q : 没有任何延迟的显示速度,如果使⽤者是有 superuser 的权限,则 top 将会以最⾼的优先序执⾏
  • c : 切换显示模式,共有两种模式,⼀是只显示执⾏档的名称,另⼀种是显示完整的路径与名称
  • S : 累积模式,会将⼰完成或消失的⼦进程 (dead child process) 的 CPU time 累积起来
  • s : 安全模式,将交谈式指令取消, 避免潜在的危机
  • i : 不显示任何闲置 (idle) 或⽆⽤ (zombie) 的进程
  • n : 更新的次数,完成后将会退出 top
  • b : 批次档模式,搭配 “n” 参数⼀起使⽤,可以⽤来将 top 的结果输出到档案内

在这里插入图片描述

top - 14:34:29 up 7 days, 18:51, 1 user, load average: 1.00, 0.95, 0.61

  • top:名称
  • 14:34:29 :系统当前时间
  • up 7 days, 14:30:系统以及运⾏的时间,和 uptime 命令相等
  • 1 users:当前有 1 个⽤户在线
  • load average: 1.00, 0.95, 0.61:系统负载,即任务队列的平均⻓度。 三个数值分别为 1 分钟、5 分钟、15 分钟前到现在的平均值。

Tasks: 290 total, 2 running, 287 sleeping, 0 stopped, 1 zombie

  • Tasks:任务,也就是进程
  • 290 total:当前总共有 290 个任务,也就是 290 个进程
  • 2 running:2 个进程正在运⾏
  • 287 sleeping:263 个进程正在休眠
  • 0 stopped:0 个停⽌的进程
  • 1 zombie:1 个僵⼫进程

%Cpu(s): 51.0 us, 0.7 sy, 0.0 ni, 47.8 id, 0.0 wa, 0.0 hi, 0.5 si, 0.0 st

  • %Cpu(s):CPU 使⽤率
  • 51.0 us:⽤户空间占⽤ CPU 时间的百分⽐(⼤部分进程都运⾏在⽤户态,通常都是希望⽤户空间 CPU 越⾼越好)
  • 0.7 sy:内核空间占⽤ CPU 时间的百分⽐(Linux 内核态占⽤的 CPU 时间,系统 CPU 占⽤越⾼,表明系统某部分存在瓶颈。通常这个值越低越好)
  • 0.0 ni:占⽤ CPU 时间的百分⽐(ni 是 nice 的缩写,进程⽤户态的优先级,如果调整过优先级,那么展示的就是调整过 nice 值的进程消耗掉的 CPU 时间,如果系统中没有进程被调整过 nice 值,那么 ni 就显示为 0)
  • 47.8 id:空闲 CPU 占⽤率,等待进程运⾏
  • 0.0 wa:等待输⼊输出的 CPU 时间百分⽐(CPU 的处理速度是很快的,磁盘 IO 操作是⾮常慢的。wa 表示 CPU 在等待 IO 操作完成所花费的时间。系统不应该花费⼤量的时间来等待 IO操作,否则就说明 IO 存在瓶颈)
  • 0.0 hi:CPU 硬中断时间百分⽐(硬中断是硬盘、⽹卡等硬件设备发送给 CPU 的中断消息 )
  • 0.5 si:CPU 软中断时间百分⽐(软中断是由程序发出的中断 )
  • 0.0 st:被强制等待(involuntary wait)虚拟 CPU 的时间,此时 Hypervisor 在为另⼀个虚拟处理器服务。

MiB Mem : 3889.9 total, 366.0 free, 1535.2 used, 1988.6 buff/cache

  • MiB Mem:内存
  • 3889.9 total:物理内存总量
  • 366.0 free:空闲内存量
  • 1535.2 used:已使⽤的内存量
  • 1988.6 buff/cache:⽤作内核缓存的内存量

MiB Swap: 2048.0 total, 2035.2 free, 12.8 used. 2082.9 avail Mem

  • MiB Swap:交换空间(虚拟内存,当内存不⾜的时候,把⼀部分硬盘空间虚拟成内存使⽤)
  • 2048.0 total:交换区总量
  • 2035.2 free:空闲交换区总量
  • 12.8 used:使⽤的交换区总量
  • 2082.9 avail Mem:可⽤于启动⼀个新应⽤的内存(物理内存),和 free 不同,它计算的是可回收的 page cache 和 memory slab

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

  • PID:进程 id
  • USER:进程所有者
  • PR:进程的优先级,越⼩优先级越⾼
  • NI:nice 值,负值表示⾼优先级,正值表示低优先级
  • VIRT:进程使⽤的虚拟内存,单位是 kb
  • RES:进程使⽤的物理内存,单位 kb
  • SHR:进程使⽤的共享内存,单位 kb
  • S:进程状态(S 表示休眠,R 表示正在运⾏, Z 表示僵死状态,N 表示该进程优先值为负数,I 表示空闲状态)
  • %CPU:进程占⽤ CPU 时间的百分⽐
  • %MEM:进程占⽤内存的百分⽐
  • TIME+:进程实际运⾏的时间
  • COMMAND:进程的名称

pstree

显示进程树

在这里插入图片描述

kill

kill 命令是⽤于结束进程的命令或者⽤于显示相关信号

kill [选项] [参数]

选项:

  • -l : 显示信号名称
  • -s : 指定发送的信号
  • -a : 杀死进程组中的所有进程
  • -p : 杀死进程组中的进程,并将它们从进程组中剔除
  • -u : 指定用户
  • -signal : 发送指定的信号

参数:

  • 进程号: 要结束的进程号

进程的创建

并发和并行

  • 并发:多个任务在同⼀时间段被调度运行,⽐如同时有两个任务在运行,这就是并发。在有限的 cpu 核⼼的情况下(如只有⼀个 cpu 核⼼) , 利⽤快速交替 (时间⽚轮
    转) 执⾏来达到宏观上的同时执⾏

  • 并行:多个任务在不同时间段被调度运行,⽐如同时有两个任务在不同 CPU 上运行,这就是并行。在 cpu 多核的⽀持下,实现物理上的同时执⾏

并⾏是基于硬件完成,⽽并发则可以使⽤软件算法来完成, 在完成任务时,可以创建多个进程并发执⾏
在这里插入图片描述

fork()

创建子进程

返回值:

  • 0: 子进程
  • -1: 出错
    创建子进程的过程:
  • 父进程 fork() 系统调用,创建子进程,返回子进程的进程号
  • 子进程复制父进程的地址空间
  • 子进程从 fork() 系统调用返回,父进程继续执行
    在这里插入图片描述

⽗⼦进程并发执⾏, ⼦进程从 fork() 之后开始执⾏
⽗⼦进程的执⾏顺序由操作系统算法决定的,不是由程序本身决定
⼦进程会拷⻉⽗进程地址空间的内容, 包括缓冲区、⽂件描述符等 (COPY ON WRITE)

在这里插入图片描述

父子进程执行不同的任务

  • 使用 fork() 创建子进程,⽗进程和⼦进程可以并发执⾏不同的任务,
  • 执行不同的任务需要利用fork()函数返回值
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
    pid_t cpid;
    cpid = fork();
    if (cpid == -1){
        perror("[ERROR] fork()");
        exit(EXIT_FAILURE);
    }else if(cpid == 0){// 子进程
        printf("Child process task.\n");
        exit(EXIT_SUCCESS);
    }else if (cpid > 0){// 父进程
        printf("Parent process task.\n");
    }
    //父子进程都要执行的代码
    printf("Child and Process Process task.\n");
return 0;
}

创建多个进程

  • 在创建多个进程时, 最主要的原则为 由⽗进程统⼀创建,统⼀管理, 不能进⾏递归创建
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


int main(){
    int cpid;
    cpid = fork();
    if (cpid == -1){
        perror("fork(): ");
        exit(EXIT_FAILURE);
    }else if (cpid == 0){// 子进程
        printf("The child process < %d > running...\n",getpid());
        sleep(2);
        printf("The child process < %d > has exited\n",getpid());
        exit(EXIT_SUCCESS);
    }else if (cpid > 0){// 父进程
        cpid = fork();
        if (cpid == -1){
            perror("fork(): ");
        }else if (cpid == 0){
            printf("The child process < %d > running...\n",getpid());
            sleep(3);
            printf("The child process < %d > has exited\n",getpid());
            exit(EXIT_SUCCESS);
        }else if (cpid > 0){

        }
    }
    return 0;
}

进程的退出

在进程结束时,需要释放进程地址空间 以及内核中产⽣的各种数据结构
资源的释放需要通过调⽤ exit 函数或者 _exit 函数来完成
在程序结束时,会自动调⽤ exit 函数

exit()和_exit()

exit()函数让当前进程退出,并且刷新缓冲区

#include <stdlib.h>
void exit(int status);

参数:

  • status:进程退出状态
  • 系统中定义了 EXIT_SUCCESS 和 EXIT_FAILURE 两个宏,用来表示成功和失败的状态码,具体定义在头文件 stdlib.h 中

#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

示例:
创建⼀个⼦进程,让⼦进程延时 3 s 后退出


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(){

pid_t cpid;
cpid = fork();
if (cpid == -1){
perror("[ERROR] fork(): ");
exit(EXIT_FAILURE);
}else if(cpid == 0){printf("Child Process < %d > running...\n",getpid());
sleep(3);
printf("Child Process < %d >  has exited.\n",getpid());
exit(EXIT_SUCCESS);
}else if(cpid > 0){
sleep(5);
}
return 0;
}

_exit()

exit 函数与 _exit 函数功能相似, 但有很多不同, 具体如下:

  • _exit() 属于系统调⽤, 能够使进程停⽌运⾏, 并释放空间以及销毁内核中的各种数据结构
  • exit() 基于_exit() 函数实现, 属于库函数, 可以清理 I/O 缓冲区

进程的等待

  • 在⼦进程运⾏结束后,进⼊僵死状态, 并释放资源, ⼦进程在内核中的 数据结构 依然保留
  • ⽗进程 调⽤ wait() 与 waitpid() 函数等待⼦进程退出后,释放⼦进程遗留的资源

task_struct 结构体随着进程的创建而创建和销毁而销毁, 它包含了进程的所有信息, 包括进程号、进程状态、进程调度信息、进程资源使用信息等

wait()函数 和 waitpid()函数

wait()函数

函数头文件

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);

功能:
让函数调⽤者进程进⼊到睡眠状态, 等待⼦进程进⼊僵死状态后,释放相关资源并返回

参数:

  • wstatus: 指向整数的指针,用来接收⼦进程的退出状态

获取具体值需要使⽤ WEXITSTATUS() 宏定义

返回:

  • 若成功,返回值是⼦进程的进程号
  • 若出错,返回值是 -1

会阻塞调⽤者进程(⼀般为⽗进程)
在⼦进程状态为僵死态时,回收资源,并释放资源后返回

创建⼀个⼦进程, 延时 3s 后退出, ⽗进程等待⼦进程退出
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
    pid_t cpid;
    cpid = fork();
    if (cpid == -1){
        perror("[ERROR] fork(): ");
        exit(EXIT_FAILURE);
    }else if(cpid == 0){
        printf("The Child process < %d > running...\n",getpid());
        sleep(3);
        exit(88);
    }else if(cpid > 0){
        int rpid,status = 0;
        rpid = wait(&status);//会阻塞父进程,等待子进程的状态变化,自动释放资源并返回
        if (rpid == -1){
            perror("[ERROR] wait() : ");
            exit(EXIT_FAILURE);
        }
        printf("The Child Process < %d > has exited,exit code < %d >.\n",rpid,WEXITSTATUS(status));
    }
    return 0;
}

在 wait 存储在 satus 变量的值, 存储了很多信息, 通过⼀系列 W 开头的宏来解析获取

WIFEXITED(status) : 进程是否正常结束

WEXITSTATUS(wstatus) : 获取进程退出状态值, exit 函数的参数

WIFSIGNALED(wstatus) : 表示该⼦进程是否被信号结束的, 返回真,则表示被信号结束的

WTERMSIG(wstatus) : 返回结束该⼦进程的那个信号的信号值

WCOREDUMP(wstatus) : 表示该⼦进程被信号唤醒的

WIFSTOPPED(wstatus) : 表示该⼦进程是否被信号中⽌ (stop) 的 , 返回真,则表示是被信号中⽌的

waitpid()函数

waitpid 函数的功能与 wait 函数⼀样,但⽐ wait() 函数功能更强⼤, waitpid() 函数可以指定等待的进程

####函数头⽂件

#include <sys/types.h>
#include <sys/wait.h>
函数原型
pid_t waitpid(pid_t pid, int *wstatus, int options);
函数参数

pid : 进程 id

  • -1 : 可以等待任意⼦进程
  • >0 : 等待 id 为 pid 的进程

wstatus : 保存⼦进程退出状态值变量的指针

options : 选项

  • WNOHANG : ⾮阻塞选项 // 若没有可等待的进程, 则返回 0, 否则返回 -1
  • WUNTRACED : 等待被跟踪的进程
  • WCONTINUED : 继续被跟踪的进程
  • WEXITED : 等待退出的进程
  • WSTOPPED : 等待停止的进程
  • WNOWAIT : 不创建新的进程组
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
    pid_t cpid;
    cpid = fork();
    if (cpid == -1){
        perror("[ERROR] fork(): ");
        exit(EXIT_FAILURE);
    }else if(cpid == 0){
        printf("The Child process < %d > running...\n",getpid());
        sleep(3);
        exit(88);
    }else if(cpid > 0){int rpid,status = 0;
        //rpid = waitpid(-1,&status,0);//-1是等待任意子进程退出 // 0是默认阻塞的
        rpid = waitpid(-1,&status,WNOHANG);//WNOHANG是非阻塞
        if (rpid == -1){
            perror("[ERROR] wait() : ");
            exit(EXIT_FAILURE);
        }
#if 0
        //没有子进程退出就返回0
        while((rpid = waitpid(-1,&status,WNOHANG)) == 0){
}
#endif
        printf("The Child Process < %d > has exited,exit code < %d >.\n",rpid,WEXITSTATUS(status));
    }
    return 0;
}

进程的替换

创建⼀个进程后,pid 以及在内核中的信息保持 保持不变, 但进程所执⾏的代码进⾏替换

作⽤ : 通过⼀个进程启动另外⼀个进程

在这里插入图片描述

应⽤场景:Linux 终端应⽤程序,执⾏命令时,通过创建⼀个进程后,在替换成命令的可执⾏程序再执⾏

在这里插入图片描述

在 Linux 系统中提供了⼀组⽤于进程替换的替换,共有 6 个函数

函数原型:

int execl (const char *__path, const char *__arg, ... / (char *) NULL */)
 > pathname 指向一个字符数组,即字符串。这个字符串表示可执行文件的路径
 > arg 指向一个字符数组,即命令行参数。这个参数列表可以为空。
 > 省略号 ... 表示可变参数列表,它允许传递任意数量的参数给可执行文件,包括命令行参数。在参数列表的最后必须以 (char *) NULL 来指示结束。
 > NULL 是一个字符型指针表示空值,用来指示参数列表的结束。


int execlp(const char *file, const char arg,/ (char *) NULL */);

int execle(const char *pathname, const char arg,/, (char *) NULL, char *const envp[] */);

int execv(const char *pathname, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[], char *const envp[]);

> 函数参数:
        path:可执文件的路径名
        file : 可执文件名,可以通过 path 环境变量指定的路径
        arg : 参数列表,以 NULL 结尾
        argv[] : 参数数组
        envp[] : 环境变量数组
> 函数返回值:
        成功 : 0
        失败 : -1

示例:通过 execl 函数族执⾏ ls -l 命令

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
    int ret;

#if 0
    ret = execl("/bin/ls","ls","-l",NULL);//替换当前进程,启动ls命令
    if (ret == -1){
        perror("[ERROR] execl(): ");
        exit(EXIT_FAILURE);//退出当前进程
    }
#endif

    char *const argv[]={"ls","-l",NULL};//参数列表
    ret= execv("/bin/ls",argv);//替换当前进程,启动ls命令
    if (ret == -1){
        perror("[ERROR] execl(): ");
        exit(EXIT_FAILURE);
    }

     //env可以查看环境变量
    ret= execlp("ls","ls","-l",NULL);//函数会搜索环境变量当作路径
    if (ret == -1){
        perror("[ERROR] execl(): ");
        exit(EXIT_FAILURE);
    }

    
    return 0;
}
//这个操作替换了当前进程,启动了另外的程序
//一般是先创建子进程,然后在子进程中调用 execl 函数族,替换子进程
//替换当前进程,启动ls命令
    if (ret == -1){
        perror("[ERROR] execl(): ");
        exit(EXIT_FAILURE);//退出当前进程
    }
#endif

    char *const argv[]={"ls","-l",NULL};//参数列表
    ret= execv("/bin/ls",argv);//替换当前进程,启动ls命令
    if (ret == -1){
        perror("[ERROR] execl(): ");
        exit(EXIT_FAILURE);
    }

     //env可以查看环境变量
    ret= execlp("ls","ls","-l",NULL);//函数会搜索环境变量当作路径
    if (ret == -1){
        perror("[ERROR] execl(): ");
        exit(EXIT_FAILURE);
    }

    
    return 0;
}
//这个操作替换了当前进程,启动了另外的程序
//一般是先创建子进程,然后在子进程中调用 execl 函数族,替换子进程

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

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

相关文章

ros中teleop_twist_keyboard安装使用

目录 1.安装 2.使用 3.说明 1.安装 sudo apt-get install ros-noetic-teleop-twist-keyboard 其中noetic替换成你自己的ros版本 2.使用 roscore #启动roscore rosrun teleop_twist_keyboard teleop_twist_keyboard.py …

Python学习笔记(一):基础特性

python英文官方文档:https://docs.python.org/3.8/tutorial/index.html 比较不错的python中文文档:https://www.runoob.com/python3/python3-tutorial.html 1. 写在前面 这几周从实践角度又学习了一遍python,温故而知新,还是有蛮多心得的, 周末再看之前记的python笔记,…

vue3绘制广东深圳地图使用echarts

<!-- 饼图 --> <template><el-card><template #header> 地级市分类图 </template><div :id"id" :class"className" :style"{ height, width }"></div></el-card> </template><script …

【算法专题--栈】用栈实现队列 -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双栈 模拟 队列 &#x1f95d;栈 和 队列 的特性 &#x1f34d;具体思路 &#x1f34d;案例图解 四、总结与提炼 五、共勉 一、前言 用栈实现队列 这道题&#xff0c;可以说是--栈专题--&#xff0c;最经典的一道题&…

昇思MindSpore学习入门-函数式自动微分

函数式自动微分 神经网络的训练主要使用反向传播算法&#xff0c;模型预测值&#xff08;logits&#xff09;与正确标签&#xff08;label&#xff09;送入损失函数&#xff08;loss function&#xff09;获得loss&#xff0c;然后进行反向传播计算&#xff0c;求得梯度&#…

为何同一PDF文档用不同软件打印效果不同?

通过扫描仪生成的同一PDF文档&#xff0c;同样的设置&#xff0c;为什么别的电脑打出来是白底我的打出来有灰色格子背景&#xff1f;这种情况通常是由于PDF阅读软件的不同造成的差异。 ### 可能的原因和解决方法&#xff1a; 1. **PDF阅读软件的不同**&#xff1a; - **解决方…

Java高级重点知识点-17-异常

文章目录 异常异常处理自定义异常 异常 指的是程序在执行过程中&#xff0c;出现的非正常的情况&#xff0c;最终会导致JVM的非正常停止。Java处 理异常的方式是中断处理。 异常体系 异常的根类是 java.lang.Throwable&#xff0c;&#xff0c;其下有两个子类&#xff1a;ja…

Java 面试指南合集

JVM 篇 线程篇 springBoot篇 SpringCloud篇 待更新 黑夜无论怎样悠长&#xff0c;白昼总会到来。 此文会一直更新哈 如果你希望成功&#xff0c;当以恒心为良友&#xff0c;以经验为参谋&#xff0c;以当心为兄弟&#xff0c;以希望为哨兵。

Java单体架构项目_云霄外卖-特殊点

项目介绍&#xff1a; 定位&#xff1a; 专门为餐饮企业&#xff08;餐厅、饭店&#xff09;定制的一款软件商品 分为&#xff1a; 管理端&#xff1a;外卖商家使用 用户端&#xff08;微信小程序&#xff09;&#xff1a;点餐用户使用。 功能架构&#xff1a; &#xff08…

改进经验模态分解方法-通过迭代方式(IMF振幅加权频率,Python)

一种新颖的改进经验模态分解方法-通过迭代方式&#xff08;IMF振幅加权频率&#xff09;有效缓解了模态混叠缺陷&#xff0c;以后慢慢讲&#xff0c;先占坑。 import numpy as np import matplotlib.pyplot as plt import os import seaborn as sns from scipy import stats i…

Pikachu 不安全的文件下载(Unsafe file download)概述 附漏洞利用案例

目录 获取下载链接 修改链接 重新构造链接 拓展 不安全的文件下载概述 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c;便会向后台发送一个下载请求&#xff0c;一般这个请求会包含一个需要下载的文件名称&#xff0c;后台在收到请求…

回溯法基本思想-01背包、N皇后回溯法图解

基本思想&#xff1a; ​ 回溯法是一种系统地搜索问题解空间的算法&#xff0c;常用于解决组合优化和约束满足问题。其核心思想是利用深度优先搜索逐步构建可能的解&#xff0c;同时在搜索过程中进行剪枝操作&#xff0c;以排除那些无法满足问题约束或不能产生最优解的分支&am…

第十一节:学习通过动态调用application.properties参数配置实体类(自学Spring boot 3.x的第二天)

大家好&#xff0c;我是网创有方。这节实现的效果是通过代码灵活地调用application.properties实现配置类参数赋值。 第一步&#xff1a;编写配置类 package cn.wcyf.wcai.config;import org.springframework.beans.factory.annotation.Value; import org.springframework.boo…

【后端面试题】【中间件】【NoSQL】ElasticSearch 节点角色、写入数据过程、Translog和索引与分片

中间件的常考方向&#xff1a; 中间件如何做到高可用和高性能的&#xff1f; 你在实践中怎么做的高可用和高性能的&#xff1f; Elasticsearch节点角色 Elasticsearch的节点可以分为很多种角色&#xff0c;并且一个节点可以扮演多种角色&#xff0c;下面列举几种主要的&…

python第一课 环境准备篇

一、所需工具 电脑&#xff1a;windows或mac 二、安装教程 1、访问 Python 的官方网站&#xff08;https://www.python.org/ &#xff09;&#xff0c;找到 DownLoad &#xff0c;无法访问百度网盘下载 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;8cho 2、选…

用Java操作MySQL数据中的日期类型的数据存取问题分析及其解决办法

目录 一、问题说明二、问题分析三、解决办法1.Java日期向数据存方法一&#xff1a;方法二&#xff1a; 2.从数据库中取日期最后 在Java中向MySQL数据库存取日期类型的数据时&#xff0c;可能会遇到一些常见问题&#xff0c;以下是一些关键点和解决办法&#xff1a; 一、问题说…

基于bootstrap的12种登录注册页面模板

基于bootstrap的12种登录注册页面模板&#xff0c;分三种类型&#xff0c;默认简单的登录和注册&#xff0c;带背景图片的登录和注册&#xff0c;支持弹窗的登录和注册页面html下载。 微信扫码下载

Spring学习01-[Spring实现IOC的几种方式]

Spring实现IOC的几种方式 基于xml实现Spring的IOC基于注解实现Spring的IOC基于JavaConfig实现的Spring的IOC基于SpringBoot实现Spring的IOC 基于xml实现Spring的IOC 引入spring核心依赖 <!--spring核心容器--><dependency><groupId>org.springframework<…

【最新鸿蒙应用开发】——用户信息封装

用户管理工具封装 1. 为什么要封装 在进行如下登录功能时&#xff0c; 通常需要将一些用户信息以及token进行持久化保存&#xff0c;以方便下次进行数据请求时携带这些用户信息来进行访问后端数据。下面分享一下鸿蒙当中实用的持久化封装操作。 2. 步骤 封装用户信息管理工具…

数据恢复篇:如何在没有备份的情况下从恢复已删除的照片

许多用户更喜欢将他们的私人照片保存在他们的 Android 设备上的一个单独的安全空间中&#xff0c;以确保他们的记忆不仅被存储&#xff0c;而且受到保护。这就是“安全文件夹”功能派上用场的地方。您可以使用 PIN 码、密码、指纹或图案锁定此文件夹&#xff0c;即使您的设备落…