Linux的task_struct

本文详细介绍了Linux下进程的描述结构task_struct,包括进程状态、调度策略、内存管理信息等关键字段,深入剖析了Linux进程的工作原理。

进程:程序的执行过程;即把二进制文件(程序)从磁盘加载到内存之后的操作;
同时操作系统给这个文件创建了一个描述它的结构体,称为PCB。
每个进程都有一个PCB。
Linux下的PCB结构:task_struct

struct task_struct 
{
    volatile long state;  //进程状态(可执行、可中断等) #1
    unsigned long flags;  //Flage 是进程号,在调用fork()时给出
    int sigpending;    //进程上是否有待处理的信号
    mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
                            //0-0xBFFFFFFF for user
                            //0-0xFFFFFFFF for kernel
    volatile long need_resched;//调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度   #2
    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值

    //it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
    //设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
    //it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
    //当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
    //信号SIGPROF,并根据it_prof_incr重置时间.
    //it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
    //状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
    //it_virt_incr重置初值。
    unsigned long it_real_value, it_prof_value, it_virt_value;
    unsigned long it_real_incr, it_prof_incr, it_virt_value;
    struct timer_list real_timer;   //指向实时定时器的指针
    struct tms times;      //记录进程消耗的时间
    unsigned long start_time;  //进程创建的时间
    //记录进程在每个CPU上所消耗的用户态时间和核心态时间
    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;
};

来自:http://blog.youkuaiyun.com/jurrah/archive/2009/03/07/3965437.aspx

#1、进程状态:

这里写图片描述

#2、调度策略:

这里写图片描述
只有root用户能通过sched_setscheduler()系统调用来改变调度策略。

<think>我们正在讨论Linux内核中进程和线程在`task_struct`中的表示。根据引用内容,我们知道在Linux内核中,无论是进程还是线程,都统一称为任务(Task),并使用`task_struct`结构体进行管理。`task_struct`是进程描述符,也是感知进程存在的唯一实体。内核通过一个双向循环链表将所有`task_struct`实例连接起来。用户的问题集中在进程和线程的关系以及它们在`task_struct`中的表示。我们需要解释Linux如何用同一个结构体表示进程和线程,以及它们之间的区别和联系。###详细解释1.**统一的任务表示**:-在Linux中,进程和线程都被视为任务(Task),都由`task_struct`结构体表示。这意味着从内核的角度看,进程和线程是同一个实体,只是共享资源的方式不同。-每个任务(无论是进程还是线程)都有一个唯一的`task_struct`,其中包含了该任务的所有信息,如状态、优先级、内存空间、打开的文件等。2.**进程与线程的区别**:-**进程**:通常拥有独立的地址空间(即拥有自己的内存映射),资源独立。-**线程**:属于同一个进程的多个线程共享地址空间(即共享内存映射、文件描述符等资源),但每个线程有自己的栈和寄存器状态。3.**在`task_struct`中的体现**:-对于属于同一个进程的多个线程(即轻量级进程),它们的`task_struct`中的`mm`(内存描述符)指针指向同一个`mm_struct`结构体,从而共享地址空间。-每个线程有自己独立的`task_struct`,其中包含线程特有的信息,如栈(`stack`)、寄存器状态(`thread`)等。-线程组(即同一个进程中的所有线程)通过`tgid`(线程组ID)来标识。在`task_struct`中:-`pid`:每个任务(线程)的唯一ID,称为线程ID。-`tgid`:线程组ID,即主线程的PID。对于进程(主线程)来说,`pid`等于`tgid`;对于同一进程中的其他线程,`tgid`等于主线程的`pid`。4.**创建线程与进程**:-当使用`fork()`系统调用创建新进程时,新任务会复制父进程的地址空间(写时复制),拥有独立的资源。-当使用`clone()`系统调用创建线程时,通过传递不同的标志(如`CLONE_VM`、`CLONE_FS`、`CLONE_FILES`等)来指定共享的资源。例如:-`CLONE_VM`:共享地址空间(即`mm_struct`)。-`CLONE_FS`:共享文件系统信息。-`CLONE_FILES`:共享打开的文件描述符表。-等等。5.**如何遍历所有任务**:-内核通过一个双向循环链表(`tasks`链表)连接所有`task_struct`。可以使用`for_each_process`宏来遍历所有任务(引用[2])。6.**获取当前任务**:-在ARM64架构中,可以通过读取`sp_el0`寄存器来获取当前任务的`task_struct`指针(引用[3])。在x86架构中,通常通过当前任务的内核栈来获取。7.**线程本地存储**:-`task_struct`中的`set_child_tid`和`clear_child_tid`成员与线程本地存储有关(引用[4])。当使用`CLONE_CHILD_SETTID`和`CLONE_CHILD_CLEARTID`标志创建线程时,会设置这些成员,以便在用户空间管理线程ID。###总结-Linux内核使用统一的`task_struct`结构体来管理进程和线程,线程被视为共享同一地址空间的多个任务。-进程与线程的主要区别在于资源的共享程度,这通过`clone()`系统调用的标志来控制。-在同一个进程中的线程具有相同的`tgid`(即主线程的PID),而每个线程有自己的`pid`(线程ID)。###相关问题1.在Linux中,如何通过`task_struct`判断一个任务是进程还是线程?2.使用`clone()`系统调用创建线程时,需要设置哪些标志?3.Linux内核如何实现线程组(即同一个进程中的多个线程)的管理?</think>### Linux task_struct 进程与线程关系详解 在 Linux 内核中,**进程和线程都统一表示为任务(Task)**,由核心数据结构 `task_struct` 管理。这是理解 Linux 进程/线程模型的关键,具体原理如下: #### 一、统一的任务表示 1. **共享的数据结构** Linux 内核不区分进程和线程的底层实现,两者都通过 `task_struct` 描述(引用[1])。每个任务(无论进程或线程)在内核中都有独立的 `task_struct` 实例,包含: ```c struct task_struct { // 标识信息 pid_t pid; // 线程ID(轻量级进程ID) pid_t tgid; // 线程组ID(即进程ID) // 资源指针 struct mm_struct *mm; // 内存描述符(地址空间) struct files_struct *files; // 打开文件表 // 关系结构 struct list_head tasks; // 链表节点(连接所有任务) struct task_struct *group_leader; // 线程组主进程 }; ``` 2. **内核的全局管理** 所有 `task_struct` 通过双向循环链表连接(引用[2]),内核可通过宏 `for_each_process()` 遍历: ```c #define for_each_process(p) \ for (p = &init_task; (p = next_task(p)) != &init_task; ) ``` #### 二、进程与线程的关键区别 | 特性 | 进程 (Process) | 线程 (Thread) | |--------------|----------------------------------------|----------------------------------------| | **资源隔离** | 独立地址空间 (`mm` 指针唯一) | 共享父进程地址空间 (`mm` 指向相同) | | **标识符** | `pid = tgid` (主线程) | `pid` 唯一,`tgid` 指向主线程ID | | **创建方式** | `fork()` → 复制整个资源 | `clone(CLONE_VM|CLONE_FILES...)` → 共享资源 | #### 三、线程组实现原理 当创建线程时: 1. **共享资源标志**: 通过 `clone()` 系统调用的标志控制资源共享(引用[4]): - `CLONE_VM`:共享内存空间 (`mm_struct`) - `CLONE_FS`:共享文件系统信息 - `CLONE_FILES`:共享打开的文件描述符 ```c // 创建线程的典型标志组合 clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, ...); ``` 2. **线程组关系**: - 同一进程的所有线程共享 `tgid`(即主线程的 `pid`) - `group_leader` 指针指向主线程的 `task_struct` - 内核通过 `tgid` 实现 `getpid()` 系统调用(返回进程ID) #### 四、内核操作示例 1. **获取当前任务**(引用[3]): ARM64 架构通过 `sp_el0` 寄存器直接获取当前 `task_struct`: ```c struct task_struct *get_current(void) { unsigned long sp_el0; asm ("mrs %0, sp_el0" : "=r" (sp_el0)); return (struct task_struct *)sp_el0; } #define current get_current() ``` 2. **线程本地存储**: 若创建时指定 `CLONE_CHILD_SETTID`,内核会设置 `task_struct->set_child_tid`,用于用户态线程ID同步(引用[4])。 #### 总结 > Linux 用 **`task_struct` 统一抽象进程和线程**,区别仅在于资源共享程度: > - **进程** = 独立资源的任务 > - **线程** = 共享资源的任务组(相同 `tgid`) > 这种设计减少了内核冗余代码,并通过 `clone()` 标志灵活控制资源共享级别[^1][^4]。 --- ### 相关问题 1. 为什么 Linux 线程被称为"轻量级进程"(LWP)? 2. 如何通过 `task_struct` 字段区分进程的主线程和子线程? 3. 调用 `pthread_create()` 时,内核层的 `clone()` 具体做了哪些操作? 4. 当进程退出时,内核如何处理线程组资源的回收?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值