当前位置: 首页 > news >正文

【Linux】进程概念与进程状态

文章目录

  • 一、进程概念
    • 1.进程的概念
    • 2.进程的描述-PCB
  • 二、进程相关的基本操作
    • 1.组织进程
    • 2.查看进程
    • 3.结束进程
    • 4.通过系统调用获取进程标示符
    • 5.通过系统调用创建进程-fork初识
  • 三、进程状态
    • 1.普遍操作系统层面的进程状态
    • 2.Linux操作系统的进程状态
  • 四、两种特殊的进程状态
    • 1.僵尸进程
    • 2.孤儿进程
  • 五、进程优先级
    • 1.什么是优先级
    • 2.为什么要有优先级
    • 3.Linux中优先级的特点
  • 六、进程的其他概念
  • 七、进程切换

一、进程概念

1.进程的概念

我们通常在课本看到对进程的描述是这样的:一个运行起来(被加载到内存)的程序就叫做一个进程,或者称一个在内存中的程序就被称为进程。进程和程序相比具有动态属性

我们知道,计算机系统为了提高整个计算机的效率,在数据的层面,CPU不会直接和外设打交道,而只会和内存进行交互,同样,外设也只会和内存交互;此外,我们编写的C/C++代码经过编译之后形成的二进制可执行程序在本质上就是一个存放在磁盘上的一个文件,所以当我们需要运行这个程序的时候,就必须先将这个文件加载到内存,这是因为CPU需要从内存中读取程序中的代码和数据进行运算(算数运算和逻辑运算)

在我们的程序加载到内存之后,操作系统需要对程序进行管理,而操作系统对程序的管理本质上是对数据的管理,管理方法为先描述,再组织,所以操作系统会将这个程序的属性用一个结构体来进行表示,然后为每一个进程都创建一个结构体对象,最后再将所有的结构体对象使用某一种数据结构组织起来,比如链表,此时,操作系统对进程的管理就变成了堆数据结构的管理的某个节点的管理,在操作系统中,这个用于描述和组织进程的数据结构被称为进程控制块-PCB

2.进程的描述-PCB

进程控制块PCB(process control block):操作系统中用于描述进程的工具,进程信息被放在进程控制块的数据结构中,可以理解为进程属性的集合;Linux操作系统下的PCB是: task_struct,task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_ struct内容分类

标示符: 描述本进程的唯一标示符,用来区别其他进程。

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

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

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

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

上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。

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

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

其他信息

task_struct可以使用下面的结构体来表示:

struct task_struct
{// 进程的所有属性// 进程对应的代码和数据的地址// 写一个进程的地址struct task_struct* next;
}

Linux中task_struct的部分源码如下:

在这里插入图片描述

我们了解task_struct之后,我们可以得出这样的结论:进程=内核数据结构(task_struct)+进程对应的磁盘代码

二、进程相关的基本操作

1.组织进程

所有运行在系统里的进程都以task_struct链表的形式存在内核里。

在Linux中,进程是由操作系统调度和管理的执行中程序。每个进程都被分配了一个唯一的PID(进程标识符),并且可以包含多个线程。

Linux通过使用进程控制块(PCB)来组织进程。 PCB是一个数据结构,用于存储有关进程状态的信息,如进程ID,优先级,内存映像和打开文件等。 Linux将它们链接成单向链表以跟踪所有处于活动状态的进程。此外,还有其他几个数据结构,如等待队列、可用内存空间和保护时钟中断处理程序等,用于在进程执行过程中监视和管理资源。

总之,在Linux中,操作系统通过组织进程控制块来进行进程调度,使得可以对其进行有效的管理和监视

2.查看进程

我们可以通过下面两种方式来查看进程:

1.ps axj 指令配合grep 和管道查看指定进程

#include <stdio.h>
#include <unistd.h>int main()
{while(1){printf("我是一个进程\n");sleep(1);}return 0;
}

在这里插入图片描述

2.在"/proc"系统文件夹中查看所有进程

在这里插入图片描述

3.结束进程

对于我们自己编写的普通进程来说,我们可以使用[ctrl+c]来结束,也可以使用kill指令,指定-9选择来结束:

在这里插入图片描述

4.通过系统调用获取进程标示符

我们可以使用操作系统给我们提供的系统调用接口 getpid() 与 getppid() 来获取进程id和父进程id(进程id是一个进程的唯一标识)

我们可以通过man指令来查看这两个接口的用法:

注:对于函数的返回值pid_t,我们把它当做int即可,打印进程id的时候也使用%d

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{while(1){printf("我是一个进程,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);}return 0;
}

在这里插入图片描述

我们可以看到,我们通过getpid() 和gitppid() 函数得到的值确实是我们进程对应的id,同时,我们发现myproc的父进程是bash,即shell外壳,所以我们可以得出这样的结论shell为了防止自身崩溃,并不会自己去执行指令,而是会派生子进程去执行

另外,同一个程序重新被运行时它的进程id之后它的id和之前就可能不一样了,这是因为它的代码和数据需要重新从磁盘中加载到内存中,但是它的父进程的id不会改变,因为他们都是通过bash来完成的,需要改变的话,退出重新启动服务器即可

在这里插入图片描述

5.通过系统调用创建进程-fork初识

我们可以使用系统调用接口fork来创建子进程:

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{pid_t id = fork();if(id==0){// 子进程while(1){printf("子进程:pid:%d,ppid:%d,id:%d\n",getpid(),getppid(),id);sleep(1);}}else if(id > 0){// 父进程printf("父进程:pid:%d,ppid:%d,id:%d\n",getpid(),getppid(),id);sleep(2);}else{perror("fork mail");return 1;}
}

在这里插入图片描述

我们可以看到,子进程的id为0,ppid是父进程的id,父进程的id是子进程的pid,父进程的ppid是bash,即对于父进程,fork函数返回的是子进程的id,对于子进程,fork返回0

三、进程状态

1.普遍操作系统层面的进程状态

在普遍的操作系统的层面,即站在操作系统学科的角度来说,进程的状态可能有如下几种:运行,新建,就绪,挂起,阻塞,等待,停止,挂机,死亡;其中我们最为重要的是理解以下这三种状态:运行阻塞挂起

或许我们只是听过上面我们所说的进程状态,但是什么是进程状态呢,我们知道,当一个程序被加载到内存变成进行之后,操作系统就需要对该进程进行管理,即为该进程创建PCB对象,而进程状态的本质就是PCB内部的一个属性,用一个整形变量来表示,不同的整形变量就对应不同的进程状态

运行状态

我们知道,程序被加载到内存之后就变成了进程,但是我们可能需要有多个进程需要等待执行,而一般我们的CPU只有一个,而我们的外设资源(硬件)的个数也是远小于进程的数量,此时,操作系统为了合理的分配CPU以及各种硬件资源的分配和更好的调度各个进程,就会为CPU创建一个运行队列,为每一个硬件创建一个等待队列,而让某一个进程处于运行状态的本质就是将该进程的PCB放入到CPU的运行队列中,然后将PCB维护进程状态的变量值修改为相应的值,比如0;

进程PCB里面有进程的各种属性,以及进程对应的代码和数据的地址,所以CPU从运行队列中取出PCB之后 ,就可以根据PCB来得到进程的各种数据和指令,然后执行相应的运算,所以进程处于运行状态并不意味着该进程此刻正在被运行,只要该进程处于CPU的运行队列中我们就称为该进程处于运行状态,这是因为CPU是纳秒级的芯片,运算的速度非常的快,所以只要进程处于CPU的运行队列中,我们就可以认为该进程处于运行状态

阻塞状态

我们知道,计算机的各种硬件资源也是有限的,但是需要使用这些硬件资源的进程却有很多,在同一个时刻硬件只能为一个进程服务,比如同一时刻只能有一个进程向磁盘写入数据,所以为了解决这个问题,当有多个进程需要使用同一个硬件的时候,操作系统会将进程的PCB放入硬件的等待队列中,等待硬件为其服务。进程在等待队列中等待硬件的状态称为阻塞状态,阻塞状态本质上就是将进程的PCB从CPU的运行队列剥离出来,放入硬件的等待队列中,然后将PCB中维护进程状态的变量修改为对应的值,比如1;待该进程获得对应的硬件资源之后,再将该进程放入CPU的运行队列中。此外,并不是只有等待硬件资源进程才会处于阻塞状态,一个进程等待另一个进程就绪,一个进程等待某种软件资源就绪都会处于阻塞状态

挂起状态

我们已经知道,阻塞状态的进程需要等待某种资源,所以它对应的代码和数据在短期内并不会被执行,此时他们扔存在内存中就相当于浪费了内存资源,但是如果当前操作系统处于高IO的时候,就会使得很多进程都在等待外设资源,可能就会导致内存不足,此时,操作系统就会选择将某些处于阻塞状态的进程对应的代码和数据拷贝到磁盘中,然后释放内存中的代码,从而将对应的空间释放出来,让其他的程序进入到内存中让CPU为其执行

所以挂起状态就可以理解为当内存空间不足时,操作系统将在等待资源的进程对应的代码和数据放大磁盘中的状态就称为挂起状态,挂起状态不会移动进程的PCB,只会移动该进程对应的代码和数据。挂起状态不是释放进程,因为该进程对应的PCB仍然处在某种硬件的等待队列之中,当该进行获得对应的资源之后,操作系统就会将该进程的代码和数据从磁盘中加载到内存中继续执行,这个过程的本质就是对内存数据的换入换出,同时阻塞不一定挂起,挂起也不一定是阻塞,也可能是新建挂起,就绪挂起,甚至是运行挂起

【总结】

1.一个CPU有一个运行队列

2.让进程 入队列,本质是将该进程的task_struct结构体对象放入运行队列中

3.进程PCB在runqueue,就是运行状态®,不是这个进程正在运行,才是运行状态,状态是进程内部的一个属性,保存在task_struct中,用一个整形变量来进行表示

4.不要以为你的进程只会等待(占用)CPU资源,你的进程,也随时随地需要外设资源

5.所谓的进程的不同状态,本质是进程在不同的队列中,等待某种资源

2.Linux操作系统的进程状态

上面我们所讲述的是普遍操作系统中进程的状态,下面我们学习具体操作系统Linux的进程的状态:

Linux内核源码中对进程状态的定义如下:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

我们可以看到,在Linux中,进程一共有7种状态,分别为运行,浅度睡眠,深度睡眠(磁盘休眠),暂停,追踪暂停,死亡和僵尸状态

运行状态®

运作状态即进程的PCB位于CPU的运行队列中:

在这里插入图片描述

睡眠状态(S)

Linux下的睡眠状态其实就相当于阻塞状态,进程需要等待某种资源:
在这里插入图片描述

在这里插入图片描述

我们使用ps axj 指令查看进程状态只能查看进程的某一时刻的状态,而外设的速度是远低于CPU的,所以我们就会发现,myprocess也在执行加法运算,但是我们每次查询时都处于阻塞状态,因为99%的时间都在等待硬件资源就绪,只有1%的时间在进行加法运算以及执行打印,所以我们查询的结果基本都是睡眠状态

暂停状态(t)

暂停状态也是阻塞状态的一种,我们可以使用kill 命令,使用-19选项来让一个进程从一个状态变为暂停状态:

在这里插入图片描述

我们也可以使用kill -18 让一个进程处于暂停状态回复为原来的状态

在这里插入图片描述

但是我们发现,我们将myprocess暂停或者continue之后,进程状态前面的+号消失了,事实上,进程状态后面的+号代表着一个进程是前台进程,没有+号就代表该进程是一个后台进程,对于前台进程我们可以使用ctrl+c将其终止,也可以使用kill命令来完成,但是对于后台进程,我们只能通过kill命令来完成:

在这里插入图片描述

追踪暂停状态(t)

追踪暂停状态是一种特殊的暂停状态,进程处于次状态表示该进程正在被追踪,比如我们使用gdb调试进程:

在这里插入图片描述

深度睡眠状态(D)

我们知道,当我们的内容不足的时候,操作系统会将一部分进程挂起来节省资源,但是如果内存空间严重不足,挂起已经解决不了问题的时候,操作系统就会自动杀死一些进程

我们以一个例子来说明:当前有一个进程需要向磁盘写入一些数据,这些数据是10万名用户近半年来的转账记录,非常的重要,该进程访问磁盘,让磁盘将这些数据写入磁盘,但是在磁盘写入这些数据期间,该进程属于阻塞状态,因为它要等待磁盘给他返回一个结果,即是否将这些数据写入成功,但是此时,内容严重不足,操作系统将该进程杀掉了,磁盘此时写入失败了,告诉该进程的时候,进程却无法回应,因为该进程已经被杀掉,磁盘只能将这些数据丢弃然后为其他进程提供服务,此时,这些数据就丢弃了

为了防止这种情况的发生,Linux操作系统就设计出了一个深度睡眠(D)状态,处于深度睡眠状态的进程既不能被用户杀掉,也不能被操作系统杀掉,只能通过断电,或者等待进程自己醒来,我们需要注意的是,深度睡眠一般只会发生在高IO的情况下,且如果操作系统通过杀掉进程也无法解决问题的时候,此时操作系统也即将崩溃了

死亡状态(X)

死亡状态代表着一个进程结束运行,该进行对应的PCB以及代码和数据全部都被操作系统回收

僵尸状态(Z)

我们创建一个进程的目的是为了让其为我们完成某种任务,既然是完成任务,那么就应该在进程结束前告诉返回任务的执行结果,供父进程或者操作系统进行读取,所以当一个进程退出的时候,不能立即释放该进程的全部资源,其中对于进程的代码和数据来说,操作系统可以直接将其释放,因为该进程已经退出,不会再被执行了,但是该进程的PCB会被保留,这是因为PCB中存放着该进程的各种状态的代码,特别是退出状态的代码

我们以一个简单的例子来更深入的理解:假如你在操场上跑步,你前面跑步的一个人突然倒下,你上去发现他没有了呼吸,此时你就打电话报警,警察来了之后发现这个人确实已经死亡,但是警察不会立即安排该死者的家属安排后事,而是会通过法医对他进行检查,判断他是因为什么原因死亡,是过度劳累,还是他人谋害,确定死亡原因之后才会通知家属处理后事

上述中跑步的那个人就相当于一个进程,警察就相当于操作系统或者父进程,在进程结束后会对其进行检查

所以僵尸状态就是进程在退出时等待父进程或者操作系统来读取退出状态代码,然后再释放PCB的一种状态

【总结】

我们可以看到,具体的Linux操作系统的进程状态和普遍的操作系统学科上的进程状态是不一定相同的,比如Linux操作系统中没有阻塞和挂起状态,阻塞状态而是通过浅度睡眠,深度睡眠,暂停,追踪暂停等状态来表现出来,而进程处于这些状态时是否会被挂起,我们是不知道的,对于用户来说,操作系统没有必要将挂起状态暴露出来,因为用户不关心一个进程是否处于挂起状态

四、两种特殊的进程状态

1.僵尸进程

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程,僵死进程会以终止状态保持在进程表中,该进程的PCB就一直得不到释放并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

进程退出的时候,不能立即释放该进程对应的资源,需要保存一段时候,让父进程或操作系统来读取

我们通过下面的代码进行说明:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int main()
{pid_t id=fork();if(id == 0){while(1){printf("我是子进程,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);}}else if(id > 0){while(1){printf("我是父进程,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);}}else{perror("fork fail");exit(-1);}return 0;
}

在这里插入图片描述

我们可以看到,当我们kill子进程之后,由于父进程中还没有对子进程的退出状态进行读取,所在子进程变成了Z状态,并且子进程还提示了defunct(失效的,不再使用的),此时,如果父进程一直不对子进程读取,那么子进程就会变成僵尸状态

僵尸进程危害

1.占用系统资源:维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护僵尸进程占用系统资源,导致系统运行缓慢,甚至崩溃。

2.安全漏洞:僵尸进程可能成为黑客攻击的入口,从而导致系统被入侵,数据泄露等安全问题。

3.影响系统性能:进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。僵尸进程会消耗系统的资源,导致系统性能下降,从而影响用户的使用体验。

4.影响系统稳定性:一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费。因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间僵尸进程会占用程序的资源,导致程序无法正常运行,从而影响用户的使用体验。

2.孤儿进程

父进程先退出,子进程就称之为“孤儿进程“,此时孤儿进程就会被1号init进程领养

在这里插入图片描述

在这里插入图片描述

我们可以发现,父进程退出之后并没有变成Z状态,因为父进程的父进程是bash,bash会读取父进程的退出状态,子进程被领养后变成了后台进程

【注意】

1.父进程先退出的情况一定存在

2.子进程会被操作系统领养–1号进程

3.操作系统为什么要领养?如果不领养,那么子进程退出的时候,对应的僵尸,就没有人能回收了

4.被领养的进程–孤儿进程

5.如果是前台进程创建的进程,如果孤儿了,会自动变成后台进程

五、进程优先级

1.什么是优先级

我们首先要和权限进行区分,权限决定的是一件事能不能被执行,而优先级是在能被执行的情况下,这件事情是先做还是后做的问题

cpu资源分配的先后顺序,就是指进程的优先权(priority)。优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能

2.为什么要有优先级

在我们的计算机系统是有限的,比如CPU只有一个或者多个,但始终是小于进程的数量,硬件如显示器,磁盘,显卡等等都只有一个,而内存中很多进程都要占用资源,所以我们需要指定优先级来合理的分配资源

3.Linux中优先级的特点

Linux中优先级的表示和维护通过两个变量PRI(priority)和NI(nice)来完成,每个进程的默认PRI为80,NI为0,我们 可以通过修改NI的值来调整进程的优先级的大小,NI的变动范围为[-20,19],PRI与NI的值越小,那么进程的优先级就越高

在Linux系统中,我们可以通过ps –l命令来查看进程的优先级:

在这里插入图片描述

对于ps -l的其他信息如下:

UID : 代表执行者的身份

PID : 代表这个进程的代号

PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

PRI :代表这个进程可被执行的优先级,其值越小越早被执行

NI :代表这个进程的nice值

我们可以使用top命令更改已存在进程的nice:

top  进入top后按“r”–>输入进程PID–>输入nice值

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

最终的优先级=老的优先级+nice值(老的优先级为80)

六、进程的其他概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级

独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰(每个进程的代码数据和PCB都是独立的,一个进程的死亡不会影响其他进程,包括父子进程,子进程崩溃不会影响父进程)

并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

七、进程切换

一般的电脑只有一个CPU,那么在同一个时刻就只有一个进程正在被运行,但是,我们在使用电脑的时候,在同一个时间会运行多个程序,并且这些程序都能够正常的运行,这依靠的是进程转换

进程转换:CPU同一时刻只能运行一个进程,但是CPU的速度很快,所以位于CPU运行队列中的每一个进程都只运行一个时间片,每个进程运行完一个时间片后就会被放到运行队列的尾部或者可能退出,在运行队列中的进程就等待下一次的运行,这样使得在一个时间段内有多个进程都能被运行

CPU在进行进程切换的时候需要进行上下文保护和上下文恢复

我们的进程在运行时会产生非常多的临时文件,同时CPU中存在一套寄存器硬件,当程序进行时,进程的PCB会被放入CPU内的 寄存器中,此时CPU就可以通过PCB得到进程代码和数据的地址,CPU在运行进程时产生的大量的临时数据也会被保存到寄存器中,所以我们在进行进程切换的时候需要进行程序的上下文保护和上下文恢复,即程序停止运行时会将寄存器里面的数据保存起来,进程重新运行时将保存是数据重新放入到寄存器中,以便我们能够接着上次运行的地方接着运行

我们举一个简单的例子来更好的理解:

我们在大学的时候可以选择参加入伍,我们入伍的时候需要告诉学校,保留我们的学籍,否则等我们回来的时候可能就被退学了,这就是上下文保护;我们回来也不是直接去上课,而是需要向学校进行回报,将我们的学籍处理好,继续进行学习,这就是上下文恢复

【总结】

CPU寄存器硬件被所有的进程共享,但是当CPU运行某一具体的进程的时候,CPU寄存器的数据只属于该进程,同时,我们进行上下文保护时保存的是寄存器中的数据,而不是寄存器硬件

进程在切换的时候,要进行进程的上下文保护

当进程在恢复运行的时候,要进行进程的上下文恢复

在任何时候,CPU里面的寄存器里面的数据,看起来是在大家都看得见的寄存器上,但是,寄存内的数据,只属于当前运行的进程,寄存器被所有进程所共享,寄存器内的数据,是每个进程各自私有的–上下文数据

要执行一个程序(指令),先找到这个程序 ./myprocess -> ./ -> 当前路径 ->找到程序

相关文章:

【Linux】进程概念与进程状态

文章目录 一、进程概念1.进程的概念2.进程的描述-PCB 二、进程相关的基本操作1.组织进程2.查看进程3.结束进程4.通过系统调用获取进程标示符5.通过系统调用创建进程-fork初识 三、进程状态1.普遍操作系统层面的进程状态2.Linux操作系统的进程状态 四、两种特殊的进程状态1.僵尸…...

解决安装nvm以后windows cmd无法找到npm/yarn命令的问题

安装了nodejs多版本管理工具nvm以后&#xff0c;会出现windows cmd无法找到npm/yarn命令的问题 只要一运行npm/yarn就会提示&#xff1a;不是内部命令&#xff0c;找不到运行路径之类的。 解决办法&#xff1a;首先打开windows环境变量的配置&#xff0c;查看NVM_SYMLINK指向…...

深入解析Java正则表达式:定义、原理和实例

1.前言 1.1简介 正则表达式在Java开发中扮演着重要的角色。本文将详细讲解Java正则表达式的定义、工作原理&#xff0c;并提供一些实例和示例代码&#xff0c;帮助读者更好地理解和应用正则表达式 1.2使用场景的介绍 正则表达式适用于许多问题和场景&#xff0c;包括但不限于…...

DatenLord前沿技术分享 No.38

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;通过软硬件深度融合的方式打通云云壁垒&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。在本周…...

ms-sql server sql 把逗号分隔的字符串分开

案例&#xff1a; sql 查询-字段里是逗号,分隔开的数组&#xff0c;查询匹配数据 sql 查询-字段里是逗号,分隔开的数组&#xff0c;查询匹配数据_sql server 数组匹配-CSDN博客 SQL SERVER 把逗号隔开的字符串拆分成行 SQL SERVER 把逗号隔开的字符串拆分成行_sqlserver拆分…...

零基础制作预约小程序,微信小程序预约服务指南

随着互联网的发展&#xff0c;越来越多的服务开始转移到线上。预约服务也是其中之一。通过微信小程序&#xff0c;商家可以提供更加便捷的预约服务&#xff0c;让客户随时随地预约商品或服务。本文将介绍如何零基础制作预约小程序&#xff0c;包括使用第三方制作平台、选择合适…...

算法---交替合并字符串

题目 给你两个字符串 word1 和 word2 。请你从 word1 开始&#xff0c;通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长&#xff0c;就将多出来的字母追加到合并后字符串的末尾。 返回 合并后的字符串 。 示例 1&#xff1a; 输入&#xff1a;word1 “abc”…...

下载运行ps软件提示因为计算机中丢失d3dcompiler_47.dll解决方法

在计算机系统中&#xff0c;DLL文件&#xff08;动态链接库&#xff09;是一种重要的共享库&#xff0c;它包含了可被多个程序使用的代码和数据。然而&#xff0c;当某些DLL文件丢失或损坏时&#xff0c;可能会导致程序无法正常运行。本文将介绍四种解决D3DCompiler_47.dll缺失…...

Flutter Image组件如何处理图片加载过程中的错误?

在Flutter中&#xff0c;Image组件可以通过监听加载过程中的错误来处理图片加载过程中的错误。 新一代ChatGpt智能助手 文客微创 具体来说&#xff0c;可以使用Image.network或Image.asset方法加载图片&#xff0c;并使用Builder模式来监听图片加载过程中的状态。 例如&…...

在mysql8查询中使用ORDER BY结合LIMIT时,分页查询时出现后一页的数据重复前一页的部分数据。

这里写目录标题 问题描述&#xff1a;问题模拟&#xff1a;原因分析问题解释问题解决验证官方文档支持 问题描述&#xff1a; 在mysql8查询中使用ORDER BY结合LIMIT时&#xff0c;分页查询时出现后一页的数据重复前一页的部分数据。 问题模拟&#xff1a; 表table_lock_test&…...

【SA8295P 源码分析 (三)】97 - QNX AIS Camera 框架介绍 及 Camera 工作流程分析

【SA8295P 源码分析】97 - QNX AIS Camera 框架介绍 及 Camera 工作流程分析 一、QNX AIS Server 框架分析二、QNX Hypervisor / Android GVM 方案介绍三、Camera APP 调用流程分析四、QCarCam 状态转换过程介绍五、Camera 加串-解串 硬件链路分析六、摄像头初始化检测过程介绍…...

基于epoll封装非阻塞的reactor框架(附源码)

C++常用功能源码系列 文章目录 C++常用功能源码系列前言一、reactor架构二、client端reactor代码三、server端reactor代码四、单reactor架构可以实现百万并发总结前言 本文是C/C++常用功能代码封装专栏的导航贴。部分来源于实战项目中的部分功能提炼,希望能够达到你在自己的项…...

安装Git和git命令使用

文章目录 安装Git创建版本库版本回退工作区和暂存区管理修改撤销修改 安装Git 在Windows上安装Git 在Windows上使用Git&#xff0c;可以从Git官网直接下载安装程序&#xff0c;然后按默认选项安装即可。 安装完成后&#xff0c;在开始菜单里找到“Git”->“Git Bash”&…...

【SA8295P 源码分析 (四)】65 - emac0-phy 与 emac1-switch兼容 方案实现

【SA8295P 源码分析】65 - emac0-phy 与 emac1-switch兼容 方案实现 系列文章汇总见:《【SA8295P 源码分析 (四)】网络模块 文章链接汇总 - 持续更新中》 本文链接:《【SA8295P 源码分析 (四)】65 - emac0-phy 与 emac1-switch兼容 方案实现》 本文在前文《【SA8295P 源码分析…...

SpringSecurity源码学习二:异常处理

目录 1. 原理2. 组件3. ExceptionTranslationFilter3.1 默认过滤器顺序3.2 ExceptionTranslationFilter源码3.2.1 AuthenticationException异常3.2.2 AccessDeniedException异常 总结 1. 原理 Spring Security 异常处理的原理是通过一系列的异常处理器来处理在安全验证和授权过…...

代码随想录算法训练营第23期day28|491.递增子序列 46.全排列 47.全排列 II

目录 一、&#xff08;leetcode 491&#xff09;递增子序列 二、&#xff08;leetcode 46&#xff09;全排列 三、&#xff08;leetcode 47&#xff09;全排列 II 一、&#xff08;leetcode 491&#xff09;递增子序列 力扣题目链接 状态&#xff1a;去重方法错误。 这道题…...

ubuntu磁盘扩容

1、参考链接&#xff1a; https://blog.csdn.net/qq_43265072/article/details/112312223 2、尝试过程中小心翼翼&#xff0c;生怕待会系统崩掉&#xff0c;要重装。。。 不过呢有撤销和提交按钮就非常贴心&#xff0c;如果稍有不慎就一路回撤就好啦 说一下怎么移动空间&…...

C/S架构学习之使用select实现TCP小型并发服务器

select实现TCP小型并发服务器的流程&#xff1a;一、创建套接字&#xff08;socket函数&#xff09;&#xff1a;通信域选择IPV4网络协议、套接字类型选择流式&#xff1b; int sockfd socket(AF_INET,SOCK_STREAM,0); //通信域选择IPV4、套接字类型选择流式二、填充服务器的网…...

公司注册类型分类标准是怎样的

公司法上的分支机构、分公司、子公司是什么 - 公司法 (一)以公司股东的责任范围为标准分类 以公司股东的责任范围为标准&#xff0c;亦即以公司股东是否对公司债务承担责任为标准&#xff0c;可将公司分为无限责任公司、两合公司、股份两合公司、股份有限公司和有限责任公司。…...

5.MidBook项目经验之MongoDB,Nacos,网关

1.医院查询接口 //系统1(signsignMD5加密后) ----> 系统2(数据库signMD5加密 相对比),好处在于网络之间传输不会得到直接得到sign 2.上传和删除科室信息 //map转jsonString,然后再转为对象//保存需要查数据库是否存在,存在修改,不存在添加//接口的包引入不对导致调用引包错误…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...