【Linux】进程终止进程等待

news2024/11/14 13:43:51

文章目录

    • 进程创建
      • fork函数初识
      • fork函数返回值
      • 写时拷贝
      • fork常规用法
      • fork调用失败的原因
    • 进程终止
      • 进程退出场景
      • 进程常见退出方法
    • 进程等待
      • 进程等待必要性
    • 进程等待的方法
      • wait方法
      • waitpid方法
      • 获取子进程status
      • 从操作系统层面理解waitpid
      • 阻塞状态和非阻塞状态
        • 阻塞等待
          • 例子:多进程创建和等待的模型
        • 非阻塞等待

进程创建

./ 执行程序 或者调用fork函数 本质都是创建进程

首先我们先新建工程对应的makefile文件

image-20220518201522367


但是如果我们想使用C99的标准我们就需要写成:

image-20220518201800169

fork函数初识

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程,新进程为子进程,而原进程为父进程

#include <unistd.h>
pid_t fork(void);
//fork函数有两个返回值:子进程中返回0,父进程返回子进程id
//如果fork函数创建子进程失败, 返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核会做的事情:

  • 分配新的内存块加载代码和数据, 内核数据结构(包括进程控制块PCB,地址空间,页表,构建映射关系)给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表(运行队列)当中
  • fork返回之后,开始调度器调度

fork执行之前父进程独立执行,fork之后,父子两个执行流分别执行

注意,fork之后,谁先执行完全由调度器决定


fork函数返回值

  • 子进程返回0
  • 父进程返回的是子进程的pid

问:fork函数为什么要给子进程返回0,给父进程返回子进程的PID

答:一个父进程可以创建多个子进程,而一个子进程只能有一个父进程.因此,对于子进程来说,父进程是不需要被标识的;而对于父进程来说,子进程是需要被标识的,因为父进程创建子进程的目的是让其执行任务的,父进程只有知道了子进程的PID才能很好的对该子进程指派任务

问2:为什么fork函数有两个返回值

答:父进程调用fork函数后,为了创建子进程,fork函数内部将会进行一系列操作,包括创建子进程的进程控制块.创建子进程的进程地址空间.创建子进程对应的页表等等.子进程创建完毕后,操作系统还需要将子进程的进程控制块添加到系统进程列表当中,此时子进程便创建完毕了

image-20220518211634310

也就是说,在fork函数内部执行return语句之前,子进程就已经创建完毕了,那么之后的return语句不仅父进程需要执行,子进程也同样需要执行,这就是fork函数有两个返回值的原因


例子:

image-20220518201355325

执行结果:

image-20220518201411107


写时拷贝

默认情况下,子进程会继承父进程的代码和数据父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本

image-20220515123438347

在父/子修改数据时,会发生缺页中断:OS再开辟一段空间,把数据拷贝过来(写时拷贝),重新建立映射关系;

父子分开,更改读写权限.这时候再进行写操作 这样保证了父子进程的独立性


1.为什么数据要进行写时拷贝?

进程具有独立性.多进程运行,需要独享各种资源,多进程运行期间互不干扰,不能让子进程的修改影响到父进程

2.为什么不在创建子进程的时候就进行数据的拷贝?

子进程不一定会使用父进程的所有数据,并且在子进程不对数据进行写入的情况下,没有必要对数据进行拷贝,我们应该按需分配,在需要修改数据的时候再分配(延时分配),这样可以高效的使用内存空间

3.代码会不会进行写时拷贝?

90%是不会的,但这并不代表代码不能进行写时拷贝,例如在进行进程替换的时候,则需要进行代码的写时拷贝


fork常规用法

  • 一个进程希望复制自己,使父子进程执行同一个代码的不同的代码段

例如,父进程等待客户端请求,生成子进程来处理请求.

  • 一个进程要执行一个不同的程序

例如子进程从fork返回后,调用exec*函数.


fork调用失败的原因

  • 系统中有太多的进程导致资源的不足 -> 因为创建进程的成本很大 (空间+时间)
  • 实际用户的进程数超过了限制 -> 防止某些用户创建多个无用进程

进程终止

进程退出场景

问:进程退出在操作系统层面上发生了什么?

意味着少了一个进程 -> 释放其对应的PCB,释放其的mm_strucu , 释放其页表即各种映射关系

注意:进程卡住了,不算进程退出!


  • 代码运行完毕,结果正确 -> 错误码为0

例子:

image-20220518202008771

  • 代码运行完毕,结果不正确 -> 错误码非0

例子:

image-20220518202933584

  • 代码异常终止( 进程崩溃) ->这是运行时错误,即程序崩溃,或者浮点数错误(/0)等…
    • 异常终止的本质是这个进程因为异常问题,导致自己收到了某种信号

例子:

image-20220518202701233

运行结果:

image-20220518202748545

程序崩溃时,退出码没有意义,因为没有执行return ,又上述也可看出,没有执行打印语句后面的内容


进程常见退出方法

问:为什么我们写main函数的时候,退出的时候总是return 0,这个0有什么意义?

  • 我们平常写代码,函数return的值就是进程的退出码
  • 用退出码衡量程序的结果是否正确 / 程序是否有错误,0表示成功

此时可以通过 echo $? 查看最近一次进程的退出码

  • 1.如果代码运行完毕,执行结果正确 -> 退出码为0 代表success

  • 2.代码运行完毕,执行结果不正确 -> 退出码非0, 代表failed

    为什么会不正确呢?有很多种可能,我们可以根据错误码(strerror)对应的字符串进行判断

  • 3.代码异常终止 -> 程序崩溃 ->此时退出码没有意义


查找错误码对应的内容:

image-20220518202205184

strerror函数功能:查看错误码种类的函数,返回的是一个字符串

执行结果:

image-20220518202315309

问:为什么echo $?可以查看进程的退出码?

原因:bash是所有在命令行启动的进程的父进程,bash一定是通过wait方式得到子进程的退出结果,所有echo $?可以查看子进程的退出码

问2:为什么以0表示代码执行成功,以非0表示代码执行错误

因为代码执行成功只有一种情况,成功了就是成功了,而代码执行错误却有多种原因,例如内存空间不足.非法访问以及栈溢出等等,我们就可以用这些非0的数字分别表示代码执行错误的原因


正常终止

  1. 从main返回 return退出

main函数,return代表进程退出,return的值就是进程的退出码!

注意:非main函数return的值,代表的是函数返回,并不是退出码

return是一种更常见的退出进程方法.执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数

例子

image-20220518202549159

在Linux下,指令本质也是可执行程序->也是进程,所以我们可以查看指令对应的退出码,这些命令成功执行,退出码为0 ,但是如果命令执行出错,退出码就是非0的数字,该数字代表具体的某一种错误信息

注意:退出码都有对应的字符串含义,帮助用户确认执行失败的原因,而这些退出码具体代表什么含义是人为规定的,不同环境下相同的退出码的字符串含义可能不同


2.exit退出

exit可以在任意地方调用,都代表进程终止 -> 即exit后序的代码不执行,exit的参数代表的就是退出码

image-20220518203209949

status:退出码, 可以为:exit(EXIT_SUCCESS) 或者为:exit(EXIT_FAILURE)

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

exit最后也会调用_exit, 但在调用_exit之前,还做了其他工作:

  1. 执行用户通过 atexit或on_exit定义的清理函数.
  2. 关闭所有打开的流,所有的缓存数据均被写入
  3. 调用_exit

注意:exit终止进程前会先刷新缓冲区,将缓冲区当中的数据写入


例子:

image-20220518203508334


3._exit退出

#include <unistd.h>
void _exit(int status);

参数:status 定义了进程的终止状态,父进程通过wait来获取该值

说明:虽然status是int,但是仅有低8位可以被父进程所用.所以**_exit(-1)时,在终端执行$?发现返回值是255**


首先我们知道:显示器的刷新策略是行刷新, \n可以进行刷新

image-20220518203837386

但是exit函数退出和return 在进程退出时,还会要求系统进行刷新缓冲区

image-20220518204123173


_exit函数也可以在代码中的任何地方退出进程 但是_exit函数会强制终止进程,不会进行进程的首尾工作,比如刷新缓冲区

image-20220518204246777


return ,exit和_exit的区别:

只有在main函数当中的return才能起到退出进程的作用,子函数当中return不能退出进程,而exit函数和_exit函数在代码中的任何地方使用都可以起到退出进程的作用

使用exit函数退出进程前,exit函数会执行用户定义的清理函数.刷新缓冲区,关闭流等操作,然后再终止进程,而_exit函数会直接终止进程,不会做任何收尾工作

image-20220518210420512

return ,exit和_exit的关系:

执行return num等同于执行exit(num),因为调用main函数运行结束后,会将main函数的返回值当做exit的参数来调用exit函数

使用exit函数退出进程前,exit函数会先执行用户定义的清理函数.冲刷缓冲,关闭流等操作,然后再调用_exit函数终止进程


异常退出

  • 向进程发送信号导致进程异常退出
    • 如:在进程运行的时候,给进程发送9号信号杀死进程 或者 ctrl + c终止进程
  • 代码错误导致进程运行时异常退出
    • 如:出现浮点数错误(整数/0的情况) 或者存在野指针的情况,使得进程在运行时异常退出

进程等待

进程等待必要性

  • 1.子进程退出,父进程如果不读取子进程的退出信息, 就可能造成子进程变成 ‘僵尸进程’的问题,进而造成内存泄漏. 需要通过父进程wait释放该子进程占用的资源
  • 2.进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 命令也无能为力,因为谁也没有办法杀死一个已经死去的进程
  • 3.通过fork创建子进程的目的就是帮助父进程完成某种任务 父进程需要知道派给子进程的任务完成的如何,所以让父进程fork之后,需要通过wait/waitpid等待子进程退出
    • 子进程:为了帮助父进程完成某种任务,父进程需要知道子进程运行完成,结果对还是不对,或者是否正常退出
  • 4.父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
  • 5.保证时序问题,保证子进程先退出,父进程后退出

进程等待的方法

wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
  • 作用: 等待任意子进程结束

  • 返回值: 等待成功:返回被等待进程的pid,等待失败返回-1.

  • 参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

例子1:子进程变成了僵尸进程,父进程通过等待回收

验证:fork之后,我们让父进程休眠6s,然后使用wait函数等待子进程结束 在前3s的时候,子进程正常执行,后3s,子进程执行结束,exit退出,子进程进入僵尸状态, 此时父进程刚好结束休眠,开始等待,然后父进程会去读取子进程的退出信息,回收子进程

image-20220518213935200

写一个监控脚本:

while :; do ps ajx | head -1 && ps ajx | grep myproc | grep -v grep;sleep 1; echo "------------------------------------------------------"; done

运行结果:我们可以看到wait确实可以回收僵尸进程:

image-20220518214418175


例子2:wait防止子进程变成僵尸进程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        int count = 10;
        while(count)
        {
            printf("Child[%d] running,%d seconds \n",getpid(),count);
            count--;
            sleep(1);
        }
        exit(0);
    }
    //father
    int status = 0;
    pid_t ret = wait(NULL);//等待任意子进程
    if(ret>0)
    {
        //等待成功
        printf("father wait child[%d] success\n",ret);
    }
    else
    {
        printf("father wait failed\n");
    }
    sleep(3);
    return 0;
}

image-20220519090302976


waitpid方法

#include<sys/types.h>
#include<sys/wait.h>
pid_ t waitpid(pid_t pid, int *status, int options);
作用:可以等待任意子进程 也可以等待指定子进程
    
返回值:
    1.等待成功,返回被等待进程的的pid
    2.如果第三个参数为WNOHANG, 而waitpid发现没有已退出的子进程可收集,则返回03.如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在
    简单来说:等待成功,返回被等待进程的PID,等待失败,返回-1
        
参数解析:
 pid:
    	Pid=-1,表示等待任意一个子进程.与wait等效
   		Pid>0. 等待其进程ID与此时第一个参数pid值相等的子进程, 即等待特定的一个进程
     
 status:
		获取子进程退出状态,不关心则可以设置成为NULL
		WIFEXITED(status): 若为正常终止子进程返回的状态,则为真.(查看进程是否是正常退出)
    	WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码.(查看进程的退出码)
       
 options:
		若设置为WNOHANG: 若等待的子进程没有结束,waitpid()函数返回0,不予以等待
        若正常结束,则返回该子进程的PID
        对应该参数我们通常置为0
  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息.
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
  • 如果等待不存在的子进程,则立即出错返回

关于返回值进一步说明:

  • 如果等待成功了,返回收集到的子进程的ID,如果等待出错,则返回-1, 这时候errno会被设置成相应的值用来标识特定的错误
  • 如果设置了WNOHANG选项,而调用中watpid发现没有进程可以收集,则返回0

例子:和上面wait一样,只不过是等待方法换了

pid_t ret = wait(NULL);//等待任意一个进程
pid_t ret = waitpid(id,NULL,0);//等待指定PID为id的进程 
pid_t ret = waitpid(-1,NULL,0);//等待任意的一个进程,等价于wait

获取子进程status

  • 上述的wait和waitpid函数都有一个status参数,该参数是一个输出型参数,由操作系统进行填充

例子:

image-20220519091013400


  • 如果status参数传入NULL,表示等待的时候,不关心子进程的退出状态信息, 否则操作系统会根据该参数,将子进程的退出信息反馈给父进程
  • status虽然是一个整形变量,但是我们不能简单的把 status当作整形来看待,可以当作位图来看待,status的不同比特位所代表的信息不同,具体细节看下图:(只研究status低16比特位)

image-20220518233509391

正常终止情况:代码跑完,运行正确 / 不正确 -> 退出码 代码异常终止:本质是进程因为异常问题导致自己收到了某种信号


在status的低16个比特位中,高8位表示进程的退出状态即退出码. 进出如果被信号所杀,那么低7位表示进程的终止信号,而第8位比特位是core dump标志

所以我们可以通过位运算操作,就可以根据status得到进程的退出码和终止信号

如果status的低7比特位为0,就没有收到信号,证明程序是正常运行结束的,运行结果是否正确就通过status的次低8位获得! 如果收到了信号,退出码没有意义!

退出码:exitCode = (status>>8)&0xFF //只要高8位的内容  0xFF==1111 1111
退出信号:exitSignal = status & 0x7F //只要低8位的内容 0x7F==0111 1111

例子1:代码跑完,运行正确

image-20220519091550005


例子2-代码运行结束, 结果不正确

image-20220519091859897


情况3:代码异常终止

  • 给子进程发送终止信号,提前把进程干掉,此时退出码没有意义, 退出信号就是我们发送的信号

image-20220519092351758

  • 浮点数错误 (整数/0)

image-20220519092507777

执行结果:

image-20220519092604205


系统当中也提供了两个宏来获取 退出码 和 退出信号,这样我们就不需要自己使用位运算了

  • WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到终止信号
  • WEXITSTATUS(status):用于获取进程的退出码
exitCode = WEXITSTATUS(status);//获取进程的退出码
exitSignal = WIFEXITED(status);//是否收到终止信号

注意:当一个进程非正常退出(由于异常终止)的时候,说明是收到了终止信号,此时进程的退出码没有意义

例子1:代码运行结束,结果不正确

image-20220519092834858


例子2:代码异常结束:

image-20220519093112456


从操作系统层面理解waitpid

image-20220519103405154

waitpid是系统调用接口,由用户调用! 子进程陷入僵尸状态时,PCB保存进程退出时的退出数据,即exit_code和signal这两个值, 当用户层调用waitpid接口的时候,传过去的是一个&status,操作系统就拿到status的地址,然后让父进程拿到子进程的退出数据,把退出数据弄到status上


阻塞状态和非阻塞状态

pid_ t waitpid(pid_t pid, int *status, int options);

waitpid的第三个参数options,用来设置等待方式

  • 0 :默认阻塞等待
  • WNOHANG :设置为非阻塞等待

一个小例子说明什么是阻塞等待和非阻塞等待:

阻塞等待:就是我有事情找张三, 我给张三打电话叫他下来.他说要30分钟才下来,没问题 我等你,但是别挂电话,当你好的时候你在电话跟我说一声.此时我就知道你要下来了.这种不挂电话的方式就是阻塞等待,对方不完成 你也不返回 即:子进程不退出 父进程也不返回,直到子进程执行完再返回,这就叫阻塞

非阻塞等待:就是我给张三打电话叫他下来.他说要30分钟才下来,然后你说行吧,然后电话一挂, 然后我每隔一段时间就问他下来没有, 通过不断打电话挂电话的方式 即:不断检测张三的运行状态


阻塞等待和非阻塞等待都是一种等待方式,如果对应到操作系统中呢?谁在等待? 等待谁?等什么

父进程在等待,等待子进程,进一步来说是:等待子进程退出


阻塞等待

阻塞的时候,没有跑父进程的代码,父进程如果是在阻塞状态去等子进程,父进程就是处于等待状态,并不会被CPU运行,让父进程等待实际是把父进程的PCB加入到等待队列当中,将父进程的状态从R状态变为S状态,当操作系统识别到子进程执行结束时,发现父进程在等待队列当中, 把父进程的PCB重新加入到运行队列中,然后执行后序的过程,获取子进程的退出结果

阻塞的本质:意味着进程的PCB被放入等待队列中,并将进程状态由R改为S状态

子进程返回的本质:子进程退出时,父进程的PCB从等待队列拿回R队列,从而被CPU调度

例子:多进程创建和等待的模型

例子的意思:我们通过for循环同时创建10个子进程,同时将子进程的pid对应的放到ids数组中,然后将这10个子进程的退出码设置为该子进程在ids中的下标,然后父进程使用waitpid函数指定的等待这10个子进程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
    pid_t ids[10];
    for(int i = 0;i<10;i++)
    {
        pid_t id = fork();    //创建10个子进程
        if(id == 0)
        {
            //child
            printf("Child process created Success  pid:%d\n",getpid());
            sleep(3);
            exit(i);//将子进程的退出码设置为该子进程在数组ids的下标
        }
        //father
        ids[i] = id;//将子进程的pid对应在数组ids的下标位置
    }
    for(int i = 0;i<10;i++)
    {
        int status = 0;
        pid_t ret = waitpid(ids[i],&status,0);//等待ids[i]这个进程
        if(ret>=0)
        {
            //wait child success
            //ids[i]就是子进程的pid
            printf("father wait children success  pid:%d\n",ids[i]);
            if(WIFEXITED(status))
            {
                //正常退出  WEXITSTATUS(status):得到退出码
                printf("exit code:%d\n",WEXITSTATUS(status));
            }
            else
            {
                //收到了信号  status&0x7F:收到了几号信号
                printf("kill by signal %d \n",status&0x7F);
            }
        }
    }
    return 0;
}

执行结果:

image-20220525000055275

我们之前写的都是阻塞等待,当子进程没有退出的时候,父进程一直等待子进程退出,等待的时候,父进程不能做自己的事情,这种等待叫做阻塞等待,所以如果我们想让父进程等待的时候做自己的事情,就要使用非阻塞等待


非阻塞等待

子进程没有退出的时候父进程做自己的事情,当子进程退出的时候, 再去读取子进程的退出信息

我们看到某些应用或者OS本身,卡住或长时间不动,叫做应用或者程序HANG住了 那么,WNOHANG表示设置等待方式为非阻塞

例子:waitpid函数的第三个参数设置为WNOHANG :如果我们等待的子进程没有结束,那么waitpid直接返回0,不予以等待, 如果等待的子进程是正常结束,则返回该子进程的pid, 因为不知道要等到什么时候,所以父进程死循环等待子进程

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
    pid_t id = fork();//创建子进程
    if(id == 0)
    {
        //child
        int count = 2;
        while(count)
        {                                            
            printf("child running pid:%d\n",getpid());
            count-- ;
            sleep(3);
        }
        exit(0);
    }
    //parent
    //死循环等待子进程结束
    while(1)
    {
        int status = 0;
        pid_t ret = waitpid(id,&status,WNOHANG);//指定等待pid为id这个子进程结束 第三个参数设置为WNOHANG  
        if(ret > 0)
        {
            //子进程退出了
            printf("father wait child success\n");
            printf("exit code:%d\n",WEXITSTATUS(status));
            break;//不需要再等待了
        }
        else if(ret == 0)
        {   
            //子进程没有结束
            printf("father do father things\n");  //父进程做自己的事情
            sleep(1);
        }
        else
        {
            //等待失败
            printf("father wait child fail\n");
            break;//等待失败直接退出
        }
    }
    return 0;
}

image-20220525172000192

父进程在非阻塞等待子进程时,返回值有以下几种情况 ——

  • 子进程根本就没退出
  • 子进程退出,waitpid成功或失败(等待是有可能失败的,比如你等错了进程)

上述也成称为基于非阻塞等待的轮询方案

int status = 0;
while(1)
{
    pid_t ret = waitpid(id,&status,WNOHANG);
    if(ret == 0){
        //子进程没有退出,但是waitpid等待成功,需要父进程重复进行等待
    }
    else if(ret > 0){
        //子进程退出了,waitpid也成功了,获取到了对应的结果
    }
    else{	//ret<0
        //等待失败
    }
}

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

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

相关文章

设计模式 - 模板方法模式详解

介绍&定义 模板模式&#xff0c;全称是模板方法设计模式&#xff0c;英文是 Template Method Design Pattern。在 GoF 的《设计模式》一书中&#xff0c;它是这么定义的&#xff1a; Define the skeleton of an algorithm in an operation, deferring some steps to subcl…

从辅助驾驶到自动驾驶究竟还有多远?

/ 导读 /现如今&#xff0c;自动驾驶的噱头早已被厂家们放在台面上宣传了太多&#xff0c;小鹏汽车更是在最近宣称要在2023年在中国率先推出全自动驾驶&#xff0c;此言一出更是一石激起千层浪&#xff0c;而业内人士表示针对此类言论早已经见怪不怪了&#xff0c;更何况何小鹏…

计算机网络期末复习汇总(附某高校期末真题试卷)

文章目录一、选择题二、填空题三、名词解析四、简答题五、高校期末真题一、选择题 1、传输延迟时间最小的交换方法是( A ) A.电路交换 B.报文交换 C.分组交换 D.信元交换 2、在OSI七层结构模型中&#xff0c;处于数据链路层与运输层之间的是&#xff08; B&#xff09; A、物…

双代号网络图、双代号时标网络图、单代号网络图精讲

01进度管理—普通双代号网络1.识读、虚箭线(1)网络图的识读&#xff1a;基本组成及逻辑关系&#xff1b;(2)补充虚箭线&#xff1a;共用一个班组、共用一台机械&#xff1b;(3)网络图的基本绘制要求&#xff1a;①只有一个起点及终点&#xff1b;②箭线从小节点编号指向大编号&…

for var in 循环报错

近期对babel进行升级&#xff0c;突然爆出 Property left of ForInStatement expected node to be of a type ["VariableDeclaration","LVal"] but instead got undefined&#xff1b;的错误&#xff0c;不知为何&#xff1b;解决&#xff1a;for(var p in…

数据库如何分库分表

有了主从数据库为啥还需要分库分表 如果一个网站业务快速发展&#xff0c;那这个网站流量也会增加&#xff0c;数据的压力也会随之而来&#xff0c;比如电商系统来说双十一大促对订单数据压力很大&#xff0c;Tps十几万并发量&#xff0c;如果传统的架构&#xff08;一主多从&a…

基于matlab设计x波段机载SAR系统

一、前言此示例说明如何设计在 X 波段工作的合成孔径雷达 &#xff08;SAR&#xff09; 传感器并计算传感器参数。SAR利用雷达天线在目标区域上的运动来提供目标区域的图像。当SAR平台在目标区域上空行进时&#xff0c;当脉冲从雷达天线发送和接收时&#xff0c;会产生合成孔径…

MySQL(一):B+ Tree,索引以及其优点, 索引实战, 聚簇索引和非聚簇索引, 最左匹配,索引失效

文章目录一、B TreeB Tree相比于红黑树的优点1. B树有更低的树高2. B树更符合磁盘访问原理二、MySQL索引2.1 B Tree索引2.2 哈希索引2.3 全文索引2.4 空间数据索引三、索引的优点以及什么时候需要使用索引什么时候需要使用索引四、索引实战建立普通索引建立唯一索引建立主键索引…

FreeRTOS内存管理 | FreeRTOS十五

目录 说明&#xff1a; 一、FreeRTOS内存管理 1.1、动态分配与用户分配内存空间 1.2、标准C库动态分配内存缺点 1.3、FreeRTOS的五种内存管理算法优缺点 1.4、heap_1内存管理算法 1.5、heap_2内存管理算法 1.6、heap_3内存管理算法 1.7、heap_4内存管理算法 1.8、hea…

节能降耗方案-医院能源管理系统平台的研究与应用分析

摘要&#xff1a;综合性医院作为大型公共机构&#xff0c;能耗高的问题日益突出&#xff0c;构建能耗监控平台对医院能耗量化管理以及效果评估已经成为迫切需要。建立智能能耗监控平台&#xff0c;对采集的能耗数据进行分析&#xff0c;实现对医院能耗平台监控&#xff0c;为医…

Server端的Actor,分工非常的明确,但是只将Actor作为一部手机来用,真的合适吗?

这是一篇介绍PowerJob&#xff0c;Server端Actor的文章&#xff0c;如果感兴趣可以请点个关注&#xff0c;大家互相交流一下吧。 server端一共有两个Actor&#xff0c;一个是处理worker传过来的信息&#xff0c;一个是server之间的信息传递。 处理Worker的Actor叫做WorkerRequ…

5、HAL库驱动W25Qxx

一、 SPI通信驱动W25Qxx 1、使用驱动文件快速配置工程代码驱动W25Qxx &#xff08;此驱动文件只适合W25Qxx 16M及以下型号&#xff0c;因为访问地址位数不同&#xff09; 注&#xff1a;本次使用SPI的方式进行访问W25Qxx Flash进行数据读写&#xff0c;关于W25Qxx芯片不会做…

10大主流压力测试工具各有所长,怎么选适合自己的?

市面上流行的压力/负载/性能测试工具多是来自国外&#xff0c;近年来国内的性能测试工具也如雨后春笋崛起。同时由于开发的目的和侧重点不同&#xff0c;其功能也有很大差异&#xff0c;下面就为您简单介绍10款目前最常见的测试产品。 1、kylinTOP测试与监控平台&#xff08;商…

实现一个比ant功能更丰富的Modal组件

普通的modal组件如下&#xff1a; 我们写的modal额外支持&#xff0c;后面没有蒙版&#xff0c;并且Modal框能够拖拽 还支持渲染在文档流里&#xff0c;上面的都是fixed布局&#xff0c;我们这个正常渲染到文档下面&#xff1a; render部分 <RenderDialog{...restState}visi…

Lesson5.2---Python 之 NumPy 切片索引和广播机制

一、切片和索引 ndarray 对象的内容可以通过索引或切片来访问和修改&#xff08;&#xff09;&#xff0c;与 Python 中 list 的切片操作一样。ndarray 数组可以基于 0 - n 的下标进行索引&#xff08;先行后列&#xff0c;都是从 0 开始&#xff09;。 区别在于&#xff1a;数…

代码随想录算法训练营第三十二天 | 122.买卖股票的最佳时机II,55. 跳跃游戏,45.跳跃游戏II

一、参考资料买卖股票的最佳时机IIhttps://programmercarl.com/0122.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BAII.html 跳跃游戏https://programmercarl.com/0055.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8F.html 跳跃游戏 IIhttps://pr…

金三银四必备软件测试必问面试题

初级软件测试必问面试题1、你的测试职业发展是什么&#xff1f;测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前 3 年积累测试经验&#xff0c;按如何做好测试工程…

【数据结构期末例题】

前言 本文是博主自己在准备学校数据结构考试时的总结&#xff0c;各个知识点都贴有对应的详细讲解文章以供大家参考&#xff1b;当然文中还有许许多多的截图&#xff0c;这些是博主对主要内容的摘取&#xff0c;对于那些基础较好的同学可以直接看截图&#xff0c;减少跳转对应文…

声呐学习笔记之波束成形

目录什么是波束什么是波束成形线阵数学推导(均匀排布)什么是波束 和光束一样&#xff0c;当所有波的传播方向都一致时&#xff0c;即形成了波束。工程师利用波束已经有相当久的历史。在二战中&#xff0c;工程师已经将波束利用在雷达中&#xff0c;雷达通过扫描波束方向来探测…

力扣-分数排名

大家好&#xff0c;我是空空star&#xff0c;本篇带你了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;178. 分数排名二、解题1.错误示范①提交SQL运行结果2.错误示范②提交SQL运行结果3.正确示范①提交SQL运行结果4.正确示范②提交SQL运行结果5.正确示范③提交…