linux查看最大线程数,32位linux进程线程在内存中的样子

 2023-09-22 阅读 17 评论 0

摘要:1.线程诞生史 1.1 线程诞生的原因 早期是没有线程概念的,只有进程的概念,操作系统以进程为调度单位。——可以这么来理解:早期进程相当于现在的单线程的进程(只有一个线程的进程,创建进程时,里面有一个函数入口称之为主线程)

1.线程诞生史
1.1 线程诞生的原因
早期是没有线程概念的,只有进程的概念,操作系统以进程为调度单位。——可以这么来理解:早期进程相当于现在的单线程的进程(只有一个线程的进程,创建进程时,里面有一个函数入口称之为主线程)。

后来出现了线程,为啥?因为CPU多核心的出现。为了更好的利用起来多核心,榨干cpu性能。
为啥会出现多核心?因为主频提升遇到了瓶颈,主频达到3GHz等已经好多年了,但目前市面上流通的还是2-3GHz为主。你有见过10GHz,100GHz主频的cpu么?估计几百年都难哩。那为啥主频能提高性能呢?因为主频越高,单位时间内执行的指令就越多,程序运行速度就越快,自然性能就高了。
那主频遇到了瓶颈,该咋提高CPU性能呢?那就用数量来凑呗,于是多核心时代来临了。

当然编译器也是一方面,编译器做的越好,同样的代码编译出来的指令就越少,那程序运行自然就更快。——但这得看软件,优秀的程序员了。但是提升主频是硬件厂家能直接干涉的东东,所以这块也是前期cpu发展的一块发力之处,直到遇见瓶颈,才堆积多核心。

1.2 用户态线程先出现
刚开始出现线程,内核开发人员是不支持的——人家懒得支持因为没有说服力,你得证明线程比进程有优点呀,不然人家那么忙凭啥给你支持?于是最开始线程的实现是用户态实现的线程,由用户来管理。内核调度器对用户态线程没有感知,因为和内核调度器打交道的是进程,内核看不到用户态线程——它相当于一个瞎子。这有很大弊端——一旦用户态线程调用阻塞IO接口那么整个进程都被切换出去,这样其他同一个进程内的用户态线程都被阻塞了,这很不符合预想。

linux查看最大线程数。1.3 内核线程后出现
随着用户态线程的出现,证明了线程的优点:切换快,占用资源少。——于是各大操作系统的内核开发者就开始支持了内核线程——用户态线程是进程在用户态创建的由用户管理,现在线程的创建由内核来创建和管理了——即为内核线程——内核调度器的调度度单位就换成了线程,也就是说内核不再是个瞎子了,它能看到线程了——内核线程完全由内核管理,掌控其生死。内核管理的任务粒度又更细了。那么这个时候就来到了内核线程时代。这样同一个进程里的内核线程被阻塞了,同进程内的其他内核线程照样可以执行不会受到干扰。这样极大地提高了并发并行能力,而且这也满足了人们最初对线程的幻想——利用多核心实现并行优势、榨干CPU性能。

2.linux内核线程的实现
linux线程的实现很是偷懒,因为linux线程的实现没有额外增加新的结构体,直接拿已经存在的进程的结构体struct task来用——太偷懒了,直接偷人家的结构体,可见程序员的懒惰性。这就有问题了,在linux内核眼中,进程和线程都是task,那怎么区分呢?从结构体上来说,不区分。同一个进程的线程结构体里面的mm等虚拟地址空间等直接都指向同一个地址(同一个指针),用c语言的指针解决,创建线程的时候直接指向主线程指向的地址空间等资源,即对新task相关成员信息直接用指针指过去,这就是为啥linux的线程称之为轻量级进程的原因——只有创建新的进程的时候才会费大力气进行地址空间开辟等操作,而创建线程时直接指针一指完事!——好一个一阳指功法!

3.32位下linux的进程线程在内存中的模样。
在这里插入图片描述
虚拟地址是OS对内存的抽象,也就是OS给进程看的假象。为啥叫虚拟地址?其实就是假的地址。
如上图,在32位下,每个进程都觉得自己拥有4G的虚拟地址空间,0-3G的虚拟地址是给进程的用户态使用的,所有进程的3G-4G地址间都是指向同一份的,也即共享的。为啥这么设计?你想如果每个进程的高1G地址空间不共享,单独的,那切换时太复杂了,且浪费空间,很不爽。所有进程共享内核的话,那只需要维护一个内核资源就行了,方便、节省空间。
我好有一个比喻:进程就是房东,进程中的各线程都是寄人篱下的租客。每个租客共享整个房间的公共区域,然后也有各自的私密空间——每个租客都有自己的小秘密!
具体说就是进程中的所有线程共享同一个地址空间、代码段、数据段、bss段、文件映射区、堆区等等(租客共享的公共区域),但每个线程都有独立的线程栈(租客独享的私密空间)。可以看到图中多线程里比单线程不一样的地方是在堆区多了很多普通线程栈——注意了普通线程(调用glic或uclic等运行时库线程接口pthread开辟的线程)的栈是在文件映射区开辟的——每个栈代表一个线程。主线程栈(也称之为进程栈)是fork进程的时候在栈区开辟的。

4.线程栈
线程栈也是栈,只不过是给线程用的。为何有栈呢?因为cpu的寄存器不够用,人他妈的太聪明了,正好用栈记录下,这样入栈出栈,保存和恢复cpu各寄存器的值。每个线程其实就相当于一个函数,只不过一般线程是一个无限循环的函数。因此每个线程都有一个线程栈。创建线程的时候线程栈会初始化为默认的cpu寄存器值。等到线程正常退出时会调用默认的一个函数来做清理工作之类的。
线程的运行动态图之一:线程正常运行时,线程栈就是在周而复始地入栈出栈。——就像一个气球吹大又放气变小,再吹大再放气变小,如此循环往复;也像内燃机或者打针时针管里的活塞运动,周而复始不断地进进出出;也像一个心脏周而复始地在跳动(不断缩紧和膨胀,嘭嘭、嘭嘭响)。
每个线程正常运行时,其栈达到的最大深度是有上限的(这个和程序代码有关,一旦写完就固定了)。栈溢出就是捅破天了。
特别注意:每个线程有各自独立的线程栈——不与其他线程共享。

5.为什么要用虚拟地址?
(1)安全,虚拟地址把用户程序与真实物理地址隔离了——相当于把用户程序关在笼子里、关在监狱里了!如果没有虚拟地址,直接暴露物理地址给用户,用户程序可以把os干死。
(2)解决重定位问题——换入换出导致进程地址不断变化的问题(重定位问题)。虚拟地址的引入解放了编译器、链接器和程序员的工作,增加了os的工作,进一步提高了生产力。编译器、链接器和cpu看到的都是虚拟地址,且都是从0开始,os和MMU看到的是物理地址。那么编译器和程序员等不用考虑换入换出时进程对应的真实物理地址——这部分由os做了,os保证把不同的进程虚拟地址空间映射到不同的物理地址,os负责了换入换出地址映射事宜。
(3)解决内存不够,小内存运行大程序的问题。基于虚拟地址的swapping技术(交换技术)以及程序局部性原理(时间局部性和空间局部性原理,即时间上在不远的将来还会访问该地址,空间上访问了改地址那么下次很有可能访问它相邻地址空间,一个程序运行起来并不需要全部加载只需要一小部分即可运行),使得大程序也能运行在小内存上。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/81654.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息