一起聊聊内核中的线程:操作函数、进程状态、task_struct、举个例子、

简介: 一起聊聊内核中的线程:操作函数、进程状态、task_struct、举个例子、

Perface

内核线程是直接由内核本身启动的进程

内核线程实际上是将内核函数委托给独立的进程,它与内核中的其他进程”并行”执行。

内核线程经常被称之为内核守护进程。内核线程是被调度的实体,它被加入到某种数据结构中,调度程序根据实际情况进行线程的调度。 内核线程与用户态线程的作用类似,通常用于执行某些周期性的计算任务,或者在后台执行需要大量计算的任务

本文主要介绍一下内核线程操作相关的API的使用,以及内核线程的实现基本原理,更深入的内容在后续文章中介绍。

内核线程操作函数

内核线程操作涉及的函数(API)主要是创建、调度和停止等函数。操作起来也是比较简单的。

下面分别介绍一下这些接口的定义。

创建线程

创建线程的函数为kthread_create,如下是函数的原型,该函数实际上是函数kthread_create_on_node的一个宏定义。后者则是在某个CPU上创建一个线程。

该函数的前两个参数分别是线程主函数指针和函数的参数而后面的参数通过变参数的方式为线程命名

#define kthread_create(threadfn, data, namefmt, arg...) \
       kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)

唤醒线程

通过该函数创建的线程处于非运行状态,需要调用wake_up_process函数将其唤醒后才可以在CPU上运行。

int wake_up_process(struct task_struct *p)

创建并运行线程

在内核的API中有另外一个接口可以直接创建一个处于运行状态的线程,其定义如下。这里其实就是调用了上文描述的两个函数

#define kthread_run(threadfn, data, namefmt, ...)                          \
({                                                                         \
    struct task_struct *__k                                            \
            = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    if (!IS_ERR(__k))                                                  \
            wake_up_process(__k);                                      \
    __k;                                                               \
})

停止线程

线程也可以被停止,此时主函数将会退出,当然需要主函数的实现考虑该问题。如下是停止线程的函数接口。

int kthread_stop(struct task_struct *k)

线程的调度

内核线程创建完成后将一直运行下去,除非遇到了阻塞事件或者自己将自己调度出去。通过下面函数,线程可以将自己调度出去。调度出去的含义就是将CPU让给其它线程

asmlinkage __visible void __sched schedule(void)

整个简单例子

前面介绍了内核线程基本原理及相关的API,下面我们将开发一个内核线程的基本实例。

这个实例是在一个内核模块中启动一个内核线程。内核线程的作用很简单,就是定时的向系统日志中输出一个字符串本例的目的主要是介绍如何创建、使用和销毁一个内核线程。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/socket.h>
#include <net/sock.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#define BUF_SIZE 1024
struct task_struct *main_task;
/* 这个函数用于将内核线程置于休眠状态,也就是将其调度出
 * 队列。*/
static inline void sleep(unsigned sec)
{
        __set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(sec * HZ);
}
/* 线程函数, 这个是线程执行的主体 */
static int multhread_server(void *data)
{
        int index = 0;
        /* 在线程没有被停止的情况下,循环向系统日志输出
         * 内容, 完成后休眠1秒。*/
        while (!kthread_should_stop()) {
                printk(KERN_NOTICE "thread run %d\n", index);
                index ++; 
                sleep(1);
        }
        return 0;
}
static int multhread_init(void)
{
        ssize_t ret = 0;
        printk("Hello, thread! \n");
        /* 创建并启动一个内核线程, 这里参数为线程函数,
         * 函数的参数(NULL),和线程名称。 */
        main_task = kthread_run(multhread_server,
                                  NULL,
                                  "multhread_server");
        if (IS_ERR(main_task)) {
                ret = PTR_ERR(main_task);
                goto failed;
        }
failed:
        return ret;
}
static void multhread_exit(void)
{
        printk("Bye thread!\n");
        /* 停止线程 */
        kthread_stop(main_task);
}
module_init(multhread_init);
module_exit(multhread_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SunnyZhang<shuningzhang@126.com>");

基本实现原理

创建线程

无论是用户态的进程还是内核线程,在内核态都是线程

在Linux操作系统,创建线程实质是是对父进程(线程)进行克隆的过程。 目前,在3.x以后的版本中,内核线程的创建都有一个名为kthreadd的后台线程操作完成。

创建线程的接口只是用于创建任务,并加到任务列表中,并等待后台线程的具体处理

前文中创建线程的函数kthread_create或者kthread_run 调用的函数是__kthread_create_on_node,也就是在某个CPU上创建线程。

该函数其实只是创建一个创建线程的请求,如下是裁剪的代码,核心内容如下:

struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
                                                    void *data, int node,
                                                    const char namefmt[],
                                                    va_list args)
{
        DECLARE_COMPLETION_ONSTACK(done);
        struct task_struct *task;
        struct kthread_create_info *create = kmalloc(sizeof(*create),
                                                     GFP_KERNEL);
        if (!create)
                return ERR_PTR(-ENOMEM);
        create->threadfn = threadfn;
        create->data = data;
        create->node = node;
        create->done = &done;
        spin_lock(&kthread_create_lock);
        /* 将创建任务添加到链表中 */
        list_add_tail(&create->list, &kthread_create_list);
        spin_unlock(&kthread_create_lock);
        wake_up_process(kthreadd_task);
        ... ...
}

具体创建工作在名为kthreadd的后台线程中进行,该线程会从队列中获取创建请求,并逐个创建线程。

创建线程调用的接口为kernel_thread,该函数实现从父线程克隆子线程的操作,并建立父子线程的关联关系。

线程调度

Linux的线程管理和调度是一个非常复杂的话题,很难用一篇文章说清楚,我们这里只是介绍一下基本原理。

目前Linux操作系统默认使用的是CFS调度算法,该算法是基于优先级和时间片的算法,这个算法包含4部分的内容:

  • 时间记账
  • 进程选择
  • 调度器入口
  • 睡眠和唤醒

时间记账用于记录进程运行的虚拟时间,而进程选择则是根据策略选择应该将那个进程调度到CPU上运行。进程选择使用的数据结构是红黑树,红黑树是一个自平衡二叉树,也就是其中的数据是有序的,这样可以很容易的找到目的数据。

Linux内核在具体实现的时候又使用了一个技巧,也就是将下一个要调度的进程放入缓存中,这样就可以直接找到该进程进行调度,降低了检索时间。

Linux内核的调度入口是schedule函数,当线程调用该函数时将触发线程调度。这个函数实现本身很简单,但其内部调用context_switch函数实现真正的调度,在调用该函数之前会通过调度类获取目的进程。

static __always_inline struct rq * 
context_switch(struct rq *rq, struct task_struct *prev,
               struct task_struct *next, struct rq_flags *rf)

这样,通过context_switch函数就可以将当前进程调度出去,而将新的进程调度进来

context_switch最终会调度到一个平台相关的函数,而这个函数是汇编语言实现的,主要实现寄存器和堆栈的处理,并最终完成进程的切换。

进程的状态

1、R

处于运行或可运行状态,即进程正在运行或在运行队列(可执行队列)中等待。只有在该状态的进程才可能在CPU上运行,同一时刻可能有多个进程处于该状态。

(注:很多教科书上将正在CPU上执行的进程的状态定义为Running,将可执行但尚未被调度执行的进程状态定义为Ready,这2种状态在Linux下统一为R状态)

2、S

处于可中断的睡眠状态,即进程在休眠中,由于在等待某个事件的完成(或等待某个条件的形成或等待某个信号等)

(注:等待socket连接、等待信号量等)而被挂起;当这些事件发生时,对应的等待队列中的一个或多个进程将被唤醒。一般情况下,进程列表中绝大多数进程都处于该状态。

3、D

处于不可中断的睡眠状态,不可中断指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号,无法用kill命令杀死,进程必须等待直到有中断发生。

4、T

处于暂停或跟踪状态。进程收到SIGSTOP、SIGSTP、SIGTIN、SIGTOU等信号进入暂停状态(除非进程处于不可中断的睡眠状态);当接着向进程发送1个SIGCONT信号,进程可以从暂停状态恢复到运行或能运行状态。

当进程被跟踪时,它处于被跟踪状态。“被跟踪”指进程暂停下来,等待跟踪它的进程对它进行操作。例如在GDB调试中,对被跟踪的进程设置某个断点,进程执行到断点处停下来的时候就处于被跟踪状态。

暂停与跟踪状态还是有区别的,被跟踪状态相当于在暂停状态之上多了一层保护,处于被跟踪状态的进程不能响应SIGCONT信号而被唤醒,只能等到调试进程通过ptrace系统调用执行ptrace_cont、ptrace_detach等操作(通过ptrace系统调用的参数指定操作),或调试进程退出,被调试的进程才能恢复到R状态。

5、Z

处于僵死状态,也称退出状态。它指进程已经结束,放弃了几乎所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置来记载该进程的退出状态等信息(task_struct结构体[保存了该进程的退出码])供其他进程收集。

6、X

进程在退出过程中可能不会保留它的task_struct。例如某个进程是多线程程序中被detach过的进程;或者父进程通过设置SIGCHLD信号的Handler为SIG_IGN,显示的忽略了SIGCHLD信号。

此时该进程被置于exit_dead退出状态,这意味着接下来的代码立即会将该进程彻底释放。故exit_dead状态非常短暂,几乎不可能通过ps命令捕捉到。

task_struct

在前面我们看到了频繁出现的task_struct这个结构体,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linus内核的进程控制块是task_struct的结构体。

struct task_struct{
volatile long state;         //说明了该进程是否可以执行,还是可中断等信息
unsigned long flags;         //Flage 是进程号,在调用fork()时给出
int sigpending;              //进程上是否有待处理的信号
mm_segment_t addr_limit;     //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
                           //0-0xBFFFFFFF for user-thead
                           //0-0xFFFFFFFF for kernel-thread
//调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
volatile long need_resched;
int lock_depth;  //锁深度
long nice;       //进程的基本时间片
//进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
unsigned long policy;
struct mm_struct *mm; //进程内存管理信息
int processor;
//若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
unsigned long cpus_runnable, cpus_allowed;
struct list_head run_list; //指向运行队列的指针
unsigned long sleep_time;  //进程的睡眠时间
//用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
struct task_struct *next_task, *prev_task;
struct mm_struct *active_mm;
struct list_head local_pages;       //指向本地页面     
unsigned int allocation_order, nr_local_pages;
struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式
int exit_code, exit_signal;
int pdeath_signal;     //父进程终止时向子进程发送的信号
unsigned long personality;
//Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
int did_exec:1;
pid_t pid;    //进程标识符,用来代表一个进程
pid_t pgrp;   //进程组标识,表示进程所属的进程组
pid_t tty_old_pgrp;  //进程控制终端所在的组标识
pid_t session;  //进程的会话标识
pid_t tgid;
int leader;     //表示进程是否为会话主管
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
struct list_head thread_group;   //线程链表
struct task_struct *pidhash_next; //用于将进程链入HASH表
struct task_struct **pidhash_pprev;
wait_queue_head_t wait_chldexit;  //供wait4()使用
struct completion *vfork_done;  //供vfork() 使用
unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
//内存缺页和交换信息:
//min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
//设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
//cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
//在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1; //表示进程的虚拟地址空间是否允许换出
//进程认证信息
//uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
//euid,egid为有效uid,gid
//fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
//系统的访问权限时使用他们。
//suid,sgid为备份uid,gid
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups; //记录进程在多少个用户组中
gid_t groups[NGROUPS]; //记录进程所在的组
//进程的权能,分别是有效位集合,继承位集合,允许位集合
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
struct rlimit rlim[RLIM_NLIMITS];  //与进程相关的资源限制信息
unsigned short used_math;   //是否使用FPU
char comm[16];   //进程正在运行的可执行文件名
 //文件系统信息
int link_count, total_link_count;
//NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
struct tty_struct *tty;
unsigned int locks;
//进程间通信信息
struct sem_undo *semundo;  //进程在信号灯上的所有undo操作
struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
//进程的CPU状态,切换时,要保存到停止进程的task_struct中
struct thread_struct thread;
  //文件系统信息
struct fs_struct *fs;
  //打开文件信息
struct files_struct *files;
  //信号处理函数
spinlock_t sigmask_lock;
struct signal_struct *sig; //信号处理函数
sigset_t blocked;  //进程当前要阻塞的信号,每个信号对应一位
struct sigpending pending;  //进程上是否有待处理的信号
unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
u32 parent_exec_id;
u32 self_exec_id;
spinlock_t alloc_lock;
void *journal_info;
};

task_struct结构体是Linux内核中的一种数据结构,接下来就进入本文的重点:剖析task_struct结构体

(1)进程的标识 PID(process identifier):

  • pid_t pid;//进程的唯一标识
  • pid_t tgid;// 线程组的领头线程的pid成员的值

32位无符号整型数据。但最大值取32767。表示每一个进程的标识符。也是内核提供给用户程序的借口,用户程序通过pid操作程序。因为Unix的原因引入还引入了线程组的概念。称为:tgid。一个线程组中的所有线程使用和该线程组中的第一个轻量级线程的pid,被存在tgid成员中。当进程没有线程时,tgid=pid;当有多线程时,tgid表示的是主线程的id,而pid表示每一个线程自己的id。

(2)进程的状态 volatile long state

state的可能取值是:

  • #define TASK_RUNNING 0//进程要么正在执行,要么准备执行
  • #define TASK_INTERRUPTIBLE 1 //可中断的睡眠,可以通过一个信号唤醒
  • #define TASK_UNINTERRUPTIBLE 2 //不可中断睡眠,不可以通过信号进行唤醒
  • #define __TASK_STOPPED 4 //进程停止执行
  • #define __TASK_TRACED 8 //进程被追踪

/* in tsk->exit_state */

  • #define EXIT_ZOMBIE 16 //僵尸状态的进程,表示进程被终止,但是父进程还没有获取它的终止信息,比如进程有没有执行完等信息。
  • #define EXIT_DEAD 32 //进程的最终状态,进程死亡

/* in tsk->state again */

  • #define TASK_DEAD 64 //死亡
  • #define TASK_WAKEKILL 128 //唤醒并杀死的进程
  • #define TASK_WAKING 256 //唤醒进程

(3)进程的优先级 long priority

Priority的值给出进程每次获取CPU后可使用的时间(按jiffies计)。优先级可通过系统sys_setpriorty改变(在kernel/sys.c中)。

  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
  • 上下文数据:进程执行时处理器的寄存器中的数据。
  • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备(如磁带驱动器)和被进程使用的文件列表。
  • 审计信息:可包括处理器时间总和,使用的时钟数总和,时间限制,审计号等。

(4)进程调度信息

表示当前进程或一个进程允许运行的时间,待到该进程的时间片运行结束,CPU会从运行队列上拿出另一个进程运行。

  • need_resched:调度标志
  • Nice:静态优先级
  • Counter:动态优先级;重新调度进程时会在run_queue中选出Counter值最大的进程。也代表该进程的时间片,运行中不断减少。
  • Policy:调度策略开始运行时被赋予的值
  • rt_priority:实时优先级

(5)进程通信有关信息(IPC:Inter_Process Communication)

  • unsigned long signal:进程接收到的信号。每位表示一种信号,共32种。置位有效。
  • unsigned long blocked:进程所能接受信号的位掩码。置位表示屏蔽,复位表示不屏蔽。
  • Spinlock_t sigmask_lock:信号掩码的自旋锁
  • Long blocked:信号掩码
  • Struct sem_undo *semundo:为避免死锁而在信号量上设置的取消操作
  • Struct sem_queue *semsleeping:与信号量操作相关的等待队列
  • struct signal_struct *sig:信号处理函数

(6)进程信息

Linux中存在多进程,而多进程中进程之间的关系可能是父子关系,兄弟关系。

  除了祖先进程外,其他进程都有一个父进程,通过folk创建出子进程来执行程序。除了表示各自的pid外,子进程的绝大多数信息都是拷贝父进程的信息。且父进程对子进程手握生杀大权,即子进程时是父进程创建出来的,而父进程也可以发送命令杀死子进程。

(7)时间信息

  • Start_time:进程创建时间
  • Per_cpu_utime:进程在执行时在用户态上耗费的时间。
  • Pre_cpu_stime:进程在执行时在系统态上耗费的时间。
  • ITIMER_REAL:实时定时器,不论进程是否运行,都在实时更新。
  • ITIMER_VIRTUAL:虚拟定时器,只有进程运行在用户态时才会更新。
  • ITIMER_PROF:概况定时器,进程在运行处于用户态和系统态时更新。

(8)文件信息

文件的打开和关闭都是资源的一种操作,Linux中的task_struct中有两个结构体储存这两个信息。

  • Sruct fs_struct *fs:进程的可执行映象所在的文件系统,有两个索引点,称为root和pwd,分别指向对应的根目录和当前目录。
  • Struct files_struct *files:进程打开的文件

(8)地址空间/虚拟内存信息

每个进程都有自己的一块虚拟内存空间,用mm_struct来表示,mm_struct中使用两个指针表示一段虚拟地址空间,然后在最终时通过页表映射到真正的物理内存上。

(9)页面管理信息

  • Int swappable:进程占用的内存页面是否可换出。
  • Unsigned long min_flat,maj_flt,nswap:进程累计换出、换入页面数。
  • Unsigned long cmin_flat,cmaj_flt,cnswap:本进程作为祖先进程,其所有层次子进程的累计换出、换入页面数。

(10)对称对处理机信息

  • Int has_cpu: 进程是否当前拥有CPU
  • Int processor: 进程当前正在使用的CPU
  • Int lock_depth: 上下文切换时内核锁的深度

(11)上下文信息:

  • struct desc_struct *ldt:进程关于CPU段式存储管理的局部描述符表的指针。
  • struct thread_struct tss:任务状态段。与Intel的TSS进行互动,当前运行的TSS保存在PCB的tss中,新选中的的进程的tss保存在TSS。

(12)信号量数据成员

  • struct sem_undo *semundo:进程每一次操作一次信号量,都会生成一个undo操作。保存在sem_undo结构体中,最终在进程异常终止结束的时候,sem_undo的成员semadj就会指向一个数组,这个数组中每个成员都表示之前每次undo的量。
  • truct sem_queue *semsleeping:进程在操作信号量造成堵塞时,进程会被送入semsleeping指示的关于该信号量的sem_queue队列。

(13)进程队列指针

  • struct task_struct *next_task,*prev_task:所有进程均有各自的PCB。且各个PCB会串在一起,形成一个双向链表。其next_task和+ prev_task就表示上一个或下一个PCB,即前后指针。进程链表的头和尾都是0号进程。
  • struct task_struct *next_run,*prev_run:由进程的run_queue中产生作用的,指向上一个或下一个可运行的进程,链表的头和尾都是0号进程。
  • struct task_struct *p_opptr:原始父进程(祖先进程)
  • struct task_struct *p_pptr :父进程
  • struct task_struct *p_cptr:子进程
  • struct task_struct *p_ysptr:弟进程
  • struct task_struct *p_osptr:兄进程
      以上分别是指向原始父进程(original parent)、父进程(parent)、子进程(youngest child)及新老兄弟进程(younger sibling,older sibling)的指针。
  • current:当前正在运行进程的指针。
  • struct task_struct init_task:0号进程的PCB,进程的跟=根,始终是INIT_TASK。
  • char comm[16]:进程正在执行的可执行文件的文件名。
  • int errno:进程最后一次出错的错误号。0表示无错误。

参考资料


目录
相关文章
|
27天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
40 1
|
6天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
11天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
8天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
19 1
|
13天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
46 4
|
13天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
28 2
|
14天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
15天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
1月前
|
存储 消息中间件 人工智能
进程,线程,协程 - 你了解多少?
本故事采用简洁明了的对话方式,尽洪荒之力让你在轻松无负担的氛围中,稍微深入地理解进程、线程和协程的相关原理知识
41 2
进程,线程,协程 - 你了解多少?
|
18天前
|
算法 调度
探索操作系统的心脏:内核与进程管理
【10月更文挑战第25天】在数字世界的复杂迷宫中,操作系统扮演着关键角色,如同人体中的心脏,维持着整个系统的生命力。本文将深入浅出地剖析操作系统的核心组件——内核,以及它如何通过进程管理来协调资源的分配和使用。我们将从内核的概念出发,探讨它在操作系统中的地位和作用,进而深入了解进程管理的机制,包括进程调度、状态转换和同步。此外,文章还将展示一些简单的代码示例,帮助读者更好地理解这些抽象概念。让我们一起跟随这篇文章,揭开操作系统神秘的面纱,理解它如何支撑起我们日常的数字生活。

相关实验场景

更多