美文网首页
进程调度与管理1-task_struct

进程调度与管理1-task_struct

作者: xuefeng_apple | 来源:发表于2020-10-21 21:04 被阅读0次
进程主要关联

**task_struct **
msm-4.9/include/linux/sched.h

struct task_struct {
    struct thread_info thread_info;  //线程描述符
    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
    void *stack;

    unsigned int flags; /* per process flags, defined below */
    unsigned int ptrace;

    int prio, static_prio, normal_prio;
    unsigned int rt_priority;
    const struct sched_class *sched_class;

    unsigned int policy;
    int nr_cpus_allowed;

    cpumask_t cpus_allowed;
    struct sched_info sched_info;

    struct mm_struct *mm, *active_mm;

    pid_t pid;
    pid_t tgid;

    cputime_t utime, stime, utimescaled, stimescaled;
    cputime_t gtime;
    struct prev_cputime prev_cputime;
        .......
};

下面介绍主要部分,有一些结构成成员查看源码

1-进程状态

volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
1.1 个互斥状态
state取值有:

1.2 2个终止状态
只有当进程终止的时候,才会达到这两种状态.

/* task state */
int exit_state;
int exit_code, exit_signal;

Linux 内核提供了两种方法将进程置为睡眠状态。
将进程置为睡眠状态的普通方法是将进程状态设置为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 并调用调度程序的 schedule() 函数。这样会将进程从 CPU 运行队列中移除。

  • 如果进程处于可中断模式的睡眠状态(通过将其状态设置TASK_INTERRUPTIBLE),那么可以通过显式的唤醒呼叫(wakeup_process())或需要处理的信号来唤醒它。

  • 如果进程处于非可中断模式的睡眠状态(通过将其状态设置为 TASK_UNINTERRUPTIBLE),那么只能通过显式的唤醒呼叫将其唤醒。除非万不得已,否则我们建议您将进程置为可中断睡眠模式,而不是不可中断睡眠模式(比如说在设备 I/O 期间,处理信号非常困难时)。

对不可中断睡眠模式的进程的唤醒呼叫可能会由于某些原因不会发生,这会使进程无法被终止,从而最终引发问题

TASK_KILLABLE:当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE,只不过可以响应致命信号

进程状态的切换过程和原因大致如下图


2-进程标识符PID

pid_t pid; 
pid_t tgid;

在CONFIG_BASE_SMALL配置为0的情况下,PID的取值范围是0到32767,即系统中的进程数最大为32768个。
#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)
在Linux系统中,一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的PID,并被存放在tgid成员中。只有线程组的领头线程的pid成员才会被设置为与tgid相同的值。注意,getpid()系统调用返回的是当前进程的tgid值而不是pid值。

  • 每个线程的task_struct中,pid都不同,方便系统进行调度等操作
  • 每个线程的task_struct中,tgid都相同,都是第一个线程的pid,这样方便了编程人员对一个进程产生的线程进行统一管理,当然我们在线程组的每个线程内用getpid()函数时,返回相同的值。这里有个“线程组内第一个线程(thread group leader)”的概念,我认为,就是创建线程的进程,也就是那个不轻量的进程,它的pid和tgid是相同的

3-进程内核栈

void *stack;
对每个进程,Linux内核都把两个不同的数据结构紧凑的存放在一个单独为进程分配的内存区域中

  • 一个是内核态的进程堆栈,
  • 另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符。

Linux把thread_info(线程描述符)和内核态的线程堆栈存放在一起,这块区域通常是8K(占两个页框)
内核态的进程访问处于内核数据段的栈,这个栈不同于用户态的进程所用的栈。
用户态进程所用的栈,是在进程线性地址空间中;
而内核栈是当进程从用户空间进入内核空间时,特权级发生变化,需要切换堆栈,那么内核空间中使用的就是这个内核栈。因为内核控制路径使用很少的栈空间,所以只需要几千个字节的内核态堆栈。
内核态堆栈仅用于内核例程,Linux内核另外为中断提供了单独的硬中断栈和软中断栈
内核堆栈是向下增长,就是下图箭头的方向

内核栈与进程描述符
在这个图中,esp寄存器是CPU栈指针,用来存放栈顶单元的地址。在80x86系统中,栈起始于顶端,并朝着这个内存区开始的方向增长。从用户态刚切换到内核态以后,进程的内核栈总是空的。因此,esp寄存器指向这个栈的顶端。一旦数据写入堆栈,esp的值就递减。
current_thread_info 可以获取thread_info,在不同的kernel 实现方式不同

4-进程标记

unsigned int flags; /* per process flags, defined below */
反应进程状态的信息,但不是运行状态,用于内核识别进程当前的状态,以备下一步操作
flags成员的可能取值如下,这些宏以PF(ProcessFlag)开头

 /*
* Per process flags
*/
#define PF_EXITING      0x00000004      /* getting shut down */
#define PF_EXITPIDONE   0x00000008      /* pi exit done on shut down */
#define PF_VCPU         0x00000010      /* I'm a virtual CPU */
#define PF_WQ_WORKER    0x00000020      /* I'm a workqueue worker */
#define PF_FORKNOEXEC   0x00000040      /* forked but didn't exec */
#define PF_MCE_PROCESS  0x00000080      /* process policy on mce errors */
#define PF_SUPERPRIV    0x00000100      /* used super-user privileges */
#define PF_DUMPCORE     0x00000200      /* dumped core */
#define PF_SIGNALED     0x00000400      /* killed by a signal */
#define PF_MEMALLOC     0x00000800      /* Allocating memory */
#define PF_NPROC_EXCEEDED 0x00001000    /* set_user noticed that RLIMIT_NPROC was exceeded */
#define PF_USED_MATH    0x00002000      /* if unset the fpu must be initialized before use */
#define PF_USED_ASYNC   0x00004000      /* used async_schedule*(), used by module init */
#define PF_NOFREEZE     0x00008000      /* this thread should not be frozen */
#define PF_FROZEN       0x00010000      /* frozen for system suspend */
#define PF_FSTRANS      0x00020000      /* inside a filesystem transaction */
#define PF_KSWAPD       0x00040000      /* I am kswapd */
#define PF_MEMALLOC_NOIO 0x00080000     /* Allocating memory without IO involved */
#define PF_LESS_THROTTLE 0x00100000     /* Throttle me less: I clean memory */
#define PF_KTHREAD      0x00200000      /* I am a kernel thread */
#define PF_RANDOMIZE    0x00400000      /* randomize virtual address space */
#define PF_SWAPWRITE    0x00800000      /* Allowed to write to swap */
#define PF_NO_SETAFFINITY 0x04000000    /* Userland is not allowed to meddle with cpus_allowed */
#define PF_MCE_EARLY    0x08000000      /* Early kill for mce process policy */
#define PF_MUTEX_TESTER 0x20000000      /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP 0x40000000      /* Freezer should not count it as freezable */
#define PF_SUSPEND_TASK 0x80000000      /* this thread called freeze_processes and should not be frozen */

5-表示进程亲属关系的成员

/*
 * pointers to (original) parent process, youngest child, younger sibling,
 * older sibling, respectively.  (p->father can be replaced with
 * p->real_parent->pid)
 */
struct task_struct __rcu *real_parent; /* real parent process */
struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
/*
 * children/sibling forms the list of my natural children
 */
struct list_head children;      /* list of my children */
struct list_head sibling;       /* linkage in my parent's children list */
struct task_struct *group_leader;       /* threadgroup leader */

在Linux系统中,所有进程之间都有着直接或间接地联系,每个进程都有其父进程,也可能有零个或多个子进程。拥有同一父进程的所有进程具有兄弟关系。


6-ptrace系统调用与Performance Event

trace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。
它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。
ptrace

unsigned int ptrace;
 ptraced is the list of tasks this task is using ptrace on.
* This includes both natural children and PTRACE_ATTACH targets.
* p->ptrace_entry is p's link on the p->parent->ptraced list.
*/
struct list_head ptraced;
struct list_head ptrace_entry;
unsigned long ptrace_message;
siginfo_t *last_siginfo; /* For ptrace use.  */

Performance Event是一款随 Linux 内核代码一同发布和维护的性能诊断工具。这些成员用于帮助PerformanceEvent分析进程的性能问题。

#ifdef CONFIG_PERF_EVENTS
    struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
    struct mutex perf_event_mutex;
    struct list_head perf_event_list;
#endif

7-进程调度

7.1-优先级

int prio, static_prio, normal_prio;
unsigned int rt_priority;

实时优先级范围是0到MAX_RT_PRIO-1(即99),而普通进程的静态优先级范围是从MAX_RT_PRIO到MAX_PRIO-1(即100到139)。值越大静态优先级越低。

7.2-调度策略相关字段

unsigned int policy;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
cpumask_t cpus_allowed;

调度策略
/*
* Scheduling policies
*/
#define SCHED_NORMAL            0
#define SCHED_FIFO              1
#define SCHED_RR                2
#define SCHED_BATCH             3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE              5
#define SCHED_DEADLINE          6

调度类

extern const struct sched_class stop_sched_class;
extern const struct sched_class dl_sched_class;
extern const struct sched_class rt_sched_class;
extern const struct sched_class fair_sched_class;
extern const struct sched_class idle_sched_class;
  • stop_sched_class 优先级最高的任务会使用这种策略,会中断所有其他线程,且不会被其他任务打断;
  • dl_sched_class 就对应上面的 deadline 调度策略;采用EDF算法调度的实时调度实体
  • rt_sched_class 就对应 RR 算法或者 FIFO 算法的调度策略,具体调度策略由进程的 task_struct->policy 指定;
  • fair_sched_class 就是普通进程的调度策略;采用CFS算法调度的普通非实时进程的调度实体
  • idle_sched_class 就是空闲进程的调度策略

其所属进程的优先级顺序为:stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class
开发者可以根据己的设计需求,來把所属的Task配置到不同的Scheduling Class中.

8-进程地址空间

struct mm_struct *mm, *active_mm;
/* per-thread vma caching */
u32 vmacache_seqnum;
struct vm_area_struct *vmacache[VMACACHE_SIZE];
#if defined(SPLIT_RSS_COUNTING)
struct task_rss_stat    rss_stat;
#endif

#ifdef CONFIG_COMPAT_BRK
unsigned brk_randomized:1;
#endif

因此如果当前内核线程被调度之前运行的也是另外一个内核线程时候,那么其mm和avtive_mm都是NULL

9-判断标志

int exit_code, exit_signal;
int pdeath_signal;  /*  The signal sent when the parent dies  */
unsigned long jobctl;   /* JOBCTL_*, siglock protected */

/* Used for emulating ABI behavior of previous Linux versions */
unsigned int personality;

/* scheduler bits, serialized by scheduler locks */
unsigned sched_reset_on_fork:1;
unsigned sched_contributes_to_load:1;
unsigned sched_migrated:1;
unsigned :0; /* force alignment to the next boundary */

/* unserialized, strictly 'current' */
unsigned in_execve:1; /* bit to tell LSMs we're in execve */
unsigned in_iowait:1;

10-信号处理

/* signal handlers */
struct signal_struct *signal;
struct sighand_struct *sighand;
1583 
sigset_t blocked, real_blocked;
sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
struct sigpending pending;
1587 
unsigned long sas_ss_sp;
size_t sas_ss_size;

11-时间

cputime_t gtime;
struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
seqcount_t vtime_seqcount;
unsigned long long vtime_snap;
enum {
        /* Task is sleeping or running in a CPU with VTIME inactive */
        VTIME_INACTIVE = 0,
        /* Task runs in userspace in a CPU with VTIME active */
        VTIME_USER,
        /* Task runs in kernelspace in a CPU with VTIME active */
        VTIME_SYS,
} vtime_snap_whence;
#endif
unsigned long nvcsw, nivcsw; /* context switch counts */
u64 start_time;         /* monotonic time in nsec */
u64 real_start_time;    /* boot based time in nsec */
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
unsigned long min_flt, maj_flt;

struct task_cputime cputime_expires;
struct list_head cpu_timers[3];

/* process credentials */
const struct cred __rcu *real_cred; /* objective and real subjective task
                                 * credentials (COW) */
const struct cred __rcu *cred;  /* effective (overridable) subjective task
                                 * credentials (COW) */
char comm[TASK_COMM_LEN]; /* executable name excluding path
                             - access with [gs]et_task_comm (which lock
                               it with task_lock())
                             - initialized normally by setup_new_exec */
/* file system info */
struct nameidata *nameidata;
#ifdef CONFIG_SYSVIPC
/* ipc stuff */
struct sysv_sem sysvsem;
struct sysv_shm sysvshm;
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
/* hung task detection */
unsigned long last_switch_count;
#endif

REF:
https://blog.csdn.net/gatieme/article/details/51383272
https://blog.csdn.net/qq_26768741/article/details/54375524

相关文章

  • 进程调度与管理1-task_struct

    **task_struct **msm-4.9/include/linux/sched.h 下面介绍主要部分,有一...

  • 打通Framework与Kernel-谈谈我对进程管理的理解

    Kernel:Linux学习-进程管理与调度(一)-进程描述及其生命周期Linux学习-进程管理与调度(二)-进程...

  • 操作系统笔记

    操作系统主要功能 进程管理(CPU管理) 进程控制:创建,暂停,唤醒,撤销 进程调度:调度策略,优先级 进程通信:...

  • 详解iOS面试:进程与线程

    进程与线程 进程 = 资源管理 + 线程, 进程是资源分配单位,线程是 CPU 调度单位 以前没有线程的时候,进程...

  • Android 进程管理篇(五)-调度策略与优先级

    接上篇cpuset,这篇来看看进程优先级与调度策略管理。 一、进程优先级与调度策略 Linux中,优先级号一共有0...

  • 第三章 处理机调度与死锁

    3.2 作业与作业调度 3.2.3 先来先服务(FCFS)和短作业优先(SJF)调度算法 进程调度 进程调度方式:...

  • 笔记:线程小白基础

    进程与线程的区别 1.进程:操作系统调度的最小单元 如任务管理器里每一个应用就是一个进程2.线程:CPU调度的最小...

  • 2018-07-26 schedule

    linux进程管理之调度与切换 进程的切换过程是在context_switch()中实现的。linux-sourc...

  • Linux 调度

    调度策略与调度类 进程包括两类: 实时进程(优先级高); 普通进程 两种进程调度策略不同: task_struct...

  • 进程调度

    目标 本章将讨论Linux内核是如何进行进程调度的,进程调度程序(也称为调度器)的工作与实现原理。 进程调度程序负...

网友评论

      本文标题:进程调度与管理1-task_struct

      本文链接:https://www.haomeiwen.com/subject/bfnemktx.html