• 线程概念
    • 什么是线程?
      • CPU视角:
      • 与进程的关系:
    • Linux下线程概念
      • 那么CPU能分辨task_struct是进程和线程吗?
    • 理解页表
      • 为何以多级页表实现?
      • 多级页表是如何实现的?
      • 多级页表的优点
    • 多线程的特点
      • 优点
      • 缺点
    • 线程异常
    • 线程用途
    • 进程和线程的关系
  • 线程控制
    • 创建线程
    • 等待线程
    • 线程退出
    • 线程分离
    • 线程id

线程概念

什么是线程?

CPU视角:

线程是CPU调度的基本单位

与进程的关系:

线程是进程内部的执行流,线程比进程粒度更细,调度成本更低,切换成本更低

线程在进程内部运行,本质是在进程地址空间内运行,一个进程至少有一个执行线程

透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

Linux下线程概念

在Linux系统的CPU眼中,看到的PCB其实就是轻量化进程,看图理解:

​ 将上图的所有的task_struct,进程地址空间,页表这三者之和称为进程,而对于每个task_struct就是线程。所以创建线程时只创建PCB,线程只占用进程地址空间中的一部分代码和一部分数据。

站在OS的角度重新理解进程:进程是承担分配系统资源的实体 (向系统申请资源的基本单位) 。

对于只有一个task_struct的进程就是单执行流进程,不止一个task_struct的进程就是多执行流进程。

那么CPU能分辨task_struct是进程和线程吗?

​ 答案:不能也不需要,在Linux中进程和线程没有概念上的区分,都叫执行流,CPU只需要关心一个个独立执行流。无论进程内部只有一个执行流还是有多个执行流,CPU都是以task_struct为单位进行调度的。

​ 类比理解:进程和线程关系就好像家庭和家庭成员,家庭是分配社会资源的基本载体,家庭成员占一部分资源。

​ 站在CPU的视角:task_struct <= 传统的进程(当且仅当进程是单执行流,等号成立),也就是所CPU看到的PCB比传统的进程更轻量化了 。

理解页表

为何以多级页表实现?

​ 以32位平台为例,会产生232个地址,倘若是一张页表建立虚拟地址和物理内存的映射,则需要232个页表项,页表项中还包含权限相关的信息,比如字符串常量不能修改,因为页表中有读写权限标识了变量的读写权限,这样一张页表的占据空间是巨大的,所以Linux下的页表是以多级页表实现的。

多级页表是如何实现的?

以32位平台为例,多级页表的组成:页目录 + 页表,将32位地址分成3部分=10+10+12

页目录:虚拟地址前10位 映射到-> 页表

页表:虚拟地址的中间10位 映射到-> page的起始地址(页框4KB)

画图理解:

物理内存被分成了一个个4KB大小的页框page,可见对内存的管理就是对数组struct page mem[1024*1024]的管理。

多级页表的优点

1.将进程虚拟地址管理和内存管理,通过页表+页框page解耦

2.使用分页机制和按需创建页表,节省了内存空间

多线程的特点

优点

线程创建:创建线程的代价比创建进程的代价小很多

线程切换:与进程切换相比,线程需要OS做的工作少很多

线程占有资源:线程占用的资源比进程少很多

利用多处理器:能充分利用多处理器的可并行数量

并行处理任务:在等待慢速IO的时候,程序可以执行其他计算任务

计算密集型应用:在多处理器系统上运行,不同CPU执行不同的计算线程

I/O密集型应用:将IO操作重叠,不同线程等待不同的IO操作,提高性能

缺点

性能损失:计算密集型应用的计算线程数量大于处理器数量,较大的性能消耗,增加额外的同步和调度开销,而可用的资源不变。

健壮性降低:编写多线程程序需要更全面深入的考虑,因为时间的影响导致不该共享的变量共享了会造成影响,因为多个线程使用同一个地址空间,会相互影响,又称线程之间是缺乏保护的。

缺乏访问控制:进程是访问控制的基本粒度,线程的IO函数会影响整个进程,比如不同同时往显示器打印

编程难度提高:排查问题难度更大,编写多线程程序比单线程程序更难

线程异常

​ 当一个线程异常的时候,会导致整个进程退出,因为线程异常的时候,就是进程异常了,进程就会收到OS发来的信号,进而进程退出。当创建新的线程异常的时候,新线程异常退出的信号是整个进程的,主线程不用主动获取。线程异常会使异常的线程影响其他的线程,所以线程的健壮性较低。

线程用途

1.提高CPU密集型程序的执行效率

2.提高IO密集型程序的用户体验

进程和线程的关系

​ 进程是OS分配资源的基本单位,线程是CPU调度的基本单位,因为线程共用一份地址空间,因此代码段(Text Segment、Data Segment),数据段都是共享的,定义函数、全局变量,在各线程都可以看到,除此之外,线程还共享:

文件描述符表

每种信号处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)

当前工作目录

用户 id 和 组 id

线程共享进程数据,也拥有自己的一部分数据:

线程 ID

一组寄存器

线程栈

errno(错误码)

信号屏蔽字

调度优先级

线程控制

​ 原生线程库:Linux下有一套遵从POSIX标准的线程库,与线程有关的函数构成了一个完整的序列,绝大多数函数名以pthread_开头,使用函数时需要引入头文件<pthread.h>,编译的时候需要使用-lpthread选项表示要链接线程库pthread。在C++11中也更新了线程库,C++的线程库也是封装了这套原生线程库的。

创建线程

使用函数pthread_create创建线程,函数原型:

参数说明:

thread:输出型参数,表示线程id

attr:设置创建线程的属性,传入NULL表示使用默认属性

start_routine:线程执行的函数,该函数的返回值类型是void*,作为线程等待的输出型参数

arg:线程执行的函数传入的参数

函数返回值:创建成功返回0,创建失败返回错误码

等待线程

使用函数pthread_join等待退出的线程,倘若不做此工作也没有分离线程,就会造成类似僵尸进程般的内存泄露。

函数原型:

参数说明:

thread:等待的线程id

retval:输出型参数,输出进程的退出码

函数返回值:等待成功返回0,错误时返回错误码

线程退出

退出方法:

1.在线程运行函数中return

void*threadRoutine(void*args)
{...return nullptr;
}

2.线程调用函数pthread_exit主动退出

3.线程调用pthread_cancle取消其他线程(通常是主线程调用)

注意:线程调用exit函数会退出进程,任何一个线程调用都代表整个进程退出。

线程分离

使用pthread_detach函数分离指定线程,相当于告诉OS线程退出时自动释放线程资源

可以让其他线程分离,或者自己主动分离。让主线程分离时,主线程退出相当于进程退出 ,分离主线程后,主线程一般不退出(常驻内存)。

线程分离相当于线程退出的第四种退出方式, 延后退出。

线程id

线程调用函数pthread_self可以获取线程的线程id

代码实例:

#include <pthread.h>
#include <iostream>
#include <unistd.h>
using namespace std;void *threadRoutine(void *args)
{pthread_detach(pthread_self());for (int i = 0; i < 20; i++){cout << "[" << pthread_self() << "] pthread " << (int)i << "is running..." << endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tp[3];for (int i = 0; i < 3; i++){pthread_create(&tp[i], nullptr, threadRoutine, (void *)i);}while (1){cout << "main thread is running!" << endl;sleep(1);}return 0;
}

编译运行程序,使用ps -aL查看线程,L表示查看轻量级进程(LWP)。

可以看到多个线程往显示器打印出现了混乱。上图的LWP,是OS标识轻量级进程的编号,即线程:LWP=1:1。

​ 线程是独立执行流,在运行过程中会产生临时数据,需要有自己独立的栈结构,所有的代码执行都在进程的地址空间中,线程的全部实现,并没有体现在OS内,而是OS提供执行流,具体的线程结构由库来管理,在进程地址空间加载的共享区中的线程库维护着线程结构,每个用户级线程的控制结构体的起始地址就是线程id,如图:

线程的局部存储指的是线程私有的全局变量,指定线程的代码中在全局变量前加上 __thread可以使变量变成私有的全局变量。

链接的线程库:

线程控制块中有私有栈,底层是通过调用clone函数实现的:

这个函数可以创建共享内存空间的进程,其实是Linux下的线程的原型。

另外使用syscall(SYS_gettid)间接调用gettid 可以获得LWP,如下:

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>int main(int argc, char *argv[])
{pid_t tid;tid = syscall(SYS_gettid);tid = syscall(SYS_tgkill, getpid(), tid);
}

另外使用syscall(SYS_gettid)间接调用gettid 可以获得LWP,如下:

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>int main(int argc, char *argv[])
{pid_t tid;tid = syscall(SYS_gettid);tid = syscall(SYS_tgkill, getpid(), tid);
}

Linux多线程---线程概念和线程控制相关推荐

  1. Linux 多线程(一)线程概念:线程概念、线程与进程、线程间的独有与共享、多线程与多进程、线程控制

    线程概念 线程与进程 线程间的独有与共享 多线程与多进程 线程控制 线程概念 什么是线程 线程是进程中的一条执行流,执行程序中的某部分代码.linux下没有具体实现的线程,只有库函数用pcb来实现的线 ...

  2. [Linux]线程概念_线程控制(线程与进程的区别与联系 | 线程创建 | 线程等待 | 线程终止 | 线程分离 | LWP)

    文章目录 线程概念 进程和线程的关系 线程的优点 线程的缺点 线程控制 Linux线程和接口关系的认识 线程创建 线程ID及进程地址空间布局 线程等待 线程终止 线程终止状态 线程分离 LWP和pth ...

  3. 【Linux】线程概念与线程控制

    认识线程 线程是一个执行流(运行代码,处理数据) ​ 1.操作系统使用pcb来描述一个程序的运行-------pcb就是进程 ​ 2.linux下通过pcb模拟实现线程,因此linux下的线程是一个轻 ...

  4. Linux多线程——使用信号量同步线程

    http://blog.csdn.net/ljianhui/article/details/10813469/ 信号量.同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对 ...

  5. Linux多线程编程:pthread线程创建、退出、回收、分离、取消

    文章目录 Linux线程 1.简单了解一下线程 2.线程创建:pthread_create 3.线程传参注意事项 4.线程退出:pthread_exit 5.线程回收:pthread_join 6.线 ...

  6. 【Linux 内核】进程管理 ( 内核线程概念 | 内核线程、普通进程、用户线程 | 内核线程与普通进程区别 | 内核线程主要用途 | 内核线程创建函数 kernel_thread 源码 )

    文章目录 一.内核线程概念 二.内核线程.普通进程.用户线程 三.内核线程.普通进程区别 四.内核线程主要用途 五.内核线程创建函数 kernel_thread 源码 一.内核线程概念 直接 由 Li ...

  7. Linux多线程实践(9) --简单线程池的设计与实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

  8. java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part1~整起(线程与进程篇:线程概念、线程状态、线程死锁)

    这个题目我感觉很多大哥大姐和我一样,虽然夹在众位大哥大姐中跟着一块喊着"多线程与高并发"的口号,但是这里面其实包含的东西并不像名字里面这么少.现在就开始咱们的旅程吧. 特此感谢,低 ...

  9. Linux 多线程(二)线程安全:线程安全、互斥与互斥锁、死锁、同步与条件变量

    线程安全 互斥 死锁 同步 线程安全 所谓线程安全,其实就是当多个线程对临界资源进行争抢访问的时,不会造成数据二义或者逻辑混乱的情况(通常情况下对全局变量和静态变量进行操作时在会出现) 常见的线程安全 ...

最新文章

  1. python题目关于企业利润_【每日一练】巧用python实现利润计算
  2. hdu 携程全球数据中心建设 (球面距离 + 最小生成树)
  3. Practice:在2008 core上部署和管理DHCP服务器
  4. 欢迎参加天津PDC Party的活动
  5. MATLAB函数随笔之计算篇
  6. Leetcode每日一题:3.无重复字符的最长子串
  7. Understanding ES6 -- 深入理解ES6书籍
  8. C#: switch语句的重构『网摘』
  9. ElasticSearch全文搜索引擎之term和match的区别
  10. ios 输入法扩展_iOS8、iOS9都可用的原生输入法扩展词库(搜狗词库)
  11. win7西捷硬盘测试软件,Seagate希捷SeaTools硬盘检测工具
  12. 运筹学动态规划逆序解法_运筹学第七章 动态规划讲解.ppt
  13. PHP 实现 阿里云 短信接口调用
  14. 东周科目三考场5号线_光明东周科目三考场,5条道图纸心得分享
  15. python安装和学习-最后推荐winpython
  16. STM32F10xxx20xxx21xxxL1xxxx Cortex-M3程序设计手册 阅读笔记四(5):系统滴答定时器
  17. 蓝桥杯单片机必备知识-----(9)超声波测距
  18. 数据结构__图书管理系统(C语言)
  19. 放大电路的分析方法(以共射放大电路为例、交流通路、直流通路、三极管等效电路及其如何等效的)
  20. 千兆无源光网络(GPON)行业调研报告 - 市场现状分析与发展前景预测

热门文章

  1. 美国中小学停课,20个优质在线学习平台推荐
  2. signed和unsigned的比较
  3. Linux内核驱动开发-USB热插拔信息调取
  4. 最最最基础的C++代码
  5. Android9.0 Launcher启动Activity详解(一)
  6. Linux 那么多命令的来源
  7. vray 用于室内渲染的10大技巧,看进来!
  8. 智慧医院IT基础设施建设方案
  9. cytoscape3.2.0 java_【看图说话】Cytoscape的“傻瓜式”教程
  10. 打印系统开发(36)——打印方面套打是什么意思,如何实现套打?