Linux内核分析(Linux中的进程)


前言

本篇文章开始带大家正式进入到Linux内核源码分析,那我们来看看Linux内核的奥秘吧!

一、进程的概念

1. 进程的概念

在Linux操作系统中,进程是一个正在执行的程序实例。它是计算机上执行任务的基本单位。进程不仅包括了程序的代码,还包括了执行程序所需的资源,如内存、文件描述符、环境变量、处理器状态等。

每个进程在系统中都有一个唯一的标识符,称为进程ID(PID)。操作系统通过进程ID来管理和调度进程。进程的生命周期包括创建、执行、等待(阻塞)、结束等状态。

2. 在内核中,进程叫做任务

在Linux内核中,进程被称为“任务”(Task)。Linux内核使用一个叫做task_struct的结构体来表示每个任务。这是因为从内核的角度来看,进程不仅仅是一个程序的实例,更是一个需要管理的任务。内核负责为这些任务分配资源、调度执行,以及管理它们的状态。

在内核源码中,task_struct包含了与进程相关的所有信息,如进程ID、父进程、进程状态、内存映射、文件系统信息、调度信息等。通过管理这些任务,内核实现了多任务操作系统的基本功能。

3. 进程的虚拟地址空间

每个进程在执行时,都拥有自己的虚拟地址空间。虚拟地址空间是一个独立的地址空间,允许每个进程看起来像是独占计算机的内存。这种虚拟化的方式提供了内存保护,使得一个进程不能直接访问另一个进程的内存。

在Linux中,进程的虚拟地址空间可以分为两部分:

  1. 用户虚拟地址空间

    • 这是进程在用户态下运行时所使用的虚拟地址空间。
    • 用户态程序只能访问这一部分的内存空间,典型的用户虚拟地址范围在 0x000000000xBFFFFFFF(具体范围取决于系统的架构和配置)。
    • 用户虚拟地址空间用于存储用户程序的代码、数据段、堆、栈等。
  2. 内核虚拟地址空间

    • 这是内核态运行时使用的虚拟地址空间。
    • 当进程陷入内核态(如系统调用或中断处理)时,内核虚拟地址空间会被映射到进程的虚拟地址空间中。
    • 内核虚拟地址空间用于存放内核代码、内核数据结构、内核栈、设备驱动程序等。
    • 内核虚拟地址空间通常位于较高的地址区域,例如 0xC00000000xFFFFFFFF

总结

在Linux中,进程是一个程序的执行实例,是操作系统管理和调度的基本单位。在内核中,进程被称为任务,并使用task_struct结构体来表示。进程的虚拟地址空间分为用户虚拟地址空间和内核虚拟地址空间,以实现内存的隔离和保护。通过这种方式,Linux系统可以有效管理多任务,并确保进程之间的安全性。

二、进程的生命周期

进程的生命周期包括从创建到终止所经历的各个状态,以及这些状态之间的转换过程。一个典型的进程生命周期包括以下几个主要状态:

创建(New):

进程被创建时,它处于创建状态。此时,操作系统为进程分配必要的资源(如内存、PCB 等),但进程还未进入就绪状态。
就绪(Ready):

进程已经创建并分配了所需资源,等待被 CPU 调度执行。就绪状态的进程排队等待 CPU 分配时间片。
运行(Running):

进程被 CPU 调度,正在执行指令。当进程占用 CPU 并在执行其代码时,它处于运行状态。
等待(Waiting)/阻塞(Blocked):

如果进程在运行时需要等待某些条件满足(如 I/O 操作、资源可用等),它会进入等待状态。此时,进程暂停执行,等待条件满足后再进入就绪状态。
终止(Terminated):

进程完成任务或被强制终止后进入终止状态。此时,操作系统释放进程所占用的资源,并删除进程控制块(PCB)。

以下是一个简单的进程状态流转图的描述:

+---------+        +---------+        +----------+
|  创建   | -----> |  就绪   | -----> |  运行    |
+---------+        +---------+        +----------+
                        ^                |
                        |                v
                +----------------+    +---------+
                |   等待/阻塞    | <--|  终止   |
                +----------------+    +---------+

三、task_struct结构体分析

在内核源码的include\linux\sched.h目录下可以找到task_struct结构体:
在这里插入图片描述
task_struct结构体分析:

好的,让我为你讲解这段代码的结构和功能。我会在代码中加入注释进行详细说明。

```c
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
    /*
     * 这段代码指定了 `thread_info` 必须是 `task_struct` 的第一个元素。
     * 这是因为一些函数(如 `current_thread_info()`)需要直接访问它。
     */
    struct thread_info thread_info;
#endif

    /* 进程状态:-1 表示不可运行,0 表示可运行,>0 表示停止 */
    volatile long state;

    /*
     * 这里开始 `task_struct` 的可随机化部分。
     * 只有与调度密切相关的元素应该放在这个位置之上。
     */
    randomized_struct_fields_start

    /* 进程的栈指针 */
    void *stack;
    
    /* 引用计数,用于进程资源管理 */
    refcount_t usage;

    /* 任务标志位 (PF_*),这些标志位在后面有定义 */
    unsigned int flags;

    /* ptrace 系统调用的状态 */
    unsigned int ptrace;

#ifdef CONFIG_SMP
    /* 用于多核处理器上的进程唤醒管理 */
    struct llist_node wake_entry;

    /* 当前任务是否正在 CPU 上运行 */
    int on_cpu;

#ifdef CONFIG_THREAD_INFO_IN_TASK
    /* 当前任务所在的 CPU */
    unsigned int cpu;
#endif

    /* 与任务唤醒相关的其他字段 */
    unsigned int wakee_flips;
    unsigned long wakee_flip_decay_ts;
    struct task_struct *last_wakee;

    /*
     * 最近使用的 CPU,用于任务调度时优先考虑分配到最近使用的 CPU。
     */
    int recent_used_cpu;
    int wake_cpu;
#endif

    /* 表示任务是否在运行队列上 */
    int on_rq;

    /* 任务的优先级 */
    int prio;
    int static_prio;
    int normal_prio;
    unsigned int rt_priority;

    /* 任务的调度类 */
    const struct sched_class *sched_class;

    /* 任务的调度实体,包含调度信息 */
    struct sched_entity se;
    struct sched_rt_entity rt;

#ifdef CONFIG_CGROUP_SCHED
    /* 调度时所属的任务组 */
    struct task_group *sched_task_group;
#endif

    /* 调度的其他相关字段 */
    struct sched_dl_entity dl;

#ifdef CONFIG_UCLAMP_TASK
    /* 任务的调度上限值(uclamp) */
    struct uclamp_se uclamp_req[UCLAMP_CNT];
    struct uclamp_se uclamp[UCLAMP_CNT];
#endif

#ifdef CONFIG_PREEMPT_NOTIFIERS
    /* 任务的抢占通知链表 */
    struct hlist_head preempt_notifiers;
#endif

#ifdef CONFIG_BLK_DEV_IO_TRACE
    /* 用于 I/O 跟踪的序列号 */
    unsigned int btrace_seq;
#endif

    /* 调度策略和允许使用的 CPU 数量 */
    unsigned int policy;
    int nr_cpus_allowed;
    const cpumask_t *cpus_ptr;
    cpumask_t cpus_mask;

#ifdef CONFIG_PREEMPT_RCU
    /* RCU(读取-复制更新)相关字段 */
    int rcu_read_lock_nesting;
    union rcu_special rcu_read_unlock_special;
    struct list_head rcu_node_entry;
    struct rcu_node *rcu_blocked_node;
#endif

#ifdef CONFIG_TASKS_RCU
    /* 用于 RCU 任务的字段 */
    unsigned long rcu_tasks_nvcsw;
    u8 rcu_tasks_holdout;
    u8 rcu_tasks_idx;
    int rcu_tasks_idle_cpu;
    struct list_head rcu_tasks_holdout_list;
#endif

    /* 任务的调度信息 */
    struct sched_info sched_info;

    /* 任务列表指针 */
    struct list_head tasks;
#ifdef CONFIG_SMP
    struct plist_node pushable_tasks;
    struct rb_node pushable_dl_tasks;
#endif

    /* 进程的内存管理结构 */
    struct mm_struct *mm;
    struct mm_struct *active_mm;

    /* 每个线程的 VMA(虚拟内存区域)缓存 */
    struct vmacache vmacache;

#ifdef SPLIT_RSS_COUNTING
    /* RSS(驻留集大小)统计 */
    struct task_rss_stat rss_stat;
#endif

    /* 任务的退出状态和信号 */
    int exit_state;
    int exit_code;
    int exit_signal;
    int pdeath_signal;
    unsigned long jobctl;

    /* 模拟不同 Linux 版本的行为 */
    unsigned int personality;

    /* 任务的调度位 */
    unsigned sched_reset_on_fork:1;
    unsigned sched_contributes_to_load:1;
    unsigned sched_migrated:1;
    unsigned sched_remote_wakeup:1;
#ifdef CONFIG_PSI
    unsigned sched_psi_wake_requeue:1;
#endif

    /* 强制对齐到下一个边界 */
    unsigned :0;

    /* 非序列化字段,严格针对当前任务 */
    unsigned in_execve:1;
    unsigned in_iowait:1;
#ifndef TIF_RESTORE_SIGMASK
    unsigned restore_sigmask:1;
#endif
#ifdef CONFIG_MEMCG
    unsigned in_user_fault:1;
#endif
#ifdef CONFIG_COMPAT_BRK
    unsigned brk_randomized:1;
#endif
#ifdef CONFIG_CGROUPS
    unsigned no_cgroup_migration:1;
    unsigned frozen:1;
#endif
#ifdef CONFIG_BLK_CGROUP
    unsigned use_memdelay:1;
#endif

    /* 需要原子访问的标志位 */
    unsigned long atomic_flags;

    /* 重新启动块 */
    struct restart_block restart_block;

    /* 进程 ID 和线程组 ID */
    pid_t pid;
    pid_t tgid;

#ifdef CONFIG_STACKPROTECTOR
    /* 用于 -fstack-protector 功能的 Canary 值 */
    unsigned long stack_canary;
#endif

    /*
     * 指向父进程、最年轻的子进程、兄弟进程的指针。
     * `p->father` 可以替换为 `p->real_parent->pid`
     */
    struct task_struct __rcu *real_parent;
    struct task_struct __rcu *parent;

    /* 子进程和兄弟进程列表 */
    struct list_head children;
    struct list_head sibling;
    struct task_struct *group_leader;

    /*
     * `ptraced` 是此任务正在使用 ptrace() 的任务列表。
     * 包括自然子进程和 PTRACE_ATTACH 目标。
     * `ptrace_entry` 是此任务在 `p->parent->ptraced` 列表中的链接。
     */
    struct list_head ptraced;
    struct list_head ptrace_entry;

    /* PID/PID 哈希表链表 */
    struct pid *thread_pid;
    struct hlist_node pid_links[PIDTYPE_MAX];
    struct list_head thread_group;
    struct list_head thread_node;

    /* 用于 vfork() 的完成结构 */
    struct completion *vfork_done;

    /* CLONE_CHILD_SETTID 和 CLONE_CHILD_CLEARTID 的字段 */
    int __user *set_child_tid;
    int __user *clear_child_tid;

    /* CPU 使用时间的统计 */
    u64 utime;
    u64 stime;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
    u64 utimescaled;
    u64 stimescaled;
#endif
    u64 gtime;
    struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
    struct vtime vtime;
#endif

#ifdef CONFIG_NO_HZ_FULL
    atomic_t tick_dep_mask;
#endif

    /* 任务的上下文切换次数 */
    unsigned long nvcsw;
    unsigned long nivcsw;

    /* 单调时间和启动时间 */
    u64 start_time;
    u64 start_boottime;

    /* MM 错误和交换信息 */
    unsigned long min_flt;
    unsigned long maj_flt;

    /* POSIX CPU 定时器的结构 */
    struct posix_cputimers posix_cputimers;

    /* 进程的凭据(权限和身份验证信息) */
    const struct cred __rcu *ptracer_cred;
    const struct cred __rcu *real_cred;
    const struct cred __rcu *cred;

#ifdef CONFIG_KEYS
    /* 缓存的请求密钥 */
    struct key *cached_requested_key;
#endif

    /* 可执行文件名(不包括路径) */
    char comm[TASK_COMM_LEN];

    /* 名字解析数据 */
    struct nameidata *nameidata;

#ifdef CONFIG_SYSVIPC
    /* System V IPC 结构 */
    struct sysv_sem sysvsem;
    struct sysv_shm sysvshm;
#endif

#ifdef CONFIG_DETECT_HUNG_TASK
    /* 检测卡住的任务的字段 */
    unsigned long last_switch_count;
    unsigned long last_switch_time;
#endif

    /* 文件系统信息 */
    struct fs_struct *fs;

    /* 打开的文件信息 */
    struct files_struct *files;

    /* 命名空间信息 */
    struct nsproxy *nsproxy;

    /* 信号处理程序 */
    struct signal_struct *signal;
    struct sighand_struct __rcu *sighand;

    /* 阻塞的信号集 */
    sigset_t blocked

三、进程的状态

/* task state bitmask, copied from include/linux/sched.h */
#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_DEAD		16
#define EXIT_ZOMBIE		32
#define EXIT_TRACE		(EXIT_ZOMBIE | EXIT_DEAD)
/* in tsk->state again */
#define TASK_DEAD		64
#define TASK_WAKEKILL		128
#define TASK_WAKING		256
#define TASK_PARKED		512

在 Linux 内核中,每个进程(或任务)都有一个状态值,表示当前的运行状态。这些状态通过位掩码(bitmask)的方式表示,每个状态对应一个特定的位。以下是这些宏定义的详细解释:

TASK_RUNNING (0)

  • 描述: 任务正在运行或准备运行。
  • 含义: 进程处于运行状态,它要么正在被 CPU 执行,要么准备被调度执行。就绪状态和正在执行的状态在 Linux 中被统一为 TASK_RUNNING 状态。

TASK_INTERRUPTIBLE (1)

  • 描述: 任务处于可中断的睡眠状态。
  • 含义: 进程正在等待某些条件(如等待 I/O 操作完成),但它可以被信号唤醒。这意味着当某个信号到来时,进程可以从睡眠中唤醒并处理信号。

TASK_UNINTERRUPTIBLE (2)

  • 描述: 任务处于不可中断的睡眠状态。
  • 含义: 进程同样在等待某些条件(如等待 I/O 操作完成),但它不会被信号打断。通常用于进程等待非常重要的事件时,比如内存管理操作。

__TASK_STOPPED (4)

  • 描述: 任务处于停止状态。
  • 含义: 进程由于接收到 SIGSTOPSIGTSTPSIGTTINSIGTTOU 等信号而被暂停执行。进程在这种状态下不会被调度执行,直到收到 SIGCONT 信号恢复执行。

__TASK_TRACED (8)

  • 描述: 任务正在被追踪(调试)。
  • 含义: 进程被调试器(如 gdb)追踪时会进入这种状态。此状态下,进程暂停执行,等待调试器的进一步指令。

EXIT_DEAD (16)

  • 描述: 任务处于即将结束的状态。
  • 含义: 进程已经结束但还没有完全清理干净。此状态表示进程已经结束,只是等待彻底从系统中移除。

EXIT_ZOMBIE (32)

  • 描述: 任务处于僵尸状态。
  • 含义: 进程已经终止,但其父进程尚未调用 wait() 系列系统调用获取其终止状态。此状态下的进程被称为“僵尸进程”,它保留在进程表中以便父进程能够获取其退出状态。

EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)

  • 描述: 组合状态,用于标识僵尸进程或即将退出的进程。
  • 含义: 它是 EXIT_ZOMBIEEXIT_DEAD 状态的组合,表示进程已经终止或正在终止的状态。

TASK_DEAD (64)

  • 描述: 任务已经死亡。
  • 含义: 进程已经彻底退出,并且即将从系统中被移除。此状态下,进程的所有资源将被内核回收。

TASK_WAKEKILL (128)

  • 描述: 任务处于可被唤醒或终止的状态。
  • 含义: 这个状态位用于标记某些特殊的任务状态,当任务处于这种状态时,如果收到特定信号(如致命信号),任务会被强制唤醒或终止。

TASK_WAKING (256)

  • 描述: 任务正在从睡眠状态中被唤醒。
  • 含义: 表示任务正处于被唤醒的过程中,即从休眠状态转变为运行状态的过渡状态。

TASK_PARKED (512)

  • 描述: 任务处于停驻状态。
  • 含义: 任务被“停驻”,通常是为了资源管理或电源管理的目的。这种状态下的任务不会被调度执行,直到它被重新唤醒。

这些宏定义通过位掩码来表示不同的任务状态,允许内核对进程的状态进行精确的控制和管理。在内核代码中,这些状态用于判断任务的当前情况,并采取适当的调度和信号处理措施。

四、进程优先级

在Linux内核中,进程可以被分类为三种主要类型:限期进程(Time-Sharing Processes)、实时进程(Real-Time Processes)和普通进程(Normal Processes)。这些分类反映了进程的不同需求和调度策略。以下是对这三种进程的详细解释:

1. 限期进程(Time-Sharing Processes)

概念:限期进程是指那些不需要严格实时响应的进程。它们是用户在系统上执行的常规进程,比如文本编辑器、浏览器等。系统通过动态优先级调度来管理这些进程,确保它们公平地获得CPU时间。

调度策略

  • 调度算法:限期进程主要由**“CFS”(完全公平调度器)**调度。CFS的目标是使每个进程获得公平的CPU时间,避免某个进程长时间占用CPU而影响其他进程。

限期进程的优先级是-1。

特点

  • 优先级动态调整:CFS根据进程的运行时间和等待时间动态调整其优先级。
  • 公平性:CFS算法的目标是确保所有进程在长期内获得公平的CPU时间。

2. 实时进程(Real-Time Processes)

概念:实时进程是那些对时间要求严格的进程,它们需要在特定的时间内完成任务,例如音频处理、控制系统等。这类进程对延迟的要求高,常常用于需要即时响应的应用场景。

调度策略

  • 调度策略
    • FIFO(先进先出):实时进程按照优先级顺序运行。优先级高的进程首先执行。如果多个进程具有相同的优先级,它们将按照到达顺序执行。
    • RR(轮转调度):实时进程轮流执行,每个进程在时间片用尽后被挂起,然后调度下一个进程。
  • 优先级范围:1到99。优先级数值越大,表示优先级越高。

特点

  • 优先级固定:实时进程的优先级在调度时是固定的,并且在系统中具有较高的优先权。
  • 时间敏感:实时进程的调度确保其在预定的时间内完成任务,以满足严格的时间要求。

3. 普通进程(Normal Processes)

概念:普通进程是指那些不属于实时任务的常规进程。它们不具备特别的实时要求,通常是用户启动的应用程序或系统服务。

调度策略

  • 调度算法:普通进程由CFS调度算法管理,这是一种动态公平调度策略。
  • 优先级范围:100到139。这里的优先级值较高表示较低的优先级。普通进程的优先级低于实时进程。

特点

  • 静态优先级:普通进程的优先级通常是静态的,但在某些情况下可以调整。
  • 灵活性:普通进程可以根据系统的负载和优先级调整策略获得CPU时间。

总结:

限期进程的优先级比实时进程要高,实时进程的优先级比普通进程要高。

五、task_struct中进程优先级的表示方法

在Linux内核中,task_struct结构体包含了关于每个进程的信息,其中包括与进程优先级相关的多个字段。以下是对这些字段的简要解释:

在这里插入图片描述

  1. prio

    • 作用:表示进程的当前优先级。Linux内核中的调度器使用这个值来决定哪个进程应该被调度执行。prio的值会根据进程的状态和调度策略动态调整。
  2. static_prio

    • 作用:表示进程的静态优先级。这个优先级在进程创建时被设定,并且通常不会被修改。它用于反映进程的初始优先级设定,常用于实时进程的调度。
  3. normal_prio

    • 作用:表示进程的正常优先级,是基于static_prio和系统调度策略计算出来的优先级。这个优先级通常用于非实时任务的调度,结合了static_prio和动态调整的因素。
  4. rt_priority

    • 作用:仅用于实时进程,表示进程的实时优先级。在实时调度策略(如SCHED_FIFO和SCHED_RR)中,rt_priority用来决定实时进程的调度顺序。实时优先级高的进程会优先于普通进程进行调度。

这些字段共同作用,以确保Linux内核能够有效地管理和调度进程,提高系统的响应性和公平性。

六、系统调用

系统调用(System Call)是操作系统提供的一种接口,使用户空间的程序能够请求操作系统内核执行特权操作或访问硬件资源。它是用户程序与操作系统内核之间的桥梁,通过系统调用,用户程序可以执行诸如文件操作、进程控制、内存管理等任务,这些任务通常需要特权操作,普通用户程序没有直接执行这些操作的权限。

系统调用的工作原理

用户空间和内核空间:

用户空间:这是应用程序运行的地方,权限较低。
内核空间:这是操作系统核心部分运行的地方,具有完全的硬件访问权限和控制能力。

系统调用流程:

发起系统调用:应用程序通过特定的库函数(如 read(), write(), open())发起系统调用。
切换到内核模式:系统调用发起后,CPU 从用户模式切换到内核模式。这通常通过软件中断(如 int 0x80 在早期的 x86 架构上)或更现代的系统调用指令(如 syscall 指令)来完成。
执行系统调用:内核接收系统调用请求,解析系统调用号(一个唯一标识符),并根据系统调用号执行对应的内核函数。
返回用户空间:系统调用执行完毕后,结果(如成功或错误码)会返回给用户程序,然后 CPU 切换回用户模式,程序继续执行。

系统调用的目的和功能

资源访问:操作系统负责管理系统资源(如文件、内存、进程),用户程序通过系统调用请求访问这些资源。

文件操作:如打开、读取、写入、关闭文件。
进程管理:如创建、终止进程,等待进程结束。
内存管理:如分配、释放内存。
设备控制:如控制硬件设备(打印机、网络接口等)。
保护和安全:通过系统调用,操作系统可以在内核模式下安全地执行敏感操作,防止用户程序直接操作硬件或破坏系统稳定性。

进程创建中的系统调用流程图:

1.fork: 用户空间调用 fork(),系统调用切换到内核,sys_fork() 被调用,最终到达 do_fork() 和 copy_process() 创建新的进程。

2.vfork: 用户空间调用 vfork(),系统调用切换到内核,sys_vfork() 被调用,经过 do_fork() 和 copy_process(),与 fork 类似,但 vfork 还处理了父进程的暂停和地址空间共享。

3.clone: 用户空间调用 clone(),系统调用切换到内核,sys_clone() 被调用,经过 do_fork() 和 copy_process(),根据提供的标志位共享资源和状态。

函数调用流程图:
在这里插入图片描述
在这里插入图片描述

七、内核线程

内核线程的概念

内核线程是操作系统内核中执行的线程,它与用户空间线程不同,因为它们运行在内核模式下。内核线程用于执行内核代码,如处理硬件中断、执行系统调用、处理内存管理等。它们具有较高的特权级别,能够直接访问和操作硬件资源。

创建内核线程

在不同的操作系统中,创建内核线程的函数和机制可能有所不同。以 Linux 为例,创建内核线程的常用函数是 kthread_create(),其用法如下:

#include <linux/kthread.h>

struct task_struct *kthread_create(void (*threadfn)(void *data), void *data, const char *namefmt);
  • threadfn:指向线程函数的指针。
  • data:传递给线程函数的参数。
  • namefmt:线程名称的格式化字符串。

此函数返回一个 task_struct 指针,表示新创建的线程。要启动线程,还需调用 wake_up_process() 来启动线程。

内核线程与内核进程的区别

  1. 内核线程

    • 执行环境:运行在内核模式下,主要执行内核代码。
    • 调度:由内核调度器管理,具有内核级别的调度。
    • 资源使用:共享内核资源,通常没有独立的用户空间映像。
    • 创建:通常通过内核函数创建,如 kthread_create()
  2. 内核进程

    • 执行环境:运行在内核模式下,但在创建时更接近传统进程的概念。
    • 调度:由内核调度器管理,但可以有独立的调度策略。
    • 资源使用:可能有独立的内存映像,较完整的进程特性。
    • 创建:通过系统调用(如 fork())创建,并且通常用于实现复杂的内核任务。

总结

内核线程用于执行内核任务,通常不具有用户空间映像,而内核进程则可能具备更传统的进程特性。创建内核线程的函数(如 kthread_create())在 Linux 内核中用于生成并管理这些线程。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花落已飘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值