linux 进程及调度基础知识

news2024/11/17 11:44:08

引用

  • Linux进程管理专题

  • Linux进程管理与调度-之-目录导航

  • Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)

  • 蜗窝科技-进程管理

  • 郭健: Linux进程调度技术的前世今生之“前世”

  • 郭健: Linux进程调度技术的前世今生之“今生”

  • 宋宝华:Linux的任督二脉——进程调度和内存管理

  • 深度讲解Linux内存管理和Linux进程调度-打通任督二脉

  • 宋宝华: Linux僵尸进程可以被“杀死”吗?

  • 宋宝华: 聊一聊进程深度睡眠的TASK_KILLABLE这个状态

  • 宋宝华: 关于Linux进程优先级数字混乱的彻底澄清

  • Linux进程优先级和nice值

  • Linux的进程线程及调度

  • Linux内核学习笔记(6)-- 进程优先级详解(prio、static_prio、normal_prio、rt_priority)

  • 调度器简介,以及Linux的调度策略

  • 理解Linux内核抢占模型(最透彻一篇)

  • 灵魂拷问之调度与切换十六问

  • 进程调度

  • 内核基础设施——per cpu变量 - Notes about linux and my work (laoqinren.net)

  • #Linux进程管理 (qq.com)


一. 进程基础知识

1.1进程

是资源的封装,是处于执行期的程序以及它所管理的资源(如 打开的文件,挂起的信号,进程状态,地址空间等)的总称。

PCB(Processing Control Block) 来描述,在linux中,用 struct task_struct 结构体来描述。

pid的数量是有限的,为 32768 (cat /proc/sys/kernel/pid_max)

1.2 线程

是调度单位。用struct thread_info(e.g. arm64)来描述 (线程描述符),该结构和进程的内核栈stack存放在同一个单独为进程分配的内存区域。(由于这个内存区域同时保存了thread_info和stack,所以,使用了union 来定义。)

  • 为什么需要thread_info?

内核需要存储每个进程的PCB信息, linux内核是支持不同体系的的, 但是不同的体系结构可能进程需要存储的信息不尽相同, 这就需要我们实现一种通用的方式, 我们将体系结构相关的部分和无关的部门进行分离。

用一种通用的方式来描述进程, 这就是struct task_struct, 而thread_info就保存了特定体系结构的汇编代码段需要访问的那部分进程的数据。

进程最常用的是进程描述符结构task_struct,而不是thread_info结构的地址。为了获取当前CPU上运行进程的task_struct 结构,内核提供了current 宏。

union thread_union {
    //x86: CONFIG_ARCH_TASK_STRUCT_ON_STACK is not set.
#ifndef CONFIG_ARCH_TASK_STRUCT_ON_STACK
    struct task_struct task;
#endif
    //x86,arm64: CONFIG_THREAD_INFO_IN_TASK=y
#ifndef CONFIG_THREAD_INFO_IN_TASK
    struct thread_info thread_info;
#endif

    //x86: 8k, x86_64: 16k
    //arm: 8k, arm64: 16k
    //必须是8192的整数倍。
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};

1.3 进程内核栈

  • 为什么需要内核栈?

  • 因为进程在内核态运行时,需要保持自己的栈信息。该栈不同于用户态的进程所用的栈。

用户态进程所用的栈是在进程线性地址空间中。

  • 进程内核栈除了需要保存内核空间过程调用外,还需要保存用户空间栈的数据和返回地址,以便 在返回用户空间继续执行。

  • 进程通过syscall陷入内核时进行栈切换。

1.4 内核栈stack, struct thread_info, struct task_struct的关系:

  • 经典关系 (CONFIG_THREAD_INFO_IN_TASK=n)

  • 将thread_info存放在内核栈stack中。

  • 在thread_info中保存task_struct指针。

  • 新版关系(CONFIG_THREAD_INFO_IN_TASK=y)

  • 将thread_info放到task_struct中。

  • 如何通过current宏找到当前的task_struct?

  1. 传统做法

通过寄存器(x86的esp寄存器, arm的sp栈栈顶寄存器)找到当前进程的内核栈顶,然后,找到thread_info,然后thread_info中保存了task_struct的指针,即就可以拿到当前进程的PCB了。

  1. 新做法(更高效)

  • x86

使用了current_task这个每CPU变量,来存储当前正在使用的cpu的进程描述符struct task_struct。x86上通用寄存器有限,无法像ARM中那样单独拿出寄存器来存储进程描述符task_sturct结构的地址。由于采用了每cpu变量current_task来保存当前运行进程的task_struct,所以在进程切换时,就需要更新该变量。在arch/x86/kernel/process_64.c文件中的__switch_to函数中有如下代码来更新此全局变量:

  • arm64:

会通过保存到寄存器(sp_el0)中,直接使用。

1.2 进程的各种状态和生命周期

  • 上图中左侧为操作系统中通俗的进程三状态模型,右侧为Linux对应的进程状态切换。每一个标志描述了进程的当前状态,这些状态都是互斥的;

  • Linux中的 就绪态 运行态 对应的都是TASK_RUNNING 标志位,就绪态 表示进程正处在队列中,尚未被调度;运行态 则表示进程正在CPU上运行;

5个互斥状态

state域能够取5个互为排斥的值(通俗一点就是这五个值任意两个不能一起使用,只能单独使用)。系统中的每个进程都必然处于以上所列进程状态中的一种。

状态

描述

TASK_RUNNING

表示进程要么正在执行,要么正要准备执行(已经就绪),正在等待cpu时间片的调度

TASK_INTERRUPTIBLE

进程因为等待一些条件而被挂起(阻塞)而所处的状态。这些条件主要包括:硬中断、资源、一些信号……,一旦等待的条件成立,进程就会从该状态(阻塞)迅速转化成为就绪状态TASK_RUNNING

TASK_UNINTERRUPTIBLE

意义与TASK_INTERRUPTIBLE类似,除了不能通过接受一个信号来唤醒以外,对于处于TASK_UNINTERRUPIBLE状态的进程,哪怕我们传递一个信号或者有一个外部中断都不能唤醒他们。只有它所等待的资源可用的时候,他才会被唤醒。这个标志很少用,但是并不代表没有任何用处,其实他的作用非常大,特别是对于驱动刺探相关的硬件过程很重要,这个刺探过程不能被一些其他的东西给中断,否则就会让进城进入不可预测的状态

TASK_STOPPED

进程被停止执行,当进程接收到SIGSTOP、SIGTTIN、SIGTSTP或者SIGTTOU信号之后就会进入该状态

TASK_TRACED

表示进程被debugger等进程监视,进程执行被调试程序所停止,当一个进程被另外的进程所监视,每一个信号都会让进城进入该状态

2个终止状态

两个附加的进程状态既可以被添加到state域中,又可以被添加到exit_state域中。只有当进程终止的时候,才会达到这两种状态.

状态

描述

EXIT_ZOMBIE

进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息,此时进程成为僵尸进程

EXIT_DEAD

进程的最终状态

TASK_KILLABLE

Linux 中的新进程状态(TASK_UNINTERRUPTIBLE + TASK_WAKEKILL = TASK_KILLABLE)

状态

描述

TASK_KILLABLE

当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE,只不过可以响应致命信号

1.3 进程地址空间

可用pmap查看

1.4 内核线程

  • 用ps查看线程时,名字为 [..] 这样的线程,都是内核线程。例如:中断线程化使用的irq内核线程;软中断使用的内核线程ksoftirqd;以及work使用的kworker内核线程。

  • 内核线程没有地址空间,所以task_struct->mm指针为NULL。

  • 内核线程没有用户上下文。

  • 内核线程只工作在内核空间,不会切换至用户空间。但内核线程同样是可调度且可抢占的。普通线程即可工作在内核空间,也可工作在用户空间。

  • 内核线程只能访问3GB以上内核地址空间,而普通线程可访问所有4GB地址空间。

常见内核线程

prio

policy

irq

49

SCHED_FIFO

softirq

120

SCHED_NORMAL

worker

120

SCHED_NORMAL

init

120

SCHED_NORMAL

kthreadd

120

SCHED_NORMAL

cfinteractive

0

SCHED_FIFO

  • 中断内核线程优先级很高,为49,并且使用了实时调度策略。softirq和worker都是普通内核线程。

  • init_workqueues中创建了绑定CPU0的两个kworker_pool,分别是nice=0和nice=-20。apply_workqueue_attrs创建unbund worker_pool,即kworker/uX:0,也有两个attr,分别是nice=0和nice=-20

  • 其它特殊内核线程init优先级为120,kthreadd优先级为120.

  • cfinteractive优先级最高,主要处理CPU Frequency负载更新。

1.4.1几个特殊的内核进程

1.4.2内核线程相关API

  • kernel_thread()

kernel_thread接口,使用该接口创建的线程,必须在该线程中调用daemonize()函数,这是因为只有当线程的父进程指向”Kthreadd”时,该线程才算是内核线程,而恰好daemonize()函数主要工作便是将该线程的父进程改成“kthreadd”内核线程;默认情况下,调用deamonize()后,会阻塞所有信号,如果想操作某个信号可以调用allow_signal()函数。

  • kthread_create()

kthread_create接口,则是标准的内核线程创建接口,只须调用该接口便可创建内核线程;默认创建的线程是存于不可运行的状态,所以需要在父进程中通过调用wake_up_process()函数来启动该线程。

  • kthread_run()

创建并启动线程的函数; 线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。

  • kthread_should_stop(), kthread_stop()

停止线程

  • kthread_should_park(), kthread_parkme(), kthread_park(),kthread_unpark()

当在其他某个地方,调用 kthread_park(practice_task_p)后,线程将在kthread_parkme()处挂起睡眠,直到其他某个地方执行了kthread_unpark(practice_task_p)后,线程才被唤起,继续执行。

  • do_exit()

当线程执行到函数末尾时会自动调用内核中do_exit()函数来退出或其他线程调用kthread_stop()来指定线程退出。

1.6 抢占

使用抢占式内核可以保证系统响应时间. 最高优先级的任务一旦就绪, 总能得到CPU的使用权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态, 当前任务的CPU使用权就会被剥夺,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的那个任务开始运行。

缺点

不能直接使用不可重入型函数。(即需要考虑高低优先级线程之间相关数据的竞态情况,需要加锁保护)

1.6.1 抢占触发点

  • 不管是用户抢占还是内核抢占,抢占触发点是一样的。

  • 内核提供了set_tsk_need_resched() 函数来将 thread_infoflag字段设置成TIF_NEED_RESCHED

  • 设置了TIF_NEED_RESCHED 标志,表明需要发生抢占调度;

1

信号量、等到队列、completion等机制唤醒时都是基于waitqueue的,而waitqueue的唤醒函数为default_wake_function(),其调用try_to_wake_up() 将被唤醒的任务更改为就绪状态并设置 need_resched 标志。

2

时钟中断处理例程检查当前任务的时间片,当任务的时间片消耗完时,scheduler_tick() 函数就会设置 need_resched 标志;

3

新建一个任务时,可能会使高优先级的任务进入就绪状态;

4

对CPU(SMP)进行负载均衡时,当前任务可能需要放到另外一个CPU上运行

5

设置用户进程的nice值时,可能会使高优先级的任务进入就绪状态;

6

改变任务的优先级时,可能会使高优先级的任务进入就绪状态;

1.6.2 抢占执行点

  • 用户抢占

抢占执行发生在进程处于用户态。抢占的执行,最明显的标志就是调用了schedule() 函数,来完成任务的切换。具体来说,在用户态执行抢占在以下几种情况:

  • 异常处理后返回到用户态;

  • 中断处理后返回到用户态;

  • 系统调用后返回到用户态;

  • 内核抢占

抢占执行发生在进程处于内核态。

  • Linux内核有三种内核抢占模型:
  • CONFIG_PREEMPT_NONE

不支持内核抢占,中断退出后,需要等到低优先级任务主动让出CPU才发生抢占切换;

一般服务器选择这种策略;

  • CONFIG_PREEMPT_VOLUNTARY

支持内核自愿抢占,即在一些耗时的routine中增加抢占点(提高实时性),在中断退出后遇到抢占点时进行抢占切换;

一般桌面系统选择这种策略;

  • CONFIG_PREEMPT

支持内核抢占,当中断退出后,如果遇到了更高优先级的任务,立即进行任务抢占;

有实时响应的内核选择这种策略;

  • 抢占执行点
  • 中断执行完毕后进行抢占调度;

  • 主动调用preemp_enable() schedule() 等接口的地方进行抢占调度;

二. 进程调度

2.1吞吐 vs 响应:吞吐和响应之间的矛盾

  • 响应:最小化某个任务的响应时间,哪怕牺牲其他任务为代价。

  • 吞吐:全局视野,整个系统的workload被最大化处理。

2.2 I/O 消耗型 vs CPU消耗型

  • IO bound: CPU利用率低,进程的运行效率主要受限于I/O速度。

  • CPU bound:多数时间花在CPU上面(做运算)

2.3 优先级

linux系统中有多种优先级,下面是其关系:

优先级

字段

描述

static_prio

用于保存静态优先级,可以通过nice系统调用来进行修改;(100 ~ 139)

rt_priority

用于保存实时优先级;0 - MAX_RT_PRIO-1 (0 - 99)

normal_prio

值取决于静态优先级和调度策略;

prio

用于保存动态优先级,调度器最终使用的。0 ~ 139(包括 0 和 139)

2.3.1 prio动态优先级

prio 的值是调度器最终使用的优先级数值,即调度器选择一个进程时实际选择的值。

prio 值越小,表明进程的优先级越高。prio 值的取值范围是 0 ~ MAX_PRIO,即 0 ~ 139(包括 0 和 139),根据调度策略的不同,又可以分为两个区间,其中区间 0 ~ 99 的属于实时进程,区间 100 ~139 的为非实时进程。

  • 当进程为实时进程时, prio 的值由实时优先级值(rt_priority)计算得来;

prio = MAX_RT_PRIO - 1 - rt_priority // 进程为实时进程

  • 当进程为非实时进程时,prio 的值由静态优先级值(static_prio)得来。

prio = static_prio          // 进程为非实时进程

2.3.2 static_prio 静态优先级

静态优先级不会随时间改变,内核不会主动修改它,只能通过系统调用 nice 去修改 static_prio。

通过调用 NICE_TO_PRIO(nice) 来修改 static_prio 的值, static_prio 值的计算方法如下:

static_prio = MAX_RT_PRIO + nice +20

MAX_RT_PRIO 的值为100,nice 的范围是 -20 ~ +19,故 static_prio 值的范围是 100 ~ 139。 static_prio 的值越小,表明进程的静态优先级越高。

2.3.3 normal_prio归一化优先级

normal_prio 的值取决于静态优先级和调度策略,可以通过 _setscheduler() 函数来设置 normal_prio 的值 。

  • 对于非实时进程,normal_prio = static_prio

  • 对于实时进程,normal_prio = MAX_RT_PRIO-1 - p->rt_priority

2.3.4 rt_priority实时优先级

rt_priority 值的范围是 0 ~ 99,只对实时进程有效。

prio = MAX_RT_PRIO-1 - p->rt_priority

rt_priority 值越大,则 prio 值越小,故 实时优先级(rt_priority)的值越大,意味着进程优先级越高。

rt_priority 的值也是取决于调度策略的,可以在 _setscheduler 函数中对 rt_priority 值进行设置。

2.4 调度器类SCHED_CLASS和调度策略 policy

所谓调度,就是按照某种调度的算法,从进程的就绪队列中选取进程分配CPU,主要是协调对CPU等的资源使用。进程调度的目标是最大限度利用CPU时间。

2.4.1调度器类

内核默认提供了5个调度器,Linux内核使用struct sched_class来对调度器进行抽象。

目前系統中,Scheduling Class的优先级顺序为stop_sched_class > dl_sched_class > rt_sched_class > cfs_sched_class > idle_sched_class

调度器类 SCHED_CLASS

描述

Stop调度器

stop_sched_class

优先级最高的调度类,可以抢占其他所有进程,不能被其他进程抢占;

作用:

1.发生在cpu_stop_cpu_callback 进行cpu之间任务migration;

2.HOTPLUG_CPU的情况下关闭任务。

Deadline调度器

dl_sched_class

使用红黑树,把进程按照绝对截止期限进行排序,选择最小进程进行调度运行;

RT调度器

rt_sched_class

实时调度器,为每个优先级维护一个队列;

CFS调度器

cfs_sched_class

完全公平调度器,采用完全公平调度算法,引入虚拟运行时间概念;

IDLE-Task调度器

idle_sched_class

空闲调度器,每个CPU都会有一个idle线程,当没有其他进程可以调度时,调度运行idle线程;

2.4.2 调度策略

Linux内核提供了一些调度策略供用户程序来选择调度器,其中Stop调度器IDLE-Task调度器,仅由内核使用,用户无法进行选择。

字段 POLICY

描述

所在调度器类

SCHED_DEADLINE

限期进程调度策略,使task选择Deadline调度器来调度运行;

Deadline

SCHED_RR

实时进程调度策略,时间片轮转,进程用完时间片后加入优先级对应运行队列的尾部,把CPU让给同优先级的其他进程;

RT

SCHED_FIFO

实时进程调度策略,先进先出调度没有时间片,没有更高优先级的情况下,只能等待主动让出CPU;

RT

SCHEE_NORMAL

普通进程调度策略,使task选择CFS调度器来调度运行;

CFS

SCHED_BATCH

普通进程调度策略,批量处理,使task选择CFS调度器来调度运行;

CFS

SCHED_IDLE

普通进程调度策略,使task以最低优先级选择CFS调度器来调度运行;

CFS

2.5 RT调度策略和普通进程在调度算法上的差异

Linux的RT调度策略和普通进程在调度算法上面有差异,RT的SCHED_FIFOSCHED_RR采用的是一个bitmap:

每次从第0bit开始往后面搜索第一个有进程ready的bit,然后调度这个优先级上面的进程执行,所以,在内核里面,prio数值越小,优先级越高。但是,从用户态的API里面,则是数值越大,优先级越高。

下面的代码,一个线程通过调用API把自己设置为SCHED_FIFO,优先级50。

这个上面的50,对应内核的49 (从内核的视角上面来看,又会用99减去用户在chrt里面设置的优先级)。

如果我们把优先级设置为51:

这个51,对应内核bitmap上面的48。

所以,你会发现,从用户的视角来看,数值变大,优先级变高。

对于RT的进程而言,TOP的视角里面的 PR= -1 -用户视角。

注:只有最高优先级的RT进程,才在top里面显示为rt。(用户视角的99---内核bitmap视角的0

2.6 普通进程的优先级 nice

普通的讲nice的人相对来说比较简单,我们更关注它的nice值,-20~19之间,nice越低,优先级越高,权重越大,在CFS的红黑树左边的机会大。

你发现.nice为5的进程,在top命令显示PR是25。

下面我们看nice是-5的:

它显示的是PR=15。

由此大家可以发现规律,对于普通的采用CFS策略的NORMAL进程,top里面的 PR=20+NICE

由此发现,在top里面,RT策略的PR都显示为负数;最高优先级的RT,显示为rt。top命令里面也是,数字越小,优先级越高

2.7 ps 中的RTPRIO, PRI,NI

一个是PRI,一个是NI,这到底是什么东西?相对而言,PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。如前面所说,PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。

到目前为止,更需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。

注:这里的PRI = 139 - 内核中prio。即该值越大,优先级越高。

2.8 rt的门限

2.9 CFS调度

2.10 怎样修改进程优先级?

2.10.1 实时进程调度

2.10.2 非实时进程的调度和动态优先级

2.11 怎样查看linux系统中的实时进程和普通进程?

ps -eo state,uid,pid,ppid,rtprio,time,comm
  • RTPRIO

  • "-": 表示普通进程

  • "数字": 表示优先级为xx的实时进程

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

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

相关文章

1.7 古典概型问题类型一——随机取数问题

(1)我的答案:一、信息首先7个数字全不相同二、分析七个数字全不相同意味着每次取出来的数都不一样,然后每次取出后选择少一种,为简单排列不含10和1这意味从8个数里面选且可以重复为重复排列3.10恰好出现两次隐含着两个问题,第一&a…

备考考研2数学

进度说明,开始形成自己的复习进度说明! 14:21 2023年2月28日星期二 武忠祥数学 截止目前,看完了01.高数基础01 14:21 2023年2月28日星期二 开始看02. 现在15:04 2023年2月28日星期二, 因为这2天的百度网盘不能进行解析了&…

初识HTML技术

文章目录一、为什么学习前端?二、第一个HTML文件VSCode三. HTML元素四. HTML页面一、为什么学习前端? 我们作为一个后端程序员,为什么还要学习前端,因为我们的终极目的是实现web开发,搭建网站,网站 前端 后端 比如我们随便…

最近几篇较好论文实现代码(附源代码下载)

《Towards Layer-wise Image Vectorization》(CVPR 2022) GitHub: github.com/ma-xu/LIVEInstallationWe suggest users to use the conda for creating new python environment.Requirement: 5.0<GCC<6.0; nvcc >10.0.git clone gitgithub.com:ma-xu/LIVE.gitcd LIVE…

一步一步学会给Fritzing添加元器件-丰富你的器件库

文章目录1、获取元器件文件2、单个添加元器件3、批量加入&#xff08;1&#xff09;、通过别人发布的bin文件加载&#xff08;2&#xff09;、终极大招&#xff08;拖&#xff09;4、制作自己器件文章出处&#xff1a; https://blog.csdn.net/haigear/article/details/12931545…

【C++】类和对象——六大默认成员函数

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、类的6个默认成员函数二、构造…

错误异常捕获

1、React中错误异常捕获 在 React 中&#xff0c;可以通过 Error Boundaries&#xff08;错误边界&#xff09;来捕获错误异常。Error Boundaries 是一种 React 组件&#xff0c;它可以在其子组件树的渲染期间捕获 JavaScript 异常&#xff0c;并且可以渲染出备用 UI。React 提…

802.11 service服务类型

802.11 serviceservice定义service分类按照模块分为两类按照功能分为六类数据传输相关服务分布式服务DS&#xff08;Distribution Service&#xff09;整合服务IS&#xff08;Integration Service&#xff09;关联&#xff08;association&#xff09;重关联&#xff08;reasso…

RAD 11.3 delphi和C++改进后新增、废弃及优化的功能

RAD 11.3 delphi和C改进后新增和废弃的功能 目录 RAD 11.3 delphi和C改进后新增和废弃的功能 一、版本RAD 11.3 delphi和C改进后新增功能 1、官方视频位置&#xff1a; 2、官方文档的链接位置&#xff1a; 二、版本RAD 11.3 delphi和C改进后废弃的功能 2.1、编译器不再使…

Eureka注册中心和Nacos注册中心详解以及Nacos与Eureka有什么区别?

目录&#xff1a;前言Eureka注册中心Nacos注册中心Nacos与Eureka有什么区别&#xff1f;前言提供接口给其它微服务调用的微服务叫做服务提供者&#xff0c;而调用其它微服务提供的接口的微服务则是服务消费者。如果服务A调用了服务B&#xff0c;而服务B又调用了服务C&#xff0…

【iOS】设置背景渐变色

drawRect函数 主要负责iOS的绘图操作&#xff0c;程序会自动调用此方法进行绘图。我在这个函数中绘制渐变背景色。 方法定义&#xff1a; -(void)drawRect:(CGRect)rect; 重写此方法&#xff0c;执行重绘任务-(void)setNeedsDisplay; 标记为需要重绘&#xff0c;异步调用dra…

Mysql开发

Mysql开发 可以使用MySQL直接存储文件吗&#xff1f; 可以使用 BLOB (binary large object)&#xff0c;用来存储二进制大对象的字段类型。 TinyBlob 255 值的长度加上用于记录长度的1个字节(8位) Blob 65K值的长度加上用于记录长度的2个字节(16位) MediumBlob 16M值的长度加…

vue-v-for列表渲染中key的作用

1.虚拟DOM中key的作用: key是点拟DON对象的标识&#xff0c;当状态中的数据发生变化时&#xff0c;Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较&#xff0c;比较规则如下 2.对比规则: 旧虚拟DOM中找到了与新虚拟DOM相同的ke…

【NLP相关】ChatGPT的前世今生:GPT模型的原理、研究进展和案例

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

【二分查找】分巧克力、机器人跳跃、数的范围

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…

二层如何避免loop 的产生

STP是一个用于局域网中消除环路的协议。STP通过构造一棵树来消除交换网络中的环路二层交换机二层交换机不具备路由能力&#xff0c;主要功能有三种1&#xff09;地址学习&#xff08;address learning&#xff09;&#xff1a;通过查看帧的源MAC地址来加紧转发/过滤表的MAC地址…

django框架开发部署项目

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

工作负载使用教程

主要包括YCSB和背景一、安装Java二、安装Memcached数据库并配置三、修改YCSB脚本文件四、运行YCSB脚本五、运行GAPBS总结背景 测试multi-clock内核的内存性能有啥提升 YCSB&#xff1a;雅虎推出的云数据库基准测试套件 参考论文MULTI-CLOCK: Dynamic Tiering for Hybrid Memor…

03-产品解决方案:需求分析、功能优先级划分、功能价值、用户体验

文章目录3.1 需求分析3.1.1 需求分析的目的3.1.2 需求分析的方法&#xff08;Y模型&#xff09;3.1.3 需求分析的具体应用3.2 功能优先级划分Kano模型3.3 功能价值3.3.1 什么是功能价值&#xff1f;3.3.2 实际价值判断3.3.3 成本评估3.4 用户体验3.4.1 用户体验的定义及影响因素…

IDEA 每次新建工程都要重新配置 Maven 解决方案

IDEA 每次新建工程都要重新配置 Maven 解决方案 IDEA 每次新建工程都要重新配置 Maven&#xff0c;是一件相当浪费时间的事情。这是因为在创建一个项目后&#xff0c;在 File -> Settings -> Build,Execution,Deployment -> Build Tools -> Maven下配置了 Maven h…