Linux 内核101:进程数据结构

本文参考了:linux

基本概念清单

  • Linux 里面,进程和线程到了内核,统一都叫作任务(Task)。
  • 每一个 task 都有一个数据接口task_struct,用来保存task 状态。

任务列表

linux 内核中有一个包含全部 task 的链表,把全部的 task_struct 连起来。git

如图所示:github

task_struct

struct 定义:docker

struct list_head		tasks;
复制代码

看一下每一个task_struct包含了哪些重要的字段。bash

任务 ID

和任务 ID 相关的字段有下面这些:数据结构

pid_t pid;
pid_t tgid;
struct task_struct *group_leader; 
复制代码

这三个字段的具体含义为:工具

  • pid : 每一个 task 都有一个 pid,是惟一的,无论是进程仍是线程。
  • tgid: 指向主线程的 pid
  • group_leader: 指向进程的主线程

任何一个进程,若是只有主线程,那 pid 是本身,tgid 是本身,group_leader 指向的仍是本身。spa

可是,若是一个进程建立了其余线程,那就会有所变化了。线程有本身的 pid,tgid 就是进程的主线程的 pid,group_leader 指向的就是进程的主线程。操作系统

有了 tgid 以后,咱们就能够判断一个 task 是线程仍是进程了。线程

那么区分是进程仍是线程有什么用呢?考虑下面几个场景:

  • ps命令

ps默认展现的是全部进程的列表,而不是把全部的线程都列出来,那会显得很乱没有重点。

  • 给线程发送kill -9信号?

假如说咱们给某个进程中的一个线程发送了退出信号(好比kill -9),那么咱们不该该只退出这个线程,而是退出整个进程(至于为何请看下文)。因此就须要某种方式,可以获取该线程所在进程中全部线程的 pid。

从一个进程中杀死某一个线程是很是危险的操做。 好比说某个 thread正在进行分配内存的工做,这时候它会hold 内存分配器的 lock。若是你把它强制杀死了,这个锁就永远不会释放,那么其余的 thread 也会中止。因此须要主进程的协助,来优雅地退出全部的线程。

上图来源于 这个so 上的问答

不信的话,咱们能够来作一个实验。

下图显示的是htop工具,白色的表示进程绿色的表示线程。能够看到每一个线程确实都有一个惟一的 PID。

如今让咱们来给图中标记的PID 为21656code-server线程发送kill -9信号,而后发现,整个进程都退出了:

上图中,code-server这个 docker 容器进程在一分钟前退出了。

信号处理

源代码地址:github.com/torvalds/li…

/* 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 : 哪一个信号正在被处理

注意这里的struct signal_struct *signal;指向了一个signal struct。这个struct 中还有一个struct sigpending pending;。前面提到过须要区分线程和进程,这里也能够看出一点端倪。第一个是线程组共享的,一个是本任务的。

任务状态

一个 task 的任务状态一共能够取下面的这些值:

/* 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

复制代码

总结一下

个人公众号:全栈不存在的

相关文章
相关标签/搜索