进程

 

1. 什么是进程

在 《面对多任务处理、程序员是怎样榨干计算机资源的》提到,不管是进程、线程、协程都是用来处理用户任务的。从内核角度来看,进程就是用来分配CPU、内存等资源的。后来觉得进程分配CPU时间片太重了,于是轻量级进程、线程就出现了,成了CPU调度的基本单位,但是资源分配的基本单位还是进程。通俗的讲,进程被定义为程序运行的一个实例。为了管理进程,内核引入了一个进程描述符的数据结构。

2. 进程的生命周期(状态)

 

进程描述符中的state字段描述了进程所处的状态。粗略的看,进程要么在运行状态,要么不再运行状态。但非运行状态也可能有多种,比如就进程在等待外部信号、等待IO就绪等,或者说它可以马上运行,但是当前CPU被其他进程所占用了,前一种状态我们称之为睡眠,后一种状态我们称之为等待,当然还有一种终止状态。它们之间的状态转换如上图所示。

// include\linux\sched.h
#define TASK_RUNNING        0
#define TASK_INTERRUPTIBLE  1
#define TASK_UNINTERRUPTIBLE    2
#define __TASK_STOPPED      4
#define __TASK_TRACED       8
/* in tsk->exit_state */
#define EXIT_DEAD       16
#define EXIT_ZOMBIE     32
#define EXIT_TRACE      (EXIT_ZOMBIE | EXIT_DEAD)
/* 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_STATE_MAX      2048

在内核源码中,我们看到了很多状态,远不止刚刚讲的四种,不过它们也都属于这四大类。

TASK_RUNNING        为运行状态

TASK_INTERRUPTIBLE  可中断的等待状态,进程休眠,直到某个条件为真。 什么条件呢?比如传递一个信号过来。再比如说,产生一个硬件中断,释放了正在等待的资源。这都可以作为触发条件。

TASK_UNINTERRUPTIBLE    不可中断的等待状态。一般很少,在设备驱动中会用到这种状态。

__TASK_STOPPED    进程收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOUT等信号时,进入暂停状态。

 __TASK_TRACED      debugger时进程暂停的状态。

EXIT_ZOMBIE     僵死状态,进程被终止,资源被释放。但是父进程没有回收,没有wait waitpid等操作,内核不能丢弃这些描述符,还会残留在进程表中。

EXIT_DEAD       退出状态

后两个状态,既可以放在state中,也可以放在exit_state中。

ps命令中STAT 就是对应了进程的当前的状态。

ubuntu@VM-0-16-ubuntu:~$ ps -au
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      1065  0.0  0.1  15308  2048 ttyS0    Ss+  Apr19   0:00 /sbin/agetty -o -p -- \u --keep-baud 115200,38400,9600 ttyS0 vt220
root      1075  0.0  0.0  15532  1624 tty1     Ss+  Apr19   0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
ubuntu   13980  0.0  0.2  22004  5152 pts/0    Ss   16:52   0:00 -bash
ubuntu   14371  0.0  0.1  36720  3412 pts/0    R+   16:55   0:00 ps -a

3. 进程结构分析

task_struct 包含的非常多的属性,下面列出些常见的。

struct task_struct { 
    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
    void *stack; //  栈指针
    atomic_t usage;// 进程计数,表示当前有几个进程正在使用此结构
    unsigned int flags; /* per process flags, defined below */
    unsigned int ptrace;//ptrace 调试跟踪用的
    // 多处理器用到的
#ifdef CONFIG_SMP
    struct llist_node wake_entry;
    int on_cpu;
    unsigned int wakee_flips;
    unsigned long wakee_flip_decay_ts;
    struct task_struct *last_wakee;

    int wake_cpu;
#endif
    // 运行队列和进程调试相关程序
    int on_rq;
    //优先级
    int prio, static_prio, normal_prio;
    unsigned int rt_priority;
    // 调度器
    const struct sched_class *sched_class;
    struct sched_entity se;
    struct sched_rt_entity rt;
    ......
    // 进程调度策略
    unsigned int policy;
    int nr_cpus_allowed;
    cpumask_t cpus_allowed;
    ......
    // 进程的地址空间
    struct mm_struct *mm, *active_mm; 
    ......  
    // 进程pid
    pid_t pid;
    pid_t tgid; 
    ......
    // 孩子/兄弟进程列表
    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 */
    .....
};

大概分为以下及部分

  • 状态和执行信息
  • 虚拟内存的信息
  • 身份凭据
  • 所使用文件的信息
  • 线程是信息
  • 进程间通信的信息

4. 进程类型

  1. fork产生的进程,被看作是原进程的拷贝,称为子进程。它们是两个独立的实例,有同一组打开的文件、同样的工作目录、内存中同样的数据。这里有写时拷贝的优化。
  2. exec可以从一个可执行程序中加载另一个应用程序,使之替代当前进程。
  3. clone用于实现线程,和fork类似,不同的是clone出的轻量级进程共享父进程的内存、打开文件、堆等资源。

 

 

参考:

[0] 深入理解Linux内核

[1] 深入Linux内核架构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值