linux 线程_浅谈Linux线程模型
Thread Basic
基础概念
线程是操作系统能够调度和执行的基本单位,在Linux中也被称之为轻量级进程。从定义中可以看出,线程它是操作系统的概念,在不同的操作系统中的实现是不同的,不过今天分享的猪脚是Linux Thread。
对于Linux操作系统而言,它对Thread的实现方式比较特殊。在Linux内核中,其实是没有线程的概念的,它把所有的线程当做标准的进程来实现,也就是说Linux内核,并没有为线程提供任何特殊的调度语义,也没有为线程实现特定的数据结构。取而代之的是,线程只是一个与其他进程共享某些资源的进程。每一个线程拥有一个唯一的task_struct结构,Linux内核它仅仅把线程当做一个正常的进程,或者说是轻量级进程,LWP(Lightweight processes)。
对于其他的操作系统而言,比如windows,线程相对于进程,只是一个提供了更加轻量、快速执行单元的抽象概念。对于Linux而言,线程只是进程间共享资源的一种方式,非常轻量。举个简单例子,假设有一个进程包含了N个线程。对于那些显示支持线程的操作系统而言,应该是存在一个进程描述符,依次轮流指向N个线程。这个进程描述符指明共享资源,包括内存空间和打开的文件,然后线程描述它们自己独享的资源。相反的是在Linux中,只有N个进程,因此有N个task_struct数据结构,只是这些数据结构的某些资源项是共享的。
这里再总结一下,Linux线程是进程资源共享的一种方式,而其他操作系统,线程则是一种实现轻量、快速执行单元的抽象概念或者实体。这里再深入的理解一下,Linux中的线程和进程的区别。这也是诸多面试题中,最常见的一个。
资源共享
Linux线程与进程的区别,主要体现在资源共享、调度、性能几个方面,首先看一下资源共享方面。上面也提到,线程其实是共享了某一个进程的资源,这些资源包括:
- 内存地址空间
- 进程基础信息
- 大部分数据
- 打开的文件
- 信号处理
- 当前工作目录
- 用户和用户组属性
- 等等
哪些是线程独自拥有的呢?
- 线程ID
- 一系列的寄存器
- 栈的局部变量和返回地址
- 错误码 errno
- 信号掩码
- 优先级
- 等等
这里说一个黑科技,线程拥有独立的调用栈,除了栈之外共享了其他所有的段segment。但是由于线程间共享了内存,也就是说一个线程,理论上是可以访问到其他线程的调用栈的,可以用一个指针变量,去访问其他线程的局部栈帧,以访问其他线程的局部变量。
调度
说到调度,就得提到进程的上下文切换。上下文切换也被称作为进程调度或者任务切换,简单的来说是把CPU从一个进程或者线程切换到另一个执行。概括的来说,线程的上下文切换,要比进程更加快速,因为本质上,线程很多资源都是共享进程的,所以切换时,需要保存和切换的项是很少的。
线程上线文切换时,虚拟地址空间是不变的,但是进程上下文切换时,是需要重新映射虚拟地址空间。进程切换上下文时,进出OS内核&寄存器切换,是最大的时间支出。更模糊的代价是上下文切换时,会干扰处理器的缓存机制。当上下文切换时,处理器需要重新cache一些内存。
这里更大的一个区别时,当更改虚拟地址空间时,CPU 的 TLB 等也会被刷新,导致接下来的内存访问更加耗时,所以相对线程切换来说,进程的切换耗时更大。
性能
从性能方面,来查看一下线程与进程的对比。由于线程更加轻量,导致线程的创建速度、切换速度都要高于进程。这里就有一个疑问了,从上面提到的各个方面来看,好像线程都要优于进程,那么有没有啥缺点呢?
线程缺点
线程同样也有缺点,最大的缺点是线程的不安全性,缺乏保护机制。就是上面提到的黑科技,因为线程间共享数据,一个线程可以重写另外一个线程的堆栈,导致出现一些异常的情况。除此之外,线程还有以下缺点:
- 共享属性:全局变量是在所有线程间共享的,访问时是需要同步加锁。
- 很多库函数是线程非安全的,多线程编程时,需要注意这一点。
- 线程的健壮性不强,如果一个线程crash了,那么整个应用程序就跪了。
应用场景
上面提到了线程与进程的对比,也提到了线程的优点和缺点,那么什么情况下适合用线程呢?简单的来说,计算密集型的任务,适合于多线程来处理。因为计算密集型任务,需要耗费很多CPU,上下文的切换是非常频繁的,而线程切换速度是高于进程的,所以使用线程是更加适合的。在实际的编程过程中,根据业务的场景,再结合进程和线程的优缺点对比,来决定适合的编程模型。
线程创建
那么Linux中线程是如何创建出来的呢?上面也提到,在Linux中线程是一种资源共享的方式,可以在创建进程的时候,指定某些资源是从其他进程共享的,从而在概念上创建了一个线程。在Linux中,可以通过clone系统调用来创建一个进程,它的函数签名如下:
#include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);
我们在使用clone创建进程的过程中,可以指明相应的参数,来决定共享某些资源,比如:
clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);
这个clone系统调用的行为类似于fork,不过新创建出来的进程,它的内存地址、文件系统资源、打开的文件描述符和信号处理器,都是共享父进程的。换句话说,这个新创建出来的进程,也被叫做Linux Thread。从这个例子中,也可以看出Linux中,线程其实是进程实现资源共享的一种方式。
内核线程
在Linux中,还存在一个Kernel Thread的概念,也就是内核线程。内核创建一些内核线程来执行一些后台任务。相对于普通的进程,内核线程完整的存在于内核空间,是没有自己的地址空间的,也就是mm指针为空,它的操作仅存在于内核态,并且也不会上下文切换到用户态。不过内核线程和普通进程类似的是,是可调度和可抢占的。
同步
由于线程间共享了很多资源,所以在多线程的编程环境下,为了保障结果的准确性和一致性,需要对共享资源的访问进行同步。常见的同步方式,也就是加锁,以保障操作共享资源时,不会出错。在Linux中,锁的种类大致有四种:
- 互斥锁
- 读写锁
- 条件变量
- 自旋锁
- 内存屏障
有兴趣的同学,看看看下这篇文章:http://blog.lecury.cn/2016/02/21/%E5%90%8C%E6%AD%A5%E4%BA%92%E6%96%A5(%E9%94%81).html 。总结来说,锁的代价是高昂的,所以在设计高并发、高吞吐的程序时,尽量避免锁的使用,或者减少锁的区间。
常见的多线程编程模式
下面谈一下实际工作中,要如何合理的线程呢?这里我简单的提出三种常见的线程模型。
- leader-follow 模型(主从)
- 线程与连接对应,并发度等于线程数。
- 所有线程经历accept->close整个过程。
- 适用于连接数少、处理时间长、CPU密集型服务。
- producer-consumer模型(生产者消费者)
- 主线程用于accept请求,并将fd放置在消费队列pendingpool中。
- pendingpool进行连接的维护工作。
- 多个worker竞争pendingpool的连接。
- 适用于连接数多、处理速度快的业务。
- 高并发索引模型
- 无锁设计
- 将请求或者事务映射到具体线程处理
踩过的坑和小技巧
- 同步
- 过载保护
- 公平调度
- 析构出core
更加详细的内容,请参考知乎Live:
如何理解和应用Linux线程模型?www.zhihu.com
感兴趣的同学,可以加QQ群: 853832829 一起学习~
linux 线程_浅谈Linux线程模型相关推荐
- linux 易语言窗口程序_浅谈Linux入门的基本知识
浅谈Linux入门的基本知识 图形模式与文字模式的切换方式Linux预设提供了六个命令窗口终端机让我们来登录. 默认我们登录的就是第一个窗口,也就是tty1,这个六个窗口分别为tty1.tty2 - ...
- python同步锁和互斥锁的区别_浅谈Python线程的同步互斥与死锁
这篇文章主要介绍了浅谈Python线程的同步互斥与死锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 线程间通信方法 1. 通信 ...
- Linux先发送条件变量,linux 条件变量 浅谈Linux条件变量的使用
想了解浅谈Linux条件变量的使用的相关内容吗,在本文为您仔细讲解linux 条件变量的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:linux,条件变量,下面大家一起来学习吧. Linu ...
- linux read函数_浅谈Linux内核IO体系之磁盘IO
前言 Linux I/O体系是Linux内核的重要组成部分,主要包含网络IO.磁盘IO等.基本所有的技术栈都需要与IO打交道,分布式存储系统更是如此.本文主要简单分析一下磁盘IO,看看一个IO请求从发 ...
- java线程池和线程实例化_浅谈Java 线程池原理及使用方式
一.简介 什么是线程池? 池的概念大家也许都有所听闻,池就是相当于一个容器,里面有许许多多的东西你可以即拿即用.java中有线程池.连接池等等.线程池就是在系统启动或者实例化池时创建一些空闲的线程,等 ...
- java内存模型浅析_浅谈java内存模型
不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的.其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改.总结jav ...
- java归还线程_再谈java线程
什么是等待唤醒机制? 这是多个线程间的一种协作机制. 就是一个线程进行规定协作后,就进入到了等待状态'wait()',等待其他线程执行完他们的指定代码后,再将其唤醒'notify()'; 在有多个线程 ...
- linux系统安装coerplayer,浅谈Linux操作系统下的多媒体播放器
freeamp 2.1.0 freeamp是一个遵循gpl的音频播放器,彻底得free以及支持跨平台(同时支持window和linux)使用是它最大的特点.虽然在支持格式上略显单薄,但整体界面以及操作 ...
- linux网卡IO,浅谈Linux 网络 I/O 模型简介(图文)
1.介绍 Linux 的内核将所有外部设备都看做一个文件来操作(一切皆文件),对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符).而对一个sock ...
最新文章
- Manning、Ostendorf、Povey、何晓冬、周明共话多模态NLP的机遇和挑战(附视频)
- java_有返回值线程_提前加载例子
- 出现Failed to get convolution algorithm的解决方法
- 《C++面向对象高效编程(第2版)》——3.17 编写内存安全类
- android加载转圈动画,android 围绕中心旋转动画
- 模拟操作网页 webBrowser
- 洛谷 P1886 滑动窗口
- MKL学习——基本操作C++实现
- [java理论篇]--java的其他常用API
- Ubuntu 16.04安装录屏软件SimpleScreenRecorder
- Elasticsearch--进阶-match全文检索---全文检索引擎ElasticSearch工作笔记011
- arduino 长传出错_求助,米思齐写arduino总是上传失败。
- 聪聪用计算机计算235乘49,四年级数学题库
- TDD、BDD、ATDD、DDD 软件开发模式
- 专利局文件如何删除后面的注意事项
- 基于Python的文字生成图片系统
- kindle刷机安卓版_亚马逊 kindle 刷机 过程记录
- c# 身份证地区码返回地区名 [对照表]
- 机器学习小组知识点4:批量梯度下降法(BGD)
- 电脑电池出场容量与目前最大容量查询查询
热门文章
- 订单最小量限制的增强
- python 模糊匹配 合并_Python Pandas模糊合并/匹配重复
- 塑钢瓦图片_塑钢瓦和彩钢瓦哪种好 如何准确选购
- 给Python的类和对象动态增加属性和方法
- pthread 立即停止线程_线程取消(pthread_cancel)
- java 连接janusgraph_基于JanusGraph的大数据图数据库
- ubuntu交叉编译x264报错:‘X264_VERSION’ undeclared(已解决)运行version.sh
- C语言strstr()函数(在主字符串里查找子字符串,返回第一次找到的子字符串以及后面的字符串)
- Windows MSVC 符号表(.lib文件)(C++符号表解析)(符号表是如何产生的)(第四步:链接)
- windows怎么用qt MinGW gcc编译c代码