取进程的创建时间_Linux 内核101:进程数据结构

4962668723875d3f094cff0b48dcbed1.png

本文参考了:

  • 极客时间专栏《趣谈Linux操作系统》12.进程数据结构(上):项目多了就需要项目管理系统
  • Chapter 3. Process Management- Shichao's Notes
  • How to kill an individual thread under a process in linux?

基本概念清单

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

任务列表

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

如图所示:

8c3c56bcba474ad8e79b82b8e081f9ec.png

task_struct

struct 定义:

struct list_head        tasks;

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

任务 ID

和任务 ID 相关的字段有下面这些:

pid_t pid;
pid_t tgid;
struct task_struct *group_leader; 

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

  • pid : 每个 task 都有一个 pid,是唯一的,不管是进程还是线程。
  • tgid: 指向主线程的 pid
  • group_leader: 指向进程的主线程

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

但是,如果一个进程创建了其他线程,那就会有所变化了。线程有自己的 pid,tgid 就是进程的主线程的 pid,group_leader 指向的就是进程的主线程。

有了 tgid 之后,我们就可以判断一个 task 是线程还是进程了。

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

  • ps命令

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

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

假如说我们给某个进程中的一个线程发送了退出信号(比如 kill-9),那么我们不应该只退出这个线程,而是退出整个进程(至于为什么请看下文)。所以就需要某种方式,能够获取该线程所在进程中所有线程的 pid。

从一个进程中杀死某一个线程是非常危险的操作。 比如说某个 thread正在进行分配内存的工作,这时候它会hold 内存分配器的 lock。如果你把它强制杀死了,这个锁就永远不会释放,那么其他的 thread 也会停止。所以需要主进程的协助,来优雅地退出所有的线程。

104f8c00d95aee3b148c0974f3f6b3b8.png

ec0399b22703e2ac0bb0c3b1083077fe.png
上图来源于 这个so 上的问答。

不信的话,我们可以来做一个实验。

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

c3ed09467012602c5c1a6a8dcc2adc8b.png

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

ece46ba58819aeb207a91e987c5f144a.png

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

信号处理

源代码地址: https:// github.com/torvalds/lin ux/blob/master/include/linux/sched.h#L863
/* 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 : 哪个信号正在被处理

注意这里的 structsignal_struct*signal;指向了一个 signal struct。这个struct 中还有一个 structsigpending 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

4d2c8c5e6f0c60f4f7fb57825291db37.png

总结一下

905192fa8c45e3e122b33f09fc5b076e.png

1f0fee63e70be41403dc89f2225ddebb.png

我的公众号:全栈不存在的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值