简介
进程和线程这两个词,每个程序员都十分熟悉,但是想要很清晰的描述出来却有一种不知道从何说起的感觉。所以今天结合一个具体的例子来描述一下进程与线程的相关概念:在terminal上敲出a.out这个自己编译出来可执行程序路径后,这个程序是怎么在linux系统中运行起来的?如果对于这个问题有兴趣的同学可以继续往下看。
对于linux内核下进程和线程的理解程度我经历过以下几个阶段:
- 知道进程和线程这两个基本概念:知道进程/线程相关的一些资源占用,调度的一些基本理论知识;知道进程=程序+数据等等;知道PC寄存器、指令、栈帧、上下文切换等一些零碎的概念。
- 知道进程和线程都是可独立调度的最小单位; 知道进程拥有独立地址空间,线程共享进程的地址空间,有自己独立的栈;知道pthread协议相关接口定义,以及会使用pthread接口创建线程/进程等操作。
- 知道线程在linux中本质上就是进程,可以分为内核线程,可调度线程和用户空间线程。内核线程是内核启动后的一些deamon线程用于处理一写内核状态工作,比如kswapd,flushd等;可调度线程是能够被内核调度感知的线程,也就是正常的native thread;用户空间线程不为内核调度感知,比如goroutine,gevent等,需要自己实现相关调度策略。
- 知道进程和线程在内核中是如何调度的:实时调度器,完全公平调度器等相关调度策略;知道可调度线程有内核栈和用户态栈两个不同的栈,当执行系统调用时需要从用户态栈切换到内核栈执行一些特权指令,比如write,mmap之类的。
- 知道在terminal bash中输入一个cmd之后,相关的进程是如何被操作系统运行起来的,知道线程是如何创建出来的。
- 知道栈中的指令是如何加载到CPU中,如何开始执行,上下文切换时的寄存器如何操作等。(这个阶段还没达到,不过不做内核开发的话,也没必要达到这个阶段。)
那么我们就通过结合linux源码以及一些man文档一起来深入了解一下进程和线程的实现,结合简介中说的例子来看一下linux用户进程及线程是如何被创建起来以及执行的。
【文章福利】小编推荐自己的Linux内核技术交流群:【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)
内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料
学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈
进程 & 线程
task_struct,mm_struct这两个数据结构,分别描述进程/线程的数据结构及进程虚拟地址空间,是内核管理的关键结构,下面是相关内核代码。
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
/* -1 unrunnable, 0 runnable, >0 stopped: */
volatile long state; // 运行状态
void *stack; // 内核栈空间地址
atomic_t usage;
/* Per task flags (PF_*), defined further below: */
unsigned int flags;
unsigned int ptrace; //
#ifdef CONFIG_SMP // 调度相关
struct llist_node wake_entry;
int on_cpu; // 是否已被分配到CPU上执行调度
#ifdef CONFIG_THREAD_INFO_IN_TASK
/* Current CPU: */
unsigned int cpu; // 被分配到的CPU
...
#endif
// 调度相关
int on_rq; // 是否在runqueue队列中(就绪)
int prio; // 优先级
int static_prio; // 静态优先级
int normal_prio; // 普通优先级
unsigned int rt_priority; // 实时优先级
// 上述优先级结合一个简单的算法来计算task_struct的权重,用于计算时间片
const struct sched_class *sched_class; // 调度类,诸如实时调度,完全公平调度等
struct sched_entity se; // 关联到的调度实体,这是完全公平调度器真正调度的单位
struct sched_rt_entity rt; // 关联到的实时调度实体,这是实时调度器真正调度的单位
#ifdef CONFIG_CGROUP_SCHED
struct task_group *sched_task_group; // 组调度,cgroup相关
#endif
struct sched_dl_entity dl; // deadline调度器调度实体
...
struct mm_struct *mm; // 虚拟内存结构
struct mm_struct *active_mm;
...
pid_t pid; // 进程id
pid_t tgid; // 组id
/* Real parent process: */
struct task_struct __rcu *real_parent; // 真实的父进程(ptrace的时候需要用到)
/* Recipient of SIGCHLD, wait4() reports: */
struct task_struct __rcu *parent; // 当前父进程
/*
* Children/sibling form the list of natural children:
*/
struct list_head children; // 子进程(当前进程fork出来的子进程/线程)
struct list_head sibling; // 兄弟进程
struct task_struct *group_leader;
/*
* 'ptraced' is the list of tasks this task is using ptrace() on.
*
* This includes both natural children and PTRACE_ATTACH targets.
* 'ptrace_entry' is this task's link on the p->parent->ptraced list.
*/
struct list_head ptraced;
struct list_head ptrace_entry;
/* PID/PID hash table linkage. */ //线程相关结构
struct pid *thread_pid;
struct hlist_node pid_links[PIDTYPE_MAX];
struct list_head thread_group;
struct list_head thread_node;
struct completion *vfork_done;
/* CLONE_CHILD_SETTID: */
int __user *set_child_tid;
/* CLONE_CHILD_CLEARTID: */
int __user *clear_child_tid;
u64 utime; // 用户态运行时间
u64 stime; // 和心态运行时间
...
/* Context switch counts: */
unsigned long nvcsw; //自愿切换
unsigned long nivcsw; //非自愿切换
/* Monotonic time in nsecs: */
u64 start_time; // 创建时间
/* Boot based time in nsecs: */
u64 real_start_time; // 创建时间
char comm[TASK_COMM_LEN]; // 执行cmd名称,包括路径
struct nameidata *nameidata; // 路径查找辅助结构
...
/* Filesystem information: */
struct fs_struct *fs; // 文件系统信息,根路径及当前文件路径
/* Open file information: */
struct files_struct *files; // 打开的文件句柄
/* Namespaces: */
struct nsproxy *nsproxy;
/* Signal handlers: */
struct signal_struct *signal; // 信号量
struct sighand_struct *sighand; // 信号量处理函数
...
/* CPU-specific state of this task: */
struct thread_struct thread; // 线程相关信息
};
struct mm_struct {
struct {
... // 虚拟地址空间相关数据结构(虚拟地址空间比较复杂,这里不做过多介绍)
unsigned long task_size; /* size of task vm space: