进程和线程

线程和进程

linux中,进程和线程都用task_struct结构体表示,只是进程和线程的区别在于task_struct中的mm和files等资源是否共享。

记录层面
task_struct中有两个字段记录的是编号

  • pid_t pid; /* process id */ task编号,唯一标识一个task
  • pid_t tgid; /* thread group id */ 线程组编号,同一进程中的线程tgid相同

系统调用层面
getpid返回的是task的tgid,gettid返回的是task的pid(和posix的thread id不同)

用户层面
ps显示的PID是实际的tgpid,ps -aL显示的LWP是实际的pid,top -H显示的PID是实际的pid

创建上
进程使用fork函数(使用系统调用fork),线程使用pthread_create函数(使用系统调用clone),二者内部实现都是调用do_fork函数,只是传入的参数不同。
创建进程的flag是SIGCHLD,创建线程的flag包括CLONE_VM,CLONE_FS,CLONE_FILES,CLONE_SIGNAL,CLONE_THREAD,CLONE_SYSVSEM,CLONE_SETTLS,CLONE_PARENT_SETTID和CLONE_CHILD_CLEARTID。

CLONE_VM:和父进程共享内存地址空间,因此共用父进程的mm_struct结构
CLONE_FS:和父进程共享文件系统信息,因此共用父进程的fs_struct结构
CLONE_FILES:和父进程共享文件描述符,因此共用父进程的files_struct结构

而区分进程还是线程的关键在于是否拥有独立的内存空间等资源,因此常说进程是资源分配的基本单位,线程是CPU调度的基本单位。

资源占有

进程占有的资源

  • 内存地址空间
  • 全局变量,静态变量等
  • 打开文件描述符
  • 子进程
  • 信号量
  • 信号处理函数

线程私有

  • 线程ID
  • 寄存器
  • 程序计数器

为什么进程切换比线程切换效率低

CPU上下文:CPU寄存器和程序计数器
进程上下文:寄存器,内存地址空间(堆栈,全局变量,静态变量),内核栈等
进程切换时需要保存和恢复更多的上下文,甚至涉及到内存换入换出,
而同一进程的线程切换只需要保存和恢复线程私有数据,比如寄存器和栈。

之前不太认可页表切换开销大、进程上下文切换开销大的说法,而是认为主要原因是进程切换时涉及到页表的切换,切换之后TLB就失效了,地址转换需要重新查找页表,而线程切换不需要切换页表。
当时想的是进程只有创建时申请内存,切换时不涉及内存分配变动,且页表也在内存中,页表切换只简单涉及页表寄存器的更改。但实际上进程切换可能伴随着页调度,内存不够时进程内存数据要换出,需要的内容换入,所以这么说也可以。

附录

排查线程CPU占用过高的问题

# 查看进程,获取进程ID
top

# 查看进程的线程,获取线程ID
top -H -p <PID>
# 或者
ps -T -p <PID>

# 查看线程的栈信息
pstack <PID>

参考

posted @ 2024-04-27 21:12  trashwin  阅读(11)  评论(0编辑  收藏  举报