current task_struct

本文深入解析Linux中的进程管理核心——task_struct数据结构。探讨其在进程控制中的关键作用,包括进程状态、调度信息、进程间通信等多个方面。同时,文章还讨论了fork()函数创建子进程的过程以及task_struct在其中的作用。

 

 

 #define current get_current()

 

 

20 static inline struct task_struct *get_current(void)

 21 {

 22         return current_thread_info()->task;

 23 }

 

 

 

 在linux   中每一个进程都由task_struct   数据结构来定义.   task_struct就是我们通常所说的PCB.她是对进程控制的唯一手段也是最有效的手段.   当我们调用fork()   时,   系统会为我们产生一个task_struct结构.然后从父进程,那里继承一些数据,   并把新的进程插入到进程树中,   以待进行进程管理.因此了解task_struct的结构对于我们理解任务调度(在linux   中任务和进程是同一概念)的关键.在进行剖析task_struct的定义之前.   我们先按照我们的理论推一下它的结构.   1,   进程状态   ,将纪录进程在等待,运行,或死锁   2,   调度信息,   由哪个调度函数调度,怎样调度等   3,   进程的通讯状况   4,因为要插入进程树,必须有联系父子兄弟的指针,   当然是task_struct型   5,时间信息,   比如计算好执行的时间,   以便cpu   分配   6,标号   ,决定改进程归属   7,可以读写打开的一些文件信息   8,   进程上下文和内核上下文   9,处理器上下文   10,内存信息   因为每一个PCB都是这样的,   只有这些结构,   才能满足一个进程的所有要求.打开/include/linux/sched.h   找到task_struct   的定义   
    struct   task_struct   {   /*   these   are   hardcoded   -   don't   touch   */    这里是一些硬件设置对程序原来说是透明的.   其中state   说明了该进程是否可以执行,还是可中断等信息.   Flage   是进程号,   在调用   fork()   时给出,addr_limit   是区分内核进程与普通进程在内存存放的位置不同  
volatile   long   state; /*   -1   unrunnable,   0   runnable,  >0  stopped   */  
unsigned   long   flags;   /*   per   process   flags,   defined   below   */   int   sigpending;  
mm_segment_t   addr_limit;   /*   thread   address   space:   0-0xBFFFFFFF   for   user-thead   0-0xFFFFFFFF   for   kernel-thread   */  
struct   exec_domain   *exec_domain;  
long   need_resched;     
  /*   various   fields   */    count   是   计数器   priorrity   是优先级  
long   counter;  
long   priority;  
cycles_t   avg_slice;  
/*   SMP   and   runqueue   state   */    为多处理机定义的变量.  
int   has_cpu;  
int   processor;  
int   last_processor;  
int   lock_depth;   /*   Lock   depth.   We   can   context   switch   in   and   out   of   holding   a   syscall   kernel   lock...   */  
  为了在进程树中排序,   定义的父子,兄弟指针  
struct   task_struct   *next_task,   *prev_task;  
struct   task_struct   *next_run,   *prev_run;     
  /*   task   state   */    定义可   task   运行的状态,   以及信号  
struct   linux_binfmt   *binfmt;  
int   exit_code,   exit_signal;  
int   pdeath_signal;   /*   The   signal   sent   when   the   parent   dies   */   /*   定义可进程的用户号,用户组以及进程组*/  
unsigned   long   personality;  
int   dumpable:1;  
int   did_exec:1;  
pid_t   pid;  
pid_t   pgrp;  
pid_t   tty_old_pgrp;  
pid_t   session;  
/*   boolean   value   for   session   group   leader   */  
  是不是进程组的头文件  
int   leader;  
/*  
*   pointers   to   (original)   parent   process,   youngest   child,   younger   sibling,  
*   older   sibling,   respectively.   (p->father   can   be   replaced   with   *   p->p_pptr->pid)  
*/  
父子进程的一些指针  
struct   task_struct   *p_opptr,   *p_pptr,   *p_cptr,   *p_ysptr,   *p_osptr;     
  /*   PID   hash   table   linkage.   */    在调度中用的一些hash   表  
struct   task_struct   *pidhash_next;  
struct   task_struct   **pidhash_pprev;     
  /*   Pointer   to   task[]   array   linkage.   */  
struct   task_struct   **tarray_ptr;     
struct   wait_queue   *wait_chldexit;   /*   for   wait4()   等待队列   */   struct   semaphore   *vfork_sem;   /*   for   vfork()   */  
unsigned   long   policy,   rt_priority;  
unsigned   long   it_real_value,   it_prof_value,   it_virt_value;  
进程的性质因为实时进程与普通进程的调度算法不一样所以应有变量区分  
下面是进程的一些时间信息  
unsigned   long   it_real_incr,   it_prof_incr,   it_virt_incr;  
struct   timer_list   real_timer;  
struct   tms   times;  
unsigned   long   start_time;  
long   per_cpu_utime[NR_CPUS],   per_cpu_stime[NR_CPUS]; 定义了时间片的大小  
/*   mm   fault   and   swap   info:   this   can   arguably   be   seen   as   either   mm-specific   or   thread-specific   */  
内存信息  
unsigned   long   min_flt,   maj_flt,   nswap,   cmin_flt,   cmaj_flt,   cnswap;   int   swappable:1;  
/*   process   credentials   */  
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;  
struct   user_struct   *user;  
以下英文注释很清楚  
/*   limits   */  
struct   rlimit   rlim[RLIM_NLIMITS];  
unsigned   short   used_math;  
char   comm[16];   /*   file   system   info   */  
int   link_count;  
struct   tty_struct   *tty;   /*   NULL   if   no   tty   */  
/*   ipc   stuff   */     
struct   sem_undo   *semundo;  
struct   sem_queue   *semsleeping;  
/*   tss   for   this   task   */  
struct   thread_struct   tss;  
 /*   filesystem   information   */  
struct   fs_struct   *fs;  
/*   open   file   information   */  
struct   files_struct   *files;  
 /*   memory   management   info   */  
struct   mm_struct   *mm;     
  /*   signal   handlers   */  
spinlock_t   sigmask_lock;  
 /*   Protects   signal   and   blocked   */  
struct   signal_struct   *sig;   sigset_t   signal,   blocked;  
struct   signal_queue   *sigqueue,   **sigqueue_tail;  
unsigned   long   sas_ss_sp;  
size_t   sas_ss_size;  
};  
在分析完   这个结构之后,   还有很多问题要想,   也许不能读   但框架要搞好.需要向的问题有以下几个   1,在task_struct   中用的常量在那里定义呢,   如最大进程个数,   最多支持的cpu   个数,等等   2,在调用fork()   时,   系统是分配一块内存   会是这样么   malloc(1,sizeof(struct   task_struck))   拷贝一些变量,还是和父进程共用一部分内存.malloc   函数怎么实现(在内存管理那一部分,但此处我认为不能不想)   3,.对于线程来说,   又如何实现呢?   4,   调度策略函数schedule()有几种形势,   时间片轮转,   抢占式,优先级抢占式,   多级反馈制.除了时间片轮转外都要对进程树进行遍历,(对于实时进程的fifo机制不用)linux   是怎样保证了高效呢?如果把最大线成数修改,   效率会不会降低   5,   进程通讯用到的管道,信号结构如何

<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、付费专栏及课程。

余额充值