Linux 进程与线程

进程

  • 进程是程序在资源集合上的一次运行
  • 进程= 代码+外设+内存+CPU(使用周期划分时间片轮流分割,满足并发)
  • 进程 = 线程集合+资源集合
  • 进程 = 进程PCB+资源+全局data+code +(线程PCB+线程用户栈/核心栈)*n

引入线程的原因

  • 把进程的两项功能“独立分配资源”“被调度分派执行”分离开来。前一任务由进程完成,后一项交给线程这个实体
  • 多线程切换只需改变堆栈和寄存器。地址空间不变

进程上下文

  • 线程组成部分有:线程唯一标识符、线程状态信息(运行态、就绪态、阻塞态和终止态)。
  • 线程是一条执行路径,它有独立的程序计数器,未运行时保护线程上下文。
  • 线程有执行栈和存放局部变量的私用存储空间。
  • 下图为同源多线程结构的进程
    线程
    进程虚存映像
    上图 进程虚存映像

*线程无挂起状态,线程不是资源拥有单位,挂起无意义。进程挂起后被对换出内存,它的所有线程因共享地址空间也必须全部对换出去。

  • 线程封装执行信息:状态信息、寄存器、执行栈(用户栈指针与核心栈指针)、局部变量、过程调用参数、返回值等私有部分。
  • 进程封装管理信息:对指令代码、全局数据、寄存器、打开的文件和信号量等共享部分。

Linux 进程描述符

task_struct{
	pid; uid; gid;
	state;
	flag;
	*thread_info;
	prio ;  //static_prio   normal_prio
	policy;    //run_list    sleep_avg   *arry   rt_priority  time_slice
	*mm;  *active_mm;
	通信信息:管道,消息队列,共享内存
	*files; *file; total_link_count;
	*signal;  //blocked  signal  sighand  pending
	*real_parent;  children ;  sibling;
	real_timer;  utime;  stime;  thread_group;  哈希链表;  ptrace;
}
  • 在Linux系统中,线程被认为是一个与其他进程共享资源的进程,当做进程来实现。每个线程也用task_struck结构体描述。
  • 系统调用clone() 创建线程,带有多个标志,允许定义父子进程所共享的内容,如果不定义内容共享,则clone()和fork() 完全相同。
    因而,Linux系统中线程就是共享上下文的进程。

创建线程

实现clone()的主要工作为:对于task_struck结构中的fs指针、files指针、sig指针和mm指针不复制,仅将数据结构成员中的使用计数器count +1
这样只有当父进程中相关数据结构成员的count =0 才释放这些数据结构成员所占用的空间。
此外,父子进程共享内存地址空间,copy_on_write策略。

即仅当父进程或子进程对虚存地址进行写操作时,才为子进程的指针所指数据结构分配页面,并复制父进程mm指针所指内容

调用内核函数do_fork()

  1. alloc_pidmap() //子进程分配pid
  2. copy_process() //它完成创建的大部分工作,如调用dup_task_struct() 创建新的task_struck结构,调用alloc_task_struck()为子进程描述符分配空间
  3. alloc_thread_info()//为子进程分配thread_info 结构和核心栈
  4. kmem_cache_alloc()//从slab分配task_struct结构,复制父进程的task_struct信息和thread_info信息
  5. audit_alloc()//分配和初始化进程记账信息
  6. copy_semundo()、copy_files() 、copy_fs() 、copy_signal() 、copy_mm() 、copy_namespace() //分别复制并继承父进程的信号量信息、文件信息、信号处理信息和进程地址空间和名字空间。
  7. copy_thread()//初始化子进程核心栈。调用sched_fork()设置进程调度信息,把子进程状态设置为TASK_RUNNING,并将父进程时间片余额的一半分给它,调用set_task_cpu设置进程所在处理器编号且父子进程在同一处理器上。
  8. SET_LINKS//子进程插入进程双向链表。attach_pid()把子进程插入哈希链表
  9. 如果clone_flags包含CLONE_STOPPED标志,把子进程状态改为TASK_STOPPED;否则调用wake_up_new_task(),它又调用_activate_task()将子进程加入运行队列。
  10. 如果clone_flags包含CLONE_VFORK标志,把父进程设置为阻塞状态直到子进程释放进程地址空间。
    创建成功后,内核让子进程先运行,子进程立即调用exec()避免copy_on_write额外开销。

进程终止

do_exit():

  1. 设置标志,表明进程正在被销毁。
  2. 若进程在定时器队列或信号量队列中等待,则将其移出。
  3. exit_mm()、exit_files()、_exit_fs() 、exit_sem()、exit_sighand()、exit_namespace()、exit_thread()//释放各种资源,其中exit_mm()释放进程地址空间,检测该进程是否由vfork()创建,若是则唤醒父进程。释放资源时,先将其共享计数器 -1 若不为0 表名有其他进程共享该资源,此时直接返回;否则才真正释放资源。
  4. 设置进程退出码,exit_notify() 处理该进程与其父进程和子进程的各种关系,设置状态TASK_ZOMBIE
  5. schedule() 调度切换其他进程

do_exit()结束,但留下task_struct结构。为了留下pid,父进程会wait()检查子进程是否结束,wait()回收TASK_ZOMBIE状态的进程task_struck和pid,若父进程先于子进程结束。init进程会变成子进程的父进程,使task_struct总能回收。

发布了27 篇原创文章 · 获赞 1 · 访问量 699

猜你喜欢

转载自blog.csdn.net/SUKI547/article/details/93743000