12 | 进程数据结构(上):项目多了就需要项目管理系统

本文仅作为学习记录,非商业用途,侵删,如需转载需作者同意。

进程或者线程,到了内核中,统一都叫任务(Task),由一个统一的结构 task_struct 进行管理。

在这里插入图片描述

一、Linux的任务管理都干些啥

Linux 内核先弄一个链表,将所有的task_struct 串起来。

struct list_head    tasks;

1.1、任务ID

任务ID 是任务的唯一标识。

task_struct 里面涉及到任务ID的有下面几个:

pid_t pid;
pid_t tgid;
struct task_struct *group_leader; 

进程和线程到内核里后,统一变成了任务,带来了2个问题

任务展示
内核中按照任务列表把所有信息都展示出来给用户看,很长,很多不是该用户的。

任务下发指令
下发指令肯定是需要按照项目组,而不是项目组中的单个小组。
同理:
linux 中有进程id,也有线程id。

pid 是process id;
tgid 是 thread group id

一个进程,如果只有主线程,pid是自己,tgid 是自己,group_leader 指向的还是自己

一个进程,如果创建其他线程。线程有自己的pid,tgid是进程的主线程的pid,group_leader 指向的就是进程的主线程。

有了 tgid,我们就知道 tast_struct 代表的是一个进程还是一个线程。

1.2、信号处理

task_struct 就是信号处理的字段:

/* Signal handlers: */
struct signal_struct    *signal;
struct sighand_struct    *sighand;
sigset_t      blocked;
sigset_t      real_blocked;
sigset_t      saved_sigmask;
struct sigpending    pending;
unsigned long      sas_ss_sp;
size_t        sas_ss_size;
unsigned int      sas_ss_flags;

被阻塞暂不处理(blocked);
尚待处理(pending)
正在处理(sighand)

处理的结果:忽略、结束进程

信号处理函数:默认使用用户态的函数栈;
也可以开辟新的栈专门用于信号处理,就是 sas_ss_xxx 这三个变量的作用。

下发信号的时候,需要区分 进程 和 线程。

task_struct 里面有一个 struct sigpending pending。如果我们进入 struct signal_struct *signal 去看的话,还有一个 struct sigpending shared_pending。它们一个是本任务的,一个是线程组共享的。

1.3、任务状态

JIRA 中的状态如下:
在这里插入图片描述
在 task_struct 里面,涉及任务状态的是下面这几个变量:


 volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
 int exit_state;
 unsigned int flags;

state(状态)可以取得值可以定义在 include/linux/sched.h 头文件中。

/* Used in tsk->state: */
#define TASK_RUNNING                    0
#define TASK_INTERRUPTIBLE              1
#define TASK_UNINTERRUPTIBLE            2
#define __TASK_STOPPED                  4
#define __TASK_TRACED                   8
/* Used in tsk->exit_state: */
#define EXIT_DEAD                       16
#define EXIT_ZOMBIE                     32
#define EXIT_TRACE                      (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_DEAD                       64
#define TASK_WAKEKILL                   128
#define TASK_WAKING                     256
#define TASK_PARKED                     512
#define TASK_NOLOAD                     1024
#define TASK_NEW                        2048
#define TASK_STATE_MAX                  4096

从上面的定义来看,state是通过 bitset 的方式设置的,也就是说当前是什么状态,哪一位就置一。

在这里插入图片描述

TASK_RUNNING 并不是说进程正在运行,而是表示进程在时刻准备运行的状态。
当处于这个状态的进程获得时间片的时候,就是在运行中;
如果没有获得时间片,就说明它被其他进程抢占了,在等待再次分配时间片。

在运行中的进程,一旦要进行一些I/O的操作,需要等待I/O完毕,这个时候会释放CPU,进入睡眠状态。

在Linux中,有两种睡眠状态:
1、TASK_INTERRUPTIBLE (可中断的睡眠状态):
在浅睡眠,等待I/O完成。 一个信号来的时候,进程还是要被唤醒。唤醒后进行信号处理。 程序员可以根据自己的意愿来写处理函数,例如收到某些信号,放弃等待这个I/O操作完成,直接退出,或者收到某些消息继续等待。

2、TASK_UNINTERRUPTIBLE (不可中断的睡眠状态):
不可被信号唤醒,只能死等I/O操作完成。
一旦I/O操作因为特殊原因不能完成,这个时候谁也叫不醒这个进程。
kill 也是一个信号,kill 信号也被忽略。
除非重启电脑,没有其他办法。

因此这个是比较危险的事情,不然不要把进程设置成 TASK_UNINTERRUPTIBLE

于是就有了新的进程睡眠状态 TASK_KILLABLE 可以终止的新睡眠状态
它的运行原理类似TASK_UNINTERRUPTIBLE,只不过可以响应致命信号。

从定义可以看出来,TASK_WAKEKILL 用于在接收到致命信号时唤醒进程,而 TASK_KILLABLE 相当于这两位都设置了。


#define TASK_KILLABLE           (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)

TASK_STOPPED :是在进程接收到 SIGSTOP、SIGTTIN、SIGTSTP 或者 SIGTTOU 信号之后进入该状态。

TASK_TRACED:进程被debugger 等进程监视,进程执行被调试程序所停止。 当一个进程被另外的进程所监视,每一个信号都会让进程进入该状态。

一旦一个进程要结束,先进入的是EXIT_ZOMBIE状态,但是这个时候它的父进程还没有使用 wait() 等系统调用来获知它的终止信息,此时进程就成了僵尸进程。

EXIT_DEAD 是进程的最终状态

EXIT_ZOMBIE 和 EXIT_DEAD 也可以用于 exit_state

上面的进程状态和进程的运行、调度有关系,还有其他的一些状态,我们成为标志

放在flags 字段中,被定义为宏,以PF开头:

#define PF_EXITING    0x00000004
#define PF_VCPU      0x00000010
#define PF_FORKNOEXEC    0x00000040

PF_EXITING :正在退出。在函数 find_alive_thread 中,找活着的线程,遇到有这个flag 的,直接跳过。

PF_VCPU :进程运行在虚拟CPU上。 在函数 account_system_time 中,统计进程的系统运行时间,有这个flag,就调用 account_guest_time ,按照客户机的时间进行统计。

PF_FORKNOEXEC:fork完了,还没有exec。 在do_fork 函数里面调用copy_process,这个时候把flag 设置为 PF_FORKNOEXEC。 当exec中调用了load_elf_binary的时候,又把这个flag去掉。

1.4、进程调度

进程状态的切换往往涉及调度,下面这些字段都是用于调度的。

//是否在运行队列上
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;
struct sched_dl_entity    dl;
//调度策略
unsigned int      policy;
//可以使用哪些CPU
int        nr_cpus_allowed;
cpumask_t      cpus_allowed;
struct sched_info    sched_info;

上面说的是进程管理复杂的数据结构。
下图是进程管理 task_struct 的结构图。

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值