1.go源码:Sleep函数与线程
2.老生常谈线程基础的调度调度几个问题
3.Golang GMP 原理
4.华为openEuler21.03版本怎么样
5.è±è¯ç¿»è¯
go源码:Sleep函数与线程
在探索 Go 语言的并发编程中,Sleep 函数与线程的源码交互方式与 Java 或其他基于线程池的并发模型有所不同。本文将深入分析 Go 语言中 Sleep 函数的开源实现及其与线程的互动方式,以解答关于 Go 语言中 Sleep 函数与线程关系的工具问题。
首先,调度调度重要的源码指标源码流通市值一点是,当一个 goroutine(g)调用 Sleep 函数时,开源它并不会导致当前线程被挂起。工具相反,调度调度Go 通过特殊的源码机制来处理这种情景,确保 Sleep 函数的开源调用不会影响到线程的执行。这一特性是工具 Go 语言并发模型中独特而关键的部分。
具体来说,调度调度当一个 goroutine 调用 Sleep 函数时,源码它首先将自身信息保存到线程的开源关键结构体(p)中并挂起。这一过程涉及多个函数调用,包括 `time.Sleep`、`runtime.timeSleep`、`runtime.gopark`、`runtime.mcall`、`runtime.park_m`、`runtime.resetForSleep` 等。最终,该 goroutine 会被放入一个 timer 结构体中,并将其放入到 p 关联的一个最小堆中,从而实现了对当前 goroutine 的保存,同时为调度器提供了切换到其他 goroutine 或 timer 的机会。因此,这里的 timer 实际上代表了被 Sleep 挂起的 goroutine,它在睡眠到期后能够及时得到执行。
接下来,我们深入分析 goroutine 的调度过程。当线程 p 需要执行时,它会通过 `runtime.park_m` 函数调用 `schedule` 函数来进行 goroutine 或 timer 的切换。在此过程中,`runtime.findrunnable` 函数会检查线程堆中是否存在已到期的 timer,如果存在,则切换到该 timer 进行执行。如果 timer 堆中没有已到期的 timer,线程会继续检查本地和全局的 goroutine 队列中是否还有待执行的 goroutine,如果队列为空,则线程会尝试“偷取”其他 goroutine 的任务。这一过程包括了检查 timer 堆、偷取其他 p 中的到期 timer 或者普通 goroutine,确保任务能够及时执行。
在“偷取”任务的过程中,线程会优先处理即将到期的 timer,确保这些 timer 的准时执行。如果当前线程正在执行其他任务(如 epoll 网络),则在执行过程中会定期检查 timer 到期情况。如果发现其他线程的 timer 到期时间早于自身,会首先唤醒该线程以处理其 timer,确保不会错过任何到期的 timer。
为了证明当前线程设置的 timer 能够准时执行,本文提出了两种证明方法。第一种方法基于代码细节,重点分析了线程状态的变化和 timer 的执行流程。具体而言,文章中提到的三种线程状态(正常运行、epoll 网络、睡眠)以及相应的 timer 执行情况,表明在 Go 语言中,timer 的执行策略能够确保其准时执行。第二种方法则从全局调度策略的角度出发,强调了 Go 语言中线程策略的设计原则,即至少有一个线程处于“spinning”状态或者所有线程都在执行任务,这保证了 timer 的准时执行。
总之,Go 语言中 Sleep 函数与线程之间的交互方式,通过特殊的线程管理机制,确保了 goroutine 的 Sleep 操作不会阻塞线程,同时保证了 timer 的准时执行。这一机制是 Go 语言并发模型的独特之处,为开发者提供了一种高效且灵活的并发处理方式。
老生常谈线程基础的几个问题
实现线程只有一种方式
我们知道启动线程至少可以通过以下四种方式:
实现Runnable接口
继承Thread类
线程池创建线程
带返回值的Callable创建线程
但是看它们的底层就一种方式,就是面试spring5源码通过newThread()实现,其他的只不过在它的上面做了层封装。
实现Runnable接口要比继承Thread类的更好:
结构上分工更明确,线程本身属性和任务逻辑解耦。
某些情况下性能更好,直接把任务交给线程池执行,无需再次newThread()。
可拓展性更好:实现接口可以多个,而继承只能单继承。
有的时候可能会问到启动线程为什么是start()方法,而不是run()方法,这个问题很简单,执行run()方法其实就是在执行一个类的普通方法,并没有启动一个线程,而start()方法点进去看是一个native方法。
当我们在执行java中的start()方法的时候,它的底层会调JVM由c++编写的代码Thread::start,然后c++代码再调操作系统的create_thread创建线程,创建完线程以后并不会马上运行,要等待CPU的调度。CPU的调度算法有很多,比如先来先服务调度算法(FIFO),最短优先(就是对短作业的优先调度)、时间片轮转调度等。如下图所示:
线程的状态在Java中线程的生命周期中一共有6种状态。
NEW:初始状态,线程被构建,但是还没有调用start方法
RUNNABLE:运行状态,JAVA线程把操作系统中的就绪和运行两种状态统一称为运行中
BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权
WAITING:等待状态
TIMED_WAITING:超时等待状态,超时以后自动返回
TERMINATED:终止状态,表示当前线程执行完毕
当然这也不是我说的,源码中就是这么定义的:
publicenumState{ /***Threadstateforathreadwhichhasnotyetstarted.*/NEW,/***Threadstateforarunnablethread.Athreadintherunnable*stateisexecutingintheJavavirtualmachinebutitmay*bewaitingforotherresourcesfromtheoperatingsystem*suchasprocessor.*/RUNNABLE,/***Threadstateforathreadblockedwaitingforamonitorlock.*Athreadintheblockedstateiswaitingforamonitorlock*toenterasynchronizedblock/methodor*reenterasynchronizedblock/methodaftercalling*{ @linkObject#wait()Object.wait}.*/BLOCKED,/***Threadstateforawaitingthread.*Athreadisinthewaitingstateduetocallingoneofthe*followingmethods:*<ul>*<li>{ @linkObject#wait()Object.wait}withnotimeout</li>*<li>{ @link#join()Thread.join}withnotimeout</li>*<li>{ @linkLockSupport#park()LockSupport.park}</li>*</ul>**<p>Athreadinthewaitingstateiswaitingforanotherthreadto*performaparticularaction.**Forexample,athreadthathascalled<tt>Object.wait()</tt>*onanobjectiswaitingforanotherthreadtocall*<tt>Object.notify()</tt>or<tt>Object.notifyAll()</tt>on*thatobject.Athreadthathascalled<tt>Thread.join()</tt>*iswaitingforaspecifiedthreadtoterminate.*/WAITING,/***Threadstateforawaitingthreadwithaspecifiedwaitingtime.*Athreadisinthetimedwaitingstateduetocallingoneof*thefollowingmethodswithaspecifiedpositivewaitingtime:*<ul>*<li>{ @link#sleepThread.sleep}</li>*<li>{ @linkObject#wait(long)Object.wait}withtimeout</li>*<li>{ @link#join(long)Thread.join}withtimeout</li>*<li>{ @linkLockSupport#parkNanosLockSupport.parkNanos}</li>*<li>{ @linkLockSupport#parkUntilLockSupport.parkUntil}</li>*</ul>*/TIMED_WAITING,/***Threadstateforaterminatedthread.*Thethreadhascompletedexecution.*/TERMINATED;}下面是这六种状态的转换:
New新创建New表示线程被创建但尚未启动的状态:当我们用newThread()新建一个线程时,如果线程没有开始调用start()方法,那么此时它的状态就是New。而一旦线程调用了start(),它的状态就会从New变成Runnable。
Runnable运行状态Java中的Runable状态对应操作系统线程状态中的两种状态,分别是Running和Ready,也就是说,Java中处于Runnable状态的线程有可能正在执行,也有可能没有正在执行,正在等待被分配CPU资源。
如果一个正在运行的线程是Runnable状态,当它运行到任务的一半时,执行该线程的CPU被调度去做其他事情,导致该线程暂时不运行,它的状态依然不变,还是Runnable,因为它有可能随时被调度回来继续执行任务。
在Java中Blocked、Waiting、TimedWaiting,这三种状态统称为阻塞状态,下面分别来看下。
Blocked从上图可以看出,从Runnable状态进入Blocked状态只有一种可能,就是进入synchronized保护的代码时没有抢到monitor锁,jvm会把当前的线程放入到锁池中。当处于Blocked的线程抢到monitor锁,就会从Blocked状态回到Runnable状态。
Waiting状态我们看上图,线程进入Waiting状态有三种可能。
没有设置Timeout参数的Object.wait()方法,jvm会把当前线程放入到等待队列。
没有设置Timeout参数的Thread.join()方法。
LockSupport.park()方法。
Blocked与Waiting的区别是Blocked在等待其他线程释放monitor锁,而Waiting则是在等待某个条件,比如join的线程执行完毕,或者是notify()/notifyAll()。
当执行了LockSupport.unpark(),或者join的线程运行结束,或者被中断时可以进入Runnable状态。当调用notify()或notifyAll()来唤醒它,它会直接进入Blocked状态,安徽距离山东源码因为唤醒Waiting状态的线程能够调用notify()或notifyAll(),肯定是已经持有了monitor锁,这时候处于Waiting状态的线程没有拿到monitor锁,就会进入Blocked状态,直到执行了notify()/notifyAll()唤醒它的线程执行完毕并释放monitor锁,才可能轮到它去抢夺这把锁,如果它能抢到,就会从Blocked状态回到Runnable状态。
TimedWaiting状态在Waiting上面是TimedWaiting状态,这两个状态是非常相似的,区别仅在于有没有时间限制,TimedWaiting会等待超时,由系统自动唤醒,或者在超时前被唤醒信号唤醒。
以下情况会让线程进入TimedWaiting状态。
设置了时间参数的Thread.sleep(longmillis)方法。
设置了时间参数的Object.wait(longtimeout)方法。
设置了时间参数的Thread.join(longmillis)方法。
设置了时间参数的LockSupport.parkNanos(longnanos)。
LockSupport.parkUntil(longdeadline)方法。
在TimedWaiting中执行notify()和notifyAll()也是一样的道理,它们会先进入Blocked状态,然后抢夺锁成功后,再回到Runnable状态。当然,如果它的超时时间到了且能直接获取到锁/join的线程运行结束/被中断/调用了LockSupport.unpark(),会直接恢复到Runnable状态,而无需经历Blocked状态。
Terminated终止Terminated终止状态,要想进入这个状态有两种可能。
run()方法执行完毕,线程正常退出。
出现一个没有捕获的异常,终止了run()方法,最终导致意外终止。
线程的停止interrupt我们知道Thread提供了线程的一些操作方法,比如stop(),suspend()和resume(),这些方法已经被Java直接标记为@Deprecated,这就说明这些方法是不建议大家使用的。
因为stop()会直接把线程停止,这样就没有给线程足够的时间来处理想要在停止前保存数据的逻辑,任务戛然而止,会导致出现数据完整性等问题。这种行为类似于在linux系统中执行kill-9类似,它是一种不安全的操作。
而对于suspend()和resume()而言,它们的问题在于如果线程调用suspend(),它并不会释放锁,就开始进入休眠,但此时有可能仍持有锁,这样就容易导致死锁问题,因为这把锁在线程被resume()之前,是不会被释放的。
interrupt最正确的停止线程的方式是使用interrupt,但interrupt仅仅起到通知被停止线程的作用。而对于被停止的线程而言,它拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。
下面我们来看下例子:
publicclassInterruptExampleimplementsRunnable{ //interrupt相当于定义一个volatile的变量//volatilebooleanflag=false;publicstaticvoidmain(String[]args)throwsInterruptedException{ Threadt1=newThread(newInterruptExample());t1.start();Thread.sleep(5);//Main线程来决定t1线程的停止,发送一个中断信号,中断标记变为truet1.interrupt();}@Overridepublicvoidrun(){ while(!Thread.currentThread().isInterrupted()){ System.out.println(Thread.currentThread().getName()+"--");}}}执行一下,运行了一会就停止了
主线程在调用t1的interrupt()之后,这个线程的中断标记位就会被设置成true。每个线程都有这样的标记位,当线程执行时,会定期检查这个标记位,如果标记位被设置成true,就说明有程序想终止该线程。在while循环体判断语句中,通过Thread.currentThread().isInterrupt()判断线程是否被中断,如果被置为true了,则跳出循环,线程就结束了,图像识别pytorch源码这个就是interrupt的简单用法。
阻塞状态下的线程中断下面来看第二个例子,在循环中加了Thread.sleep秒。
publicclassInterruptSleepExampleimplementsRunnable{ //interrupt相当于定义一个volatile的变量//volatilebooleanflag=false;publicstaticvoidmain(String[]args)throwsInterruptedException{ Threadt1=newThread(newInterruptSleepExample());t1.start();Thread.sleep(5);//Main线程来决定t1线程的停止,发送一个中断信号,中断标记变为truet1.interrupt();}@Overridepublicvoidrun(){ while(!Thread.currentThread().isInterrupted()){ try{ Thread.sleep();}catch(InterruptedExceptione){ //中断标记变为falsee.printStackTrace();}System.out.println(Thread.currentThread().getName()+"--");}}}再来看下运行结果,卡主了,并没有停止。这是因为main线程调用了t1.interrupt(),此时t1正在sleep中,这时候是接收不到中断信号的,要sleep结束以后才能收到。这样的中断太不及时了,我让你中断了,你缺还在傻傻的sleep中。
Java开发的设计者已经考虑到了这一点,sleep、wait等方法可以让线程进入阻塞的方法使线程休眠了,而处于休眠中的线程被中断,那么线程是可以感受到中断信号的,并且会抛出一个InterruptedException异常,同时清除中断信号,将中断标记位设置成false。
这时候有几种做法:
直接捕获异常,不做处理,e.printStackTrace();打印下信息
将异常往外抛出,即在方法上throwsInterruptedException
再次中断,代码如下,加上Thread.currentThread().interrupt();
@Overridepublicvoidrun(){ while(!Thread.currentThread().isInterrupted()){ try{ Thread.sleep();}catch(InterruptedExceptione){ //中断标记变为falsee.printStackTrace();//把中断标记修改为trueThread.currentThread().interrupt();}System.out.println(Thread.currentThread().getName()+"--");}}这时候线程感受到了,我们人为的再把中断标记修改为true,线程就能停止了。一般情况下我们操作线程很少会用到interrupt,因为大多数情况下我们用的是线程池,线程池已经帮我封装好了,但是这方面的知识还是需要掌握的。感谢收看,多多点赞~
作者:小杰博士
Golang GMP 原理
通常语义中的线程,指的是内核级线程,核心点包括:(1)它是操作系统最小调度单元;(2)创建、销毁、调度交由内核完成,cpu 需完成用户态与内核态间的切换;(3)可充分利用多核,实现并行。
协程,又称为用户级线程,核心点如下:(1)与线程存在映射关系,为 M:1;(2)创建、销毁、调度在用户态完成,对内核透明,所以更轻;(3)从属同一个内核级线程,无法并行;一个协程阻塞会导致从属同一线程的所有协程无法执行。
Goroutine,经 Golang 优化后的特殊“协程”,核心点包括:(1)与线程存在映射关系,为 M:N;(2)创建、销毁、调度在用户态完成,对内核透明,足够轻便;(3)可利用多个线程,实现并行;(4)通过调度器的斡旋,实现和线程间的动态绑定和灵活调度;(5)栈空间大小可动态扩缩,因地制宜。
对比三个模型的各项能力:综上,goroutine 可说是博采众长之物。
实际上,“灵活调度” 一词概括得实在过于简要,Golang 在调度 goroutine 时,针对“如何减少加锁行为”,“如何避免资源不均”等问题都给出了精彩的解决方案,这一切都得益于经典的 “gmp” 模型。
GMP = goroutine + machine + processor(+ 一套有机组合的机制),下面先单独拆出每个组件进行介绍,最后再总览全局,对 GMP 进行总述。
G = goroutine,软件强制更新源码是 Golang 中对协程的抽象;(2)g 有自己的运行栈、状态、以及执行的任务函数(用户通过 go func 指定);(3)g 需要绑定到 p 才能执行,在 g 的视角中,p 就是它的 cpu。
P = processor,是 Golang 中的调度器;(2)p 是 gmp 的中枢,借由 p 承上启下,实现 g 和 m 之间的动态有机结合;(3)对 g 而言,p 是其 cpu,g 只有被 p 调度,才得以执行;(4)对 m 而言,p 是其执行代理,为其提供必要信息的同时(可执行的 g、内存分配情况等),并隐藏了繁杂的调度细节;(5)p 的数量决定了 g 最大并行数量,可由用户通过 GOMAXPROCS 进行设定(超过 CPU 核数时无意义)。
M = machine,是 Golang 中对线程的抽象;(1)m 不直接执行 g,而是先和 p 绑定,由其实现代理;(3)借由 p 的存在,m 无需和 g 绑死,也无需记录 g 的状态信息,因此 g 在全生命周期中可以实现跨 m 执行。
全局有多个 M 和多个 P,但同时并行的 G 的最大数量等于 P 的数量。G 的存放队列有三类:P 的本地队列;全局队列;和 wait 队列(图中未展示,为 io 阻塞就绪态 goroutine 队列)。
M 调度 G 时,优先取 P 本地队列,其次取全局队列,最后取 wait 队列。这样的好处是,取本地队列时,可以接近于无锁化,减少全局锁竞争。为防止不同 P 的闲忙差异过大,设立 work-stealing 机制,本地队列为空的 P 可以尝试从其他 P 本地队列偷取一半的 G 补充到自身队列。
核心数据结构定义于 runtime/runtime2.go 文件中,各个类的成员属性较多,这里只摘取核心字段进行介绍:g 的生命周期由以下几种状态组成:_Gidle(值为 0,为协程开始创建时的状态,此时尚未初始化完成);_Grunnable(值为 1,协程在待执行队列中,等待被执行);_Grunning(值为 2,协程正在执行,同一时刻一个 p 中只有一个 g 处于此状态);_Gsyscall(值为 3,协程正在执行系统调用);_Gwaiting(值为 4,协程处于挂起态,需要等待被唤醒. gc、channel 通信或者锁操作时经常会进入这种状态);_Gdead(值为 6,协程刚初始化完成或者已经被销毁,会处于此状态);_Gcopystack(值为 8,协程正在栈扩容流程中);_Greempted(值为 9,协程被抢占后的状态)。
文字性总结难免有些过于含糊和空洞,对一些细节的描述总是不够精确的。下面照旧开启源码走读流程,从代码中寻求理论证明和细节补充。
gmp 数据结构定义为 runtime/runtime2.go 文件中,由于各个类的成员属性较多,那么只摘取核心字段进行介绍:(1)m:在 p 的代理,负责执行当前 g 的 m;(2)sched.sp:保存 CPU 的 rsp 寄存器的值,指向函数调用栈栈顶;(3)sched.pc:保存 CPU 的 rip 寄存器的值,指向程序下一条执行指令的地址;(4)sched.ret:保存系统调用的返回值;(5)sched.bp:保存 CPU 的 rbp 寄存器的值,存储函数栈帧的起始位置。其中 g 的生命周期由以下几种状态组成:(1)_Gidle(值为 0,为协程开始创建时的状态,此时尚未初始化完成);(2)_Grunnable(值为 1,协程在待执行队列中,等待被执行);(3)_Grunning(值为 2,协程正在执行,同一时刻一个 p 中只有一个 g 处于此状态);(4)_Gsyscall(值为 3,协程正在执行系统调用);(5)_Gwaiting(值为 4,协程处于挂起态,需要等待被唤醒. gc、channel 通信或者锁操作时经常会进入这种状态);(6)_Gdead(值为 6,协程刚初始化完成或者已经被销毁,会处于此状态);(7)_Gcopystack(值为 8,协程正在栈扩容流程中);(8)_Greempted(值为 9,协程被抢占后的状态)。
其中,goroutine 的类型可分为两类:(1)I 负责调度普通 g 的 g0,执行固定的调度流程,与 m 的关系为一对一;(2)II 负责执行用户函数的普通 g。m 通过 p 调度执行的 goroutine 永远在普通 g 和 g0 之间进行切换。
主动调度是用户主动执行让渡的方式,主要方式是,用户在执行代码中调用了 runtime.Gosched 方法,此时当前 g 会当让出执行权,主动进行队列等待下次被调度执行。被动调度因当前不满足某种执行条件,g 可能会陷入阻塞态无法被调度,直到关注的条件达成后,g 才从阻塞中被唤醒,重新进入可执行队列等待被调度。
正常调度指的是 g 中的执行任务已完成,g0 会将当前 g 置为死亡状态,发起新一轮调度。抢占调度指的是 g 执行系统调用超过指定的时长,且全局的 p 资源比较紧缺,此时将 p 和 g 解绑,抢占出来用于其他 g 的调度。
调度流程的主干方法是位于 runtime/proc.go 中的 schedule 函数。在宏观调度流程中,我们可以尝试对 gmp 的宏观调度流程进行整体串联,包括:(1)以 g0 -> g -> g0 的一轮循环为例进行串联;(2)g0 执行 schedule() 函数,寻找到用于执行的 g;(3)g0 执行 execute() 方法,更新当前 g、p 的状态信息,并调用 gogo() 方法,将执行权交给 g;(4)g 因主动让渡(gosche_m())、被动调度(park_m())、正常结束(goexit0())等原因,调用 m_call 函数,执行权重新回到 g0 手中;(5)g0 执行 schedule() 函数,开启新一轮循环。
在 Golang 中,调度流程的主干方法是位于 runtime/proc.go 中的 schedule 函数,此时的执行权位于 g0 手中。在 findRunnable 方法中,调度流程中,一个非常核心的步骤就是为 m 寻找到下一个执行的 g。在 execute 方法中,当 g0 为 m 寻找到可执行的 g 之后,接下来就开始执行 g。
在 g 执行主动让渡时,会调用 mcall 方法将执行权归还给 g0,并由 g0 调用 gosched_m 方法。在 g 需要被动调度时,会调用 mcall 方法切换至 g0,并调用 park_m 方法将 g 置为阻塞态。当 g 执行完成时,会先执行 mcall 方法切换至 g0,然后调用 goexit0 方法。与 g 的系统调用有关的,视角切换回发生系统调用前,与 g 绑定的原 m 当中,此时执行权同样位于 m 的 g0 手中。在 m 需要执行系统调用前,会先执行位于 runtime/proc.go 的 reentersyscall 的方法。当 m 完成了内核态的系统调用之后,此时会步入位于 runtime/proc.go 的 exitsyscall 函数中。
与 g 的系统调用有关的,视角切换回发生系统调用前,与 g 绑定的原 m 当中,在 m 需要执行系统调用前,会先执行位于 runtime/proc.go 的 reentersyscall 的方法。当 m 完成了内核态的系统调用之后,此时会步入位于 runtime/proc.go 的 exitsyscall 函数中。
当 g 执行完成时,会先执行 mcall 方法切换至 g0,然后调用 goexit0 方法。当 m 完成了内核态的系统调用之后,此时会步入位于 runtime/proc.go 的 exitsyscall 函数中。
对于抢占调度的执行者,不是 g0,而是一个全局的 monitor g,代码位于 runtime/proc.go 的 retake 方法中。与 g 的系统调用有关的,视角切换回发生系统调用前,与 g 绑定的原 m 当中,在 m 需要执行系统调用前,会先执行位于 runtime/proc.go 的 reentersyscall 的方法。当 m 完成了内核态的系统调用之后,此时会步入位于 runtime/proc.go 的 exitsyscall 函数中。
在 Golang 中,调度流程的主干方法是位于 runtime/proc.go 中的 schedule 函数,此时的执行权位于 g0 手中。在 findRunnable 方法中,调度流程中,一个非常核心的步骤就是为 m 寻找到下一个执行的 g。在 execute 方法中,当 g0 为 m 寻找到可执行的 g 之后,接下来就开始执行 g。
在 g 执行主动让渡时,会调用 mcall 方法将执行权归还给 g0,并由 g0 调用 gosched_m 方法。在 g 需要被动调度时,会调用 mcall 方法切换至 g0,并调用 park_m 方法将 g 置为阻塞态。当 g 执行完成时,会先执行 mcall 方法切换至 g0,然后调用 goexit0 方法。当 m 完成了内核态的系统调用之后,此时会步入位于
华为openEuler.版本怎么样
华为 openEuler .是华为发布的全新创新桌面选择。该版本属于全新的创新版本而不是 LTS (Long Term Support) 版本。全新的创新桌面为用户们带来的是更强的虚拟化功能和更多桌面选择支持。
华为 openEuler .介绍
全新的5.内核
1、深度优化调度、IO、内存管理,提供Arm、x、RISC-V等更多计算能力支持。
2、对调度程序进行了优化,以优化CFS任务的公平性,并添加了感知numa的异步调用机制,这在NVDIMM初始化方面有了显著的改进;优化了SCHED_IDLE的调度策略,显著提高了高优先级任务的调度延迟,降低了其他任务干扰的成本。
3、numa平衡机制的优化带来了更好的亲和性、更高的利用率和更少的无效迁移。
4、增强CPU隔离机制,支持中断隔离,支持非绑定线程隔离,增强CPU内核隔离,更好地避免业务间相互干扰。
5、优化了cgroup单线程迁移的性能,消除了对线程组读写信号量的依赖;时间名称空间的引入使得容器迁移更加方便。
6、系统容器支持限制容器中使用的文件句柄的数量。文件句柄包括普通的文件句柄和网络套接字。在启动容器时,可以指定——files-limit参数来限制容器中打开句柄的最大数量。
7、PSI能力支持。PSI (Pressure Stall Information)提供了一种评估系统资源(如CPU、内存和IO)压力的方法。准确的检测方法可以帮助资源用户确定合适的工作量,也可以帮助系统制定高效的资源调度策略,最大限度地利用系统资源,最大限度地提高用户体验。
8、进程间通信优化,pipe/epoll_wait唤醒机制优化,解决唤醒多个等待线程的性能问题。
9、增强内存管理、细化内存控制、统计、异构内存、热插拔、内存初始化等功能得到了改进,并提供了更有效的用户控制界面;热点锁和信号量优化,激进的内存调节和碎片整理,优化、vmap/vmalloc机制,显著提高内存应用效率;KASAN, kmemleak, slub_debug, oom和其他内存维护功能都得到了增强,以提高内存问题的定位和解决效率。
、提前发包时间模式开关解决了原有TCP框架在报文发送过程中的局限性。根据调度策略,为数据包设置EDT时间戳,避免了队列缓冲区过大造成的延迟,从而大大提高了TCP的性能。
、支持多路径TCP,可以提高移动和数据场景下的性能和可靠性,支持负载均衡场景下多个子流并行传输。
、引入了log fast commit方法,而EXT4引入了一种新的、更轻量级的日志记录方法——fast commit,它可以极大地加快耗时的操作,比如fsync,并带来更好的性能。
、支持dm写cache特性,提高SSD大容量顺序写性能,提高DDR持久化内存性能。
、io_uring是一个新的异步IO框架和实现,支持轮询模式。在轮询模式下,性能得到了与spdk类似的显著提高,队列深度越高,性能越好。
、支持ILP,在鲲鹏 Arm环境下支持位应用程序。
、IMA商业增强,基于开源IMA解决方案,增强安全性,提高性能,提高易用性,并帮助商业实现。
、支持单任务栈巡检,增强对ROP攻击的防范能力。
、MPAM资源管理和控制,支持Arm架构的缓存QoS和内存带宽控制技术。
、支持基于sedim的NMI机制和基于pmu的NMI机制,实现硬锁检测;启用perf nmi可以实现更准确的性能分析。
、Arm平台支持虚拟机CPU热插拔,提高了资源配置的灵活性。
、Arm kdump得到了增强,支持4G以上地址的内存预留,这样kdump可以预留更多的内存空间,并支持具有更大内存的机器。
、支持树莓派系列板。Raspberry Pi的支持已经集成到本地的openEuler .内核中,可以直接使用openEuler .内核源代码进行调试。
、RISC-V平台支持KVM虚拟化。
、支持智能网卡。
热内核升级
1、热内核升级是修复和升级操作系统漏洞的解决方案。实现了无服务意识的内核快速热替换。
2、Cpu Park和Quick Kexec的功能加速系统启动和停止,减少停机时间,提高系统可用性。
3、Pin存储器和Pmem功能可确保快速准确地恢复业务流程,并提高业务灵活性。
4、内核热升级控制器提供gRPC通讯接口,使用方便。
内存分层扩展
1、支持多内存和存储介质的统一管理,支持系统容量的平滑扩展。
2、冷热页识别,通过内核态内存页的空闲和空闲统计机制,准确识别进程内存页访问的冷热分布。
3、可以配置淘汰策略,提供配置界面,自定义内存页面冷热分类策略。
4、平滑扩展,冷页自动切换到扩展内存,其上部署的软件兼容运行,无需改变或调整编程模式。
5、支持多媒体扩展,支持单片机、XL Flash、NVMe SSD等介质作为扩展内存,并根据介质本身的访问速度指定冷热内存分层方案,以达到扩展内存、降低性能损失的目的。
6、增强的虚拟化能力和可维护的测量能力。
7、增加了热迁移Pro的能力扩展,提高了可维护性和可测性。
8、热迁移专业版功能,增强热迁移多功能支持TLS,确保迁移过程中的数据安全;支持热迁移数据的并行压缩,提高迁移性能;增加数据页面访问频率的统计,支持热迁移数据的早期预测。
9、vmtop性能调试工具可以实时动态查看虚拟机的资源使用情况,包括CPU占用率、内存占用率等信息。增加了支持x_架构的扩展。
、支持IO挂起。默认情况下,当发生io错误时,IO会自动重试,超时后会报告警报。
、RISC-V架构支持虚拟化热迁移。
轻量级虚拟运行时
1、添加灵活的内存,大页面功能,系统调用过滤功能,增强IO子系统,提高性能和稳定性。
2、灵活的内存支持,根据工作负载的内存需求实现内存分配和恢复,virtio-balloon的内存恢复速度可达3GB/s。
3、大页面支持。在轻量级框架中提供大页面支持,可以为轻量级虚拟机提供连续的物理内存页面,提高虚拟机的内存访问效率。
4、系统调用过滤简化了设备模型,增加了对系统调用过滤的支持,只需要最简单配置的个系统调用,有效减少了系统攻击面。
5、输入输出子系统得到增强,以支持多通道并发输入输出能力并提高性能。支持IO- qos能力,增强虚拟机IO流量管理的灵活性和稳定性。open stack 维多利亚集成。
6、简单、可扩展、丰富、统一的云管理操作系统。更多功能见OpenStack Victoria官方发行说明。
7、通过集成openStack Vicoria版本实现IaaS(基础设施即服务)解决方案。
8、增强数据块存储的服务能力,并添加容量扩展、快照和虚拟机映像克隆等高级功能。
9、增强集装箱化部署和网络能力,以便更好地与集装箱集成。
、添加扩展服务支持,并支持扩展服务,如控制面板管理、裸机部署和云资源跟踪。
Kubernetes 1.集成
1、有关云本机操作系统自动部署、扩展和管理容器化应用程序的更多功能,请参考Kubernetes 1.的官方发行说明。
2、自动联机和回滚。Kubernetes会自动将应用程序或其配置更改的实例联机,并监控应用程序的运行状态。如果失败,将回滚以前的更改。
3、服务发现和负载均衡,支持基于容器IP和DNS名称的服务发现和负载均衡。
4、存储业务流程支持自动挂载多个存储后端,如本地存储、NFS、iSCSI、Gluster、Ceph等网络存储系统。
5、水平扩展,支持命令行扩展,用户界面手动扩展,根据CPU占用率自动扩展。
è±è¯ç¿»è¯
Dingnan County Industrial Park located in the county west of the logistics, planning area of mu (of which the first phase of development of 2, acres), with a total investment of about billion, of which 1.5 billion initial investment, September , is listed as the provincial government key scheduling of major projects.
Scheduled logistics industrial park planning and construction of County "One Park III", "a park" that is the logistics industrial park, the "three areas" that is linked to the CMR rejection Centre, iron sea transport "dry port", fast moving consumer goods distribution centers, three a support of core projects. June , given the logistics County Association of Guangdong Province to cooperate, commissioned by the Society for the logistics industry in Guangdong Province set Logistics Industrial Park South, "a Park III" for the overall planning and design, planning and promotion, investment construction.
2024-11-28 08:59
2024-11-28 08:41
2024-11-28 08:15
2024-11-28 07:36
2024-11-28 07:07
2024-11-28 06:42