您当前的位置:首页 > 电脑百科 > 软件技术 > 操作系统 > linux百科

Linux 进程管理之进程调度与切换

时间:2023-03-03 16:06:35  来源:今日头条  作者:Linux码农

进程调度相关内核结构

我们知道,进程运行需要各种各样的系统资源,如内存、文件、打印机和最宝贵的 CPU 等,所以说,调度的实质就是资源的分配。系统通过不同的调度算法(Scheduling Algorithm)来实现这种资源的分配。通常来说,选择什么样的调度算法取决于资源分配的策略(Scheduling Policy)。

有关调度相关的结构保存在 task_struct 中,如下:

struct task_struct {
/*
task_struct 采用了如下3个成员表示进程的优先级,prio、normal_prio表示动态优先级
static_prio 为静态优先级,静态优先级是进程启动时分配的优先级,它可以使用nice和sched_setscheduler系统调用修改,
否则在进程运行期间会一直保持恒定
*/
int prio, static_prio, normal_prio;
/*
sched_clas表示该进程所属的调度器类
2.6.24版本中有3中调度类:
实时调度器 : rt_sched_class
完全公平调度器:fAIr_sched_class
空闲调度器: idle_sched_class
*/
const struct sched_class *sched_clas;
//调度实体
struct sched_entity se;
/*
policy 保存了对该进程应用的调度策略。
进程的调度策略有6种
SCHED_NORMAL SCHED_FIFO SCHED_RR SCHED_BATCH SCHED_IDLE
普通进程调度策略: SCHED_NORMAL、SCHED_BATCH、SCHED_IDLE,这些都是通过完全公平调度器来处理的
实时进程调度策略: SCHED_RR、SCHED_FIFO 这些都是通过实时调度器来处理的
*/
unsigned int policy;
/*
除了内核线程(Kernel Thread),每个进程都拥有自己的地址空间(也叫虚拟空间),用mm_struct 来描述。active_mm是为内核线程而引入的。因为内核线程没有自己的地址空间,
为了让内核线程与普通进程具有统一的上下文切换方式,当内核线程进行上下文切换时,让切换进来的线程的active_mm
指向刚被调度出去的进程的active_mm(如果进程的mm 域不为空,则其active_mm 域与mm 域相同)
*/
struct mm_struct *mm, *active_mm;
//表示实时进程的优先级,最低的优先级为0,最高为99,值越大,优先级越高
unsigned int rt_priority;
...
};

每个进程都拥有自己的地址空间(也叫虚拟空间),用 mm_struct 来描述。

active_mm 是为内核线程而引入的,因为内核线程没有自己的地址空间,为了让内核线程与普通进程具有统一的上下文切换方式,当内核线程进行上下文切换时,让切换进来的线程的 active_mm 指向刚被调度出去的进程的 active_mm(如果进程的mm 域不为空,则其 active_mm 域与 mm 域相同)。

linux 2.6 中 sched_class 表示该进程所属的调度器类有3种:

  • 实时调度器 (RT,rt_sched_class) : 为每个优先级维护一个队列;
  • 完全公平调度器 (CFS,fair_sched_class) : 采用完全公平调度算法,引入虚拟运行时间的概念。
  • 空闲调度器(idle_sched_class) : 为每个 CPU 都会有一个 idle 线程,当没有其他进程可以调度时,调度运行idle线程。

进程的调度策略有5种,用户可以调用调度器里不同的调度策略:

  • SCHED_FIFO :采用了先入先出,不使用时间片的调度算法。若处于可执行状态,就会一直执行,没有更高优先级的情况下,进程只能等待其主动让出 cpu;
  • SCHED_RR :时间片轮转,进程用完时间片后加入优先级对应的运行队列的尾部,把 CPU 让给同一优先级的其他进程;
  • SCHED_NORMAL : 使 task 选择 CFS 调度器来调度运行;
  • SCHED_BATCH:批量处理,使 task 选择 CFS 调度器来调度运行;
  • SCHED_IDLE: 使 task 选择 CFS 调度器来调度运行

在每个 CPU 中都有一个自身的运行队列 rq,每个活动进程只出现在一个运行队列中,在多个 CPU 上同时运行一个进程是不可能的。

运行队列是使用如下结构实现的:

struct rq {
//运行队列中进程的数目
unsigned long nr_running;
//进程切换总数
u64 nr_switches;
//用于完全公平调度器的就绪队列
struct cfs_rq cfs;
//用于实时调度器的就绪队列
struct rt_rq rt;
//记录本cpu尚处于TASK_UNINTERRUPTIBLE状态的进程数,和负载信息有关
unsigned long nr_uninterruptible;
//curr指向当前运行的进程实例,idle 指向空闲进程的实例
struct task_struct *curr, *idle;
//上一次调度时的mm结构
struct mm_struct *prev_mm;
...
};

每个运行队列中有2个调度队列:CFS 调度队列 和 RT 调度队列。

tast 作为调度实体加入到 CPU 中的调度队列中。

系统中所有的运行队列都在 runqueues 数组中,该数组的每个元素分别对应于系统中的一个 CPU。在单处理器系统中,由于只需要一个就绪队列,因此数组只有一个元素。

static DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);

内核也定义了一下便利的宏,其含义很明显。

#define cpu_rq(cpu) (&per_cpu(runqueues, (cpu)))
#define this_rq() (&__get_cpu_var(runqueues))
#define task_rq(p) cpu_rq(task_cpu(p))
#define cpu_curr(cpu) (cpu_rq(cpu)->curr)

进程调度与切换

在分析调度流程之前,我们先来看在什么情况下要执行调度程序,我们把这种情况叫做调度时机。

Linux 调度时机主要有。

  1. 进程状态转换的时刻:进程终止、进程睡眠;
  2. 当前进程的时间片用完时(current->counter=0);
  3. 设备驱动程序;
  4. 进程从中断、异常及系统调用返回到用户态时。

时机1,进程要调用 sleep() 或 exit() 等函数进行状态转换,这些函数会主动调用调度程序进行进程调度。

时机2,由于进程的时间片是由时钟中断来更新的,因此,这种情况和时机4 是一样的。

时机3,当设备驱动程序执行长而重复的任务时,直接调用调度程序。在每次反复循环中,驱动程序都检查 need_resched 的值,如果必要,则调用调度程序 schedule() 主动放弃 CPU。

时机4 , 如前所述, 不管是从中断、异常还是系统调用返回, 最终都调用 ret_from_sys_call(),由这个函数进行调度标志的检测,如果必要,则调用调用调度程序。那么,为什么从系统调用返回时要调用调度程序呢?这当然是从效率考虑。从系统调用返回意味着要离开内核态而返回到用户态,而状态的转换要花费一定的时间,因此,在返回到用户态前,系统把在内核态该处理的事全部做完。

Linux 的调度程序是一个叫 Schedule() 的函数,这个函数来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等。

Schedule 的实现

asmlinkage void __sched schedule(void)
{
/*prev 表示调度之前的进程, next 表示调度之后的进程 */
struct task_struct *prev, *next;
long *switch_count;
struct rq *rq;
int cpu;
need_resched:
preempt_disable(); //关闭内核抢占
cpu = smp_processor_id(); //获取所在的cpu
rq = cpu_rq(cpu); //获取cpu对应的运行队列
rcu_qsctr_inc(cpu);
prev = rq->curr; /*让prev 成为当前进程 */
switch_count = &prev->nivcsw;
/释放全局内核锁,并开this_cpu 的中断/
release_kernel_lock(prev);
need_resched_nonpreemptible:
__update_rq_clock(rq); //更新运行队列的时钟值
...
if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
// 对应到CFS,则为 put_prev_task_fair
prev->sched_class->put_prev_task(rq, prev); //通知调度器类当前运行进程要被另一个进程取代
/pick_next_task以优先级从高到底依次检查每个调度类,从最高优先级的调度类中选择最高优先级的进程作为
下一个应执行进程(若其余都睡眠,则只有当前进程可运行,就跳过下面了)/
next = pick_next_task(rq, prev); //选择需要进行切换的task
//进程prev和进程next切换前更新各自的sched_info
sched_info_switch(prev, next);
if (likely(prev != next)) {
rq->nr_switches++;
rq->curr = next;
++switch_count;
//完成进程切换(上下文切换)
context_switch(rq, prev, next); / unlocks the rq */
} else
spin_unlock_irq(&rq->lock);
if (unlikely(reacquire_kernel_lock(current) < 0)) {
cpu = smp_processor_id();
rq = cpu_rq(cpu);
goto need_resched_nonpreemptible;
}
preempt_enable_no_resched();
if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
goto need_resched;
}

从代码分析来看,Schedule 主要完成了2个功能:

  • pick_next_task 以优先级从高到底依次检查每个调度类,从最高优先级的调度类中选择最高优先级的进程作为下一个应执行进程。
  • context_switch 完成进程的上下文切换。

进程上下文切换

static inline void
context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)
{
struct mm_struct *mm, *oldmm;
prepare_task_switch(rq, prev, next);
mm = next->mm; //获取要执行进程的mm字段
oldmm = prev->active_mm; //被切换出去进程的 active_mm 字段
//mm为空,说明是一个内核线程
if (unlikely(!mm)) {
//内核线程共享上一个运行进程的mm
next->active_mm = oldmm; //借用切换出去进程的 mm_struct
//增加引用计数
atomic_inc(&oldmm->mm_count);
//惰性TLB,因为内核线程没有虚拟地址空间的用户空间部分,告诉底层体系结构无须切换
enter_lazy_tlb(oldmm, next);
} else
//进程切换包括进程的执行环境切换和运行空间的切换。运行空间的切换是有switch_mm完成的。若是用户进程,则切换运行空间
switch_mm(oldmm, mm, next);
//若上一个运行进程是内核线程
if (unlikely(!prev->mm)) {
prev->active_mm = NULL; //断开内核线程与之前借用的地址空间联系
//更新运行队列的prev_mm成员,为之后归还借用的mm_struct做准备
rq->prev_mm = oldmm;
}
/* Here we just switch the register state and the stack. */
//切换进程的执行环境
switch_to(prev, next, prev);
barrier();
//进程切换之后的处理工作
finish_task_switch(this_rq(), prev);
}

进程上下文切换包括进程的地址空间的切换和执行环境的切换。

  • switch_mm 完成了进程的地址空间的切换:如果新进程有自己的用户空间,也就是说,如果 next->mm 与 next->active_mm 相同,那么,switch_mm() 函数就把该进程从内核空间切换到用户空间,也就是加载next 的页目录。如果新进程无用户空间(next->mm 为空),也就是说,如果它是一个内核线程,那它就要在内核空间运行,因此,需要借用前一个进程(prev)的地址空间,因为所有进程的内核空间都是共享的,因此,这种借用是有效的。
  • switch_to 完成了执行环境的切换,该宏实现了进程之间的真正切换。
  •  
//进程切换包括进程的执行环境切换和运行空间的切换。运行空间的切换是有switch_mm完成的
static inline void switch_mm(struct mm_struct *prev,
struct mm_struct *next,
struct task_struct tsk)
{
//得到当前进程运行的cpu
int cpu = smp_processor_id();
//若要切换的prev != next,执行切换过程
if (likely(prev != next)) {
/ stop flush ipis for the previous mm */
//清除prev的cpu_vm_mask,标志prev已经弃用了当前cpu
cpu_clear(cpu, prev->cpu_vm_mask);
#ifdef CONFIG_SMP
//在smp系统中,更新cpu_tlbstate
per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
per_cpu(cpu_tlbstate, cpu).active_mm = next;
#endif
//设置cpu_vm_mask,表示next占用的当前的cpu
cpu_set(cpu, next->cpu_vm_mask);
/* Re-load page tables */
//加载CR3
load_cr3(next->pgd);
/*
• load the LDT, if the LDT is different:
*/
//若ldt不相同,还要加载ldt
if (unlikely(prev->context.ldt != next->context.ldt))
load_LDT_nolock(&next->context);
}
#ifdef CONFIG_SMP
else {
per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
//prev == next 那当前cpu中的active_mm就是prev,也即是next
BUG_ON(per_cpu(cpu_tlbstate, cpu).active_mm != next);
/*
在smp系统中,虽然mm时一样的,但需要加载CR3
执行cpu_test_and_set来判断next是否正运行在此cpu上,这里是判断在切换next是否运行
在当前的cpu中,
假设cpu为1,一个进程在1上执行时候,被调度出来,再次调度的时候,
又发生在cpu1上
*/
if (!cpu_test_and_set(cpu, next->cpu_vm_mask)) {
/* We were in lazy tlb mode and leave_mm disabled
• tlb flush IPI delivery. We must reload %cr3.
*/
load_cr3(next->pgd);
load_LDT_nolock(&next->context);
}
}
#endif
}

对于 switch_mm 处理,关键的一步就是它将新进程页面目录的起始物理地址装入到寄存器 CR3 中。CR3 寄存器总是指向当前进程的页面目录。

#define switch_to(prev,next,last) do { 
unsigned long esi,edi; 
/*分别保存了eflags、ebp、esp,flags和 ebp他们是被保存在prev进程(其实就是要被切换出去的进程)的内核堆栈中的*/
asm volatile("pushflnt" /* Save flags */ 
"pushl %%ebpnt" 

//%0为 prev->thread.esp, %1 为 prev->thread.eip
"movl %%esp,%0nt" /* save ESP */ 

//%5为 next->thread.esp,%6 为 next->thread.eip
"movl %5,%%espnt" /* restore ESP */ 

/*将标号为1所在的地址,也即是"popl %%ebpnt" 指令所在的地址保存在prev->thread.eip中,作为prev进程
下一次被调度运行而切入时的返回地址,因此可以知道,每个进程调离时都要执行"movl $1f,%1nt",所以
这就决定了每个进程在受到调度恢复运行时都会从标号1处也即是"popl %%ebpnt"开始执行。
但是有一个例外,那就是新创建的进程,新创建的进程并没有在上一次调离时执行上面的指令,所以一来要将其
task_struct中的thread.eip事先设置好,二来所设置的返回地址也未必是这里的标号1所在的地址,这取决于系统空间
堆栈的设置。事实上可以下fork()中可以看到,这个地址在copy_thread()中设置为ret_from_fork,其代码在entry.S中,
也就是对于新创建的进程,在调用schedule_tail()后直接转到了ret_from_sys_call, 也即是返回到了用户空间去了
*/
"movl $1f,%1nt" /* save EIP */ 

/*将next->thread.eip压入栈中,这里的next->thread.eip正是next进程上一次被调离在"movl $1f,%1nt"保存的,也即是
指向标号为1的地方,也即是"popl %%ebpnt"指令所在的地址*/
"pushl %6nt" /* restore EIP */ 

/*需要注意的是 __switch_to 是经过regparm(3)来修饰的,这个是gcc的一个扩展语法。
即从eax,ebx,ecx寄存器取函数的参数。这样,__switch_to函数的参数就是从寄存器中取的。
并是不向普通函数那样从堆栈中取的。在__switch_to之前,将next->thread.eip压栈了,这样从函数返回后
,它的下一条运行指令就是 next->thread.eip了。

对于新创建的进程。我们设置了p->thread.eip = ret_from_fork.这样子进程被切换进来之后,就会通过
ret_from_fork返回到用户空间了。
对于已经运行的进程,我们这里可以看到,在进程被切换出去的时候,prev->thread.eip被设置了标号1的地址,
即是从标号1的地址开始运行的。
标号1的操作:
恢复ebp(popl %%ebp)
恢复flags(popf1)
这样就恢复了进程的执行环境。

从代码可以看到,在进程切换时,只保留了flags esp和ebp寄存器,显示的用到了eax和edx,
那其他寄存器怎么保存的呢?
实际上过程切换只是发生在内核态,对于内核态的寄存器来说,它的段寄存器都是一样的,所以不需要保存

*/
/*通过jmp指令转入到函数__switch_to中,由于上一行"pushl %6nt"把next->thread.eip,也即是标号为1的地址压到栈中,所以
跳入__switch_to执行完后,执行标记为1的地方,也即是"popl %%ebpnt" 指令*/
"jmp __switch_ton" 
"1:t" 
"popl %%ebpnt" 
"popfl" 
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), 
"=a" (last),"=S" (esi),"=D" (edi) 
:"m" (next->thread.esp),"m" (next->thread.eip), 
"2" (prev), "d" (next)); 
} while (0)

switch_to 把寄存器中的值比如esp等存放到进程thread结构中,保存现场一边后续恢复,同时调用 __switch_to 完成了堆栈的切换。

在进程的 task_struct 结构中有个重要的成分 thread,它本身是一个数据结构 thread_struct, 里面记录着进程在切换时的(系统空间)堆栈指针,取指令地址(也就是“返回地址”)等关键性的信息。

/*
__switch_to 处理的主要逻辑是TSS,其核心就是load_esp0将TSS中的内核空间(0级)堆栈指针换成next->esp0. 这是因为
cpu在穿越中断门或者陷阱门时要根据新的运行级别从TSS中取得进程在系统空间的堆栈指针,其次,段寄存器fs和gs的内容也做了
相应的切换。同时cpu中为debug而设计的一些寄存器以及说明进程I/O 操作权限的位图。
*/
struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
struct thread_struct *prev = &prev_p->thread,
*next = &next_p->thread;
int cpu = smp_processor_id();
struct tss_struct *tss = &per_cpu(init_tss, cpu);

...

/* 将TSS 中的内核级(0 级)堆栈指针换成next->esp0,这就是next 进程在内核栈的指针*/
load_esp0(tss, next);


//为prev保存gs
savesegment(gs, prev->gs);


//从next的tls_array缓存中加载线程的Thread-Local Storage描述符
load_TLS(next, cpu);


/*若当前特权级别是0且prev->iopl != next->iopl,则恢复IOPL设置set_iopl_mask
*/
if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl))
set_iopl_mask(next->iopl);

/*根据thread_info的TIF标志_TIF_WORK_CTXSW_PREV和 _TIF_WORK_CTXSW_NEXT 判断是否需要处理
debug寄存器和IO位图*/
if (unlikely(task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV ||
task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT))
__switch_to_xtra(prev_p, next_p, tss);

//设置cpu的lazy模式
arch_leave_lazy_cpu_mode();

//若fpu_counter > 5 则恢复next_p 的FPU寄存器
if (next_p->fpu_counter > 5)
math_state_restore();

//若需要,恢复gs寄存器
if (prev->gs | next->gs)
loadsegment(gs, next->gs);

x86_write_percpu(current_task, next_p);

return prev_p;
}

关于__switch_to 的工作就是处理 TSS (任务状态段)。

TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程)切换时的任务现场信息。

linux 为每一个 CPU 提供一个 TSS 段,并且在 TR 寄存器中保存该段。

linux 中之所以为每一个 CPU 提供一个 TSS 段,而不是为每个进程提供一个TSS 段,主要原因是 TR 寄存器永远指向它,在任务切换的适合不必切换 TR 寄存器,从而减小开销。

在从用户态切换到内核态时,可以通过获取 TSS 段中的 esp0 来获取当前进程的内核栈 栈顶指针,从而可以保存用户态的 cs,esp,eip 等上下文。

TSS 在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的执行。

在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到 TR(任务寄存器)所指定的任务的 TSS 中;然后,下一任务的 TSS 被装入 TR;最后,从 TR 所指定的 TSS 中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在 TSS 中保存任务现场各寄存器状态的完整映象,实现任务的切换。

因此,__switch_to 核心内容就是将 TSS 中的内核空间(0级)堆栈指针换成 next->esp0。这是因为 CPU 在穿越中断门或者陷阱门时要根据新的运行级别从TSS中取得进程在系统空间的堆栈指针。

thread_struct.esp0 指向进程的系统空间堆栈的顶端。当一个进程被调度运行时,内核会将这个变量写入 TSS 的 esp0 字段,表示这个进程进入0级运行时其堆栈的位置。换句话说,进程的 thread_struct 结构中的 esp0 保存着其系统空间堆栈指针。当进程穿过中断门、陷阱门或者调用门进入系统空间时,处理器会从这里恢复期系统空间栈。

由于栈中变量的访问依赖的是段、页、和 esp、ebp 等这些寄存器,所以当段、页、寄存器切换完以后,栈中的变量就可以被访问了。

因此 switch_to 完成了进程堆栈的切换,由于被切进的进程各个寄存器的信息已完成切换,因此 next 进程得以执行指令运行。

由于 A 进程在调用 switch_to 完成了与 B 进程堆栈的切换,也即是寄存器中的值都是 B 的,所以 A 进程在 switch_to 执行完后,A停止运行,B开始运行,当过一段时间又把 A 进程切进去后,A 开始从 switch_to 后面的代码开始执行。

schedule 的调用流程如下:



Tags:Linux   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
微软 Win11 Linux 子系统(WSL)发布 2.2.2 版本
IT之家 4 月 8 日消息,微软近日更新 Windows Subsystem for Linux(WSL),最新 2.2.2 版本中带来了诸多改进,重点更新了 nft 规则,可以让 IPv6 流量通过 Linux 容器。图源: dev.to,AI...【详细内容】
2024-04-08  Search: Linux  点击:(6)  评论:(0)  加入收藏
Linux 6.9-rc1 内核发布:AMD P-State 首选核心、BH 工作队列
IT之家 3 月 25 日消息,Linus Torvalds 宣布,Linux 6.9 内核的首个 RC(候选发布)版 Linux 6.9-rc1 发布。▲ Linux 6.9-rc1Linus 表示,Linux 内核 6.9 看起来是一个“相当正常”...【详细内容】
2024-03-25  Search: Linux  点击:(11)  评论:(0)  加入收藏
Win + Ubuntu 缝合怪:第三方开发者推出“Wubuntu”Linux 发行版
IT之家 2 月 26 日消息,一位第三方开发者推出了一款名为“Wubuntu”的缝合怪 Linux 发行版,系统本身基于 Ubuntu,但界面为微软 Windows 11 风格,甚至存在微软 Windows 徽标。据...【详细内容】
2024-02-27  Search: Linux  点击:(49)  评论:(0)  加入收藏
2024 年 Linux 和开源的六大趋势预测
让我们尝试预测未来吧!新的一年快乐,朋友们 ✨2024 年的钟声已经敲过,我们有必要去预见一下将塑造本年度的各种潮流。我们不能预见未来,所以无法精确预知将会发生什么,但根据目前...【详细内容】
2024-01-09  Search: Linux  点击:(92)  评论:(0)  加入收藏
Linux 启动过程详解
第 1 步 - 当我们打开电源时,BIOS(基本输入/输出系统)或UEFI(统一可扩展固件接口)固件从非易失性存储器加载,并执行POST(开机自检)。第 2 步 - BIOS/UEFI 检测连接到系统的设备,包括...【详细内容】
2024-01-02  Search: Linux  点击:(88)  评论:(0)  加入收藏
对 Bash 感到厌倦?教你如何在 Linux 中更改默认 Shell
Bash 并不是唯一可供选择的 Shell。还存在数量众多的 Shell,它们都有一些独特的特性,例如 Zsh、Fish、Ksh 和 Xonsh。在你的系统中,你可以同时安装多个 Shell。要想将另一个 Sh...【详细内容】
2023-12-14  Search: Linux  点击:(199)  评论:(0)  加入收藏
如何解决Linux 服务器性能问题?
Linux 服务器以其稳定性和可靠性而闻名,但与任何系统一样,它们有时也会遇到性能问题。当您的 Linux 服务器的性能开始下降时,必须及时解决问题以确保应用程序和服务的顺利运行...【详细内容】
2023-12-14  Search: Linux  点击:(230)  评论:(0)  加入收藏
适用于 Linux 的 LibreOffice 替代品
LibreOffice 是一个出色的开源文档套件。它预装在许多 Linux 发行版上,应该足以满足大多数用户的需求。然而,有些人可能不喜欢它的用户界面和功能集。某些用户可能想尝试其他...【详细内容】
2023-11-30  Search: Linux  点击:(122)  评论:(0)  加入收藏
十个最佳免费 Linux 防火墙工具
概述防火墙是保护网络边界的关键。防火墙会阻止敏感端口并过滤传入和传出流量,以阻止恶意连接并确保不会发生未经请求的数据交换。在自由开源软件的世界里,有很多防火墙解决方...【详细内容】
2023-11-28  Search: Linux  点击:(173)  评论:(0)  加入收藏
Linux 内核调度器源码解析:从调度入口到挑选下一个进程
在Linux内核中,调度器(scheduler)扮演着至关重要的角色,决定了哪个进程将获得CPU的执行时间。本文将深入剖析内核中调度器的代码实现,从入口函数开始,一步步分析如何选择下一个要...【详细内容】
2023-11-27  Search: Linux  点击:(269)  评论:(0)  加入收藏
▌简易百科推荐
微软 Win11 Linux 子系统(WSL)发布 2.2.2 版本
IT之家 4 月 8 日消息,微软近日更新 Windows Subsystem for Linux(WSL),最新 2.2.2 版本中带来了诸多改进,重点更新了 nft 规则,可以让 IPv6 流量通过 Linux 容器。图源: dev.to,AI...【详细内容】
2024-04-08    IT之家  Tags:Linux   点击:(6)  评论:(0)  加入收藏
从原理到实践:深入探索Linux安全机制
Linux 是一种开源的类Unix操作系统内核,由Linus Torvalds在1991年首次发布,其后又衍生出许多不同的发行版(如Ubuntu、Debian、CentOS等)。前言本文将从用户和权限管理、文件系统...【详细内容】
2024-03-27  凡夫编程  微信公众号  Tags:Linux安全   点击:(16)  评论:(0)  加入收藏
在Linux系统中,如何处理内存管理和优化的问题?
本文对 Linux 内存管理和优化的一些高级技巧的详细介绍,通过高级的内存管理技巧,可以帮助系统管理员和开发人员更好地优化 Linux 系统的内存使用情况,提高系统性能和稳定性。在...【详细内容】
2024-03-26  编程技术汇  微信公众号  Tags:Linux   点击:(10)  评论:(0)  加入收藏
Linux 6.9-rc1 内核发布:AMD P-State 首选核心、BH 工作队列
IT之家 3 月 25 日消息,Linus Torvalds 宣布,Linux 6.9 内核的首个 RC(候选发布)版 Linux 6.9-rc1 发布。▲ Linux 6.9-rc1Linus 表示,Linux 内核 6.9 看起来是一个“相当正常”...【详细内容】
2024-03-25    IT之家  Tags:Linux   点击:(11)  评论:(0)  加入收藏
轻松实现Centos系统的软件包安装管理:yum指令实战详解
yum 是一种用于在 CentOS、Red Hat Enterprise Linux (RHEL) 等基于 RPM 的 Linux 发行版上安装、更新和管理软件包的命令行工具。它可以自动解决软件包依赖关系,自动下载并...【详细内容】
2024-02-27  凡夫贬夫  微信公众号  Tags:Centos   点击:(54)  评论:(0)  加入收藏
Win + Ubuntu 缝合怪:第三方开发者推出“Wubuntu”Linux 发行版
IT之家 2 月 26 日消息,一位第三方开发者推出了一款名为“Wubuntu”的缝合怪 Linux 发行版,系统本身基于 Ubuntu,但界面为微软 Windows 11 风格,甚至存在微软 Windows 徽标。据...【详细内容】
2024-02-27    IT之家  Tags:Ubuntu   点击:(49)  评论:(0)  加入收藏
Linux中磁盘和文件系统工作原理解析
在Linux系统中,一切皆文件的概念意味着所有的资源,包括普通文件、目录以及设备文件等,都以文件的形式存在。这种统一的文件系统管理方式使得Linux系统具有高度的灵活性和可扩展...【详细内容】
2024-02-20  王建立    Tags:Linux   点击:(51)  评论:(0)  加入收藏
Linux子系统概览
inux操作系统是一个模块化的系统,由多个子系统组成。这些子系统协同工作,使Linux能够执行各种任务。了解Linux的子系统有助于更好地理解整个操作系统的运作机制。以下是Linux...【详细内容】
2024-02-01    简易百科  Tags:Linux   点击:(77)  评论:(0)  加入收藏
Linux内核:系统之魂与交互之源
内核,作为任何基于Linux的操作系统的心脏,扮演着至关重要的角色。它不仅是计算机系统软件与硬件之间的桥梁,更是确保系统稳定、高效运行的关键。内核提供了一系列核心功能,为上...【详细内容】
2024-02-01  松鼠宝贝    Tags:Linux内核   点击:(68)  评论:(0)  加入收藏
如何确保Linux进程稳定与持久
在Linux系统中,进程的稳定性与持久性对于维持系统的持续运行至关重要。然而,由于各种原因,进程可能会面临崩溃或系统重启的情况。为了确保关键进程能够持续运行,我们必须采取一...【详细内容】
2024-01-19  松鼠宝贝    Tags:Linux进程   点击:(85)  评论:(0)  加入收藏
站内最新
站内热门
站内头条