当前位置: 首页 > news >正文

机械技术支持 东莞网站建设公司网站域名注册

机械技术支持 东莞网站建设,公司网站域名注册,杭州建设网杨赟,沙河网站建设线程同进程一样都是OS中非常重要的部分#xff0c;线程的应用场景非常的广泛#xff0c;试想我们使用的视频软件#xff0c;在网络不是很好的情况下#xff0c;通常会采取下载的方式#xff0c;现在你很想立即观看#xff0c;又想下载#xff0c;于是你点击了下载并且在… 线程同进程一样都是OS中非常重要的部分线程的应用场景非常的广泛试想我们使用的视频软件在网络不是很好的情况下通常会采取下载的方式现在你很想立即观看又想下载于是你点击了下载并且在线观看。学过进程的你会不会想视频软件运行后在OS内形成一个进程有一个执行流但下载和在线观看是两件事情这两件事情是如何同时进行的呢你可能会想到CPU的时间片轮转不过曾经提到过的时间片轮转是针对进程间的切换的下载和在线观看这两件事本身处于同一个进程内完成你可能还会想到在这个进程内创建一个子进程主进程负责播放子进程负责下载这确实是一个解决问题的方法但是创建一个进程所带来的开销是不小的。本篇文章将会介绍另一种更加轻便的解决方案——线程同时我们需要重新理解CPU时间片轮转的调度单位 目录 什么是线程 深入理解页表  理解进程和线程  实践线程操作  线程终止 线程等待  分离线程  线程取消  TCB 线程的优缺点  优点 缺点  C提供的线程库  什么是线程 按照课本上的定义线程就是进程内部的执行流有多个执行流就意味着一个进程可以同时进行多个操作比如视频软件同时具备播放视频和下载视频的功能如果只有一个执行流那么在播放视频时就不能同时下载视频因为播放视频和下载视频的代码是不同的 以前我们一直认为进程是CPU的调度单位现在我们要改变这个看法被CPU调度意味着被CPU执行也就是一个执行流一个进程里可以有多个线程线程才是CPU的调度单位。所谓的调度单位就是CPU时间片轮转时的切换单位以前我们解释CPU时间片轮转时说的是每个进程都被分配一定的CPU执行时间到达时间CPU会强制切换到下一个进程以保证每个进程都能够被执行 此时通过线程的概念能得知CPU时间片轮转切换的并不是进程而是线程。但上面的话并没有说错一是创建一个进程时默认只有一个执行流也就是只有一个线程时间片轮转时可以认为是切换进程。二是在后面我们将学习到Linux其实并没有线程的概念所谓的线程在Linux中是轻量级进程 有些懵没有关系后面会一一解释原因 现在线程的概念先放到一边我们接下来再次回顾曾经学习过的进程地址空间 深入理解页表  这是笔者曾经多次提到过的进程地址空间映射图并且说过虚拟地址空间和物理内存之间一一映射那么大家有没有思考过这么一个问题假设虚拟地址空间有4G大小物理内存也是4G大而页表是虚拟地址空间和物理地址空间的一一映射这意味着页表自身得有8G大小的空间才能够满足虚拟地址空间和物理内存之间一一映射要知道页表可也得加载到内存中才能让CPU执行照这样的映射法物理内存连一个页表都存不了更何况4G物理内存空间还得留1G给OS呢 可想而知页表的映射不会像哈希表那样一一对应要明白页表的真实构造我们就得从物理内存的划分开始 ​​​ 实际上物理内存是按4kb为单位进行划分的每个大小单位被称为页框大家知道磁盘往内存中加载数据时就是以4kb大小为单位正好能够加载到物理内存的页框中这看似巧妙的背后是前人无数日夜的精心设计 但是这好像并没有说明页表的真实构造别急接着往下看 真实的页表并不是只有一张页表里存储的也不是虚拟地址和物理地址的一一对应页表里正真存储的是物理内存中每个页框的起始地址一张页表里只存储指定数量的页框整个物理内存的页框被多张页表存储着 这多张页表被页目录记录着通过页目录可以找到每一张页表到这里页表的整体结构就出来了可见当初我们刚了解页表时进行了很大程度的简化。但是这就结束了吗笔者只是把页表真实的结构给描绘出来但是并没有解释现在的页表是如何进行映射的  上图是虚拟内存中的一个虚拟地址接下来我们刨析这个虚拟地址如何通过页表最终映射到物理内存  虚拟地址映射到物理内存的方法就在地址本身上通过虚拟地址的前10位可以到页目录中找到该地址对应在哪个页表找到具体的页表之后虚拟地址的中间10位标识着该地址在物理内存的哪个页框里找到具体的页框之后那么最后12位想必大家已经猜出来了 最后12位正是页框内的偏移地址因为一个页框大小就是4kb要想在某个页框内准确定位就要知道该页框的起始地址以及在该页框内的偏移地址。至于对不对咱们验证一下 一个地址的大小是4字节2的12次方是40964096 * 4字节 4kb所以验证正确 如上真实的页表映射结构就展现在我们眼前笔者这里并不是心血来潮讲一下页表通过上述的过程大家能感受到地址空间是进程接触并使用资源的窗口页表则决定了进程拥有哪些资源只有页表映射到的物理内存进程才能够访问那么通过地址空间页表映射进行资源划分就可以对一个进程所用的资源进行分类 理解进程和线程  现在回到对线程的讲解上前面说到过线程是进程内部的执行流一个进程可以拥有多个线程如下图这些线程通过使用共同的地址空间和页表从而共享进程的资源这意味着一个进程里的多个线程共享该进程的资源 前面还提到过CPU的基本调度单位是线程被CPU调度执行那就得有上下文信息那么线程就要保存好自己的上下文信息当被CPU切换执行时可以将上下文信息重新载入到CPU的寄存器中。线程在共享进程资源的同时也会产生自己的执行数据也是需要保存起来的。线程是CPU调度的基本单位这就意味着系统中会存在大量的线程等待被CPU调用根据以往的经验存在大量的线程时OS要有序将其管理起来就得给线程设计一种数据类型设计方法还是多次提到过的先描述再组织 给线程设计数据类型就要考虑线程的id号在系统中唯一同时要能存储上下文信息线程在被CPU调度时要有自己的状态信息同时在执行过程中要有自己的栈结构 并且线程共享进程资源那么文件描述符表什么的也要有越往下举例就能明显感受到这不就是当初学习进程时进程的结构里所包含的内容吗 可以发现进程结构和线程结构大量的内容都是重叠的如果进程和线程两种结构同时存在系统中就会造成大量的冗余而Linux是一个非常注重效率的OS于是聪明的Linux设计者决定不为线程设计一个独立的结构而是采用了轻量级进程结构也就是说在Linux系统中进程和线程实际上使用的是同一种结构 这一点与windows有很大的不同windows就为线程设计了一个独立的结构这也体现了两种OS各自的设计哲学 如何理解线程就是轻量级进程呢如何理解现在的进程概念呢 曾经我们认为一个task_struct就是一个进程一个task_struct有一个执行流并且记录着该执行信息的执行状态。现在学了线程知道进程和线程共同使用task_struct结构对于每个进程或线程内核都会为其分配一个唯一的task_struct结构现在的task_struct是一种轻量级进程也就是说一个进程里可能含有多个task_struct不能再将一个task_struct理解成一个进程。但这并不是说曾经学的就是错误的曾经创建一个进程默认有一个执行流也就是有一个主线程该主线程是创建进程本身的执行流task_struct就是这个主线程的结构故而也可以将task_struct理解成进程本身但是多线程后有多个task_struct再按照以前的方法理解进程就显得不严谨了 假设现在一个进程创建了三个线程那么会有几个task_struct呢 如果一个进程创建了三个线程那么通常会有四个task_struct结构在Linux中每个进程都有一个主线程也就是创建该进程的线程主线程有一个对应的task_struct结构对于每个创建的线程也会有一个对应的task_struct结构。 故而对于一个进程而言如果额外创建了三个线程那么会有一个主线程的task_struct结构以及三个子线程的task_struct结构共计四个task_struct结构这四个task_struct结构共同构成了该进程的线程组成部分 站在CPU的角度上曾经时间片轮转时切换task_struct就是切换一个进程现在CPU时间片轮转切换一个task_struct是切换进程的一个分支如果这个进程只有一个主线程那就是切换进程本身 总而言之现在一个进程有多个执行流进程的概念不能局限于曾经只有一个执行流的task_struct而是一个拥有多个task_struct的承担分配系统资源的基本实体 实践线程操作  说了这么多咱们连线程长什么样子都不知道接下来咱们通过实践来感受线程的魅力 不过在动手敲代码之前需要明确一些事情因为用轻量级进程来表示线程是Linux系统独特的线程处理方式。虽然这能很大提高效率但是也带来了不通用的麻烦很多OS包括OS的理论基础上都是有线程这个概念的因此并不通用Linux的轻量级进程大家都在使用线程接口而你Linux搞特殊提供轻量级进程接口大家是不认的为了解决这个问题Linux工程师就将轻量级进程接口进行封装适配成大家都通用的线程接口 这意味着我们在使用Linux线程接口时要在编译时带上线程动态库即选项 -l pthread 创建一个线程是通过接口 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); 头文件pthread.h    参数 thread:返回线程ID 输出型参数 attr:设置线程的属性attr为NULL表示使用默认属性 start_routine:是个函数地址线程启动后要执行的函数 arg:传给线程启动函数的参数 返回值成功返回0失败返回错误码 第一个参数是一个输出型参数我们在主函数里创建一个pthread_t类型的变量将其的地址传过去创建线程后会把该线程的id写入到这个pthread_t类型变量里 我们目前不需要关心 att r这个参数可以看到第三个参数是一个函数指针其所指向的函数就是创建一个线程后该线程去执行的任务 第四个参数是对第三个参数的补充在我们编写线程要执行的函数时有时是需要外部给这个函数传参的那个这个函数就会默认有一个void* 类型的参数这个参数就是通过pthread_create的第四个参数传递过去的 下面看一个线程代码示例 #includeiostream #includeunistd.h #includepthread.h #includecstring #includecstdlib #includecstdiousing namespace std;void* start_routine(void * arg) {while(true){printf(%s\n, (char*)arg);sleep(1);} }int main() {pthread_t thread_id;char buff[64];snprintf(buff, sizeof(buff), 我是新创建的线程我正在运行);pthread_create(thread_id, nullptr, start_routine, (void*)buff);int counter 10;while(counter--){printf(我是主线程运行倒计时%d\n, counter);sleep(1);}return 0; } 这个示例可以看到真的有两个执行流同时在跑 通过命令ps -aL可以查看所有进程内的线程接下来我们让两个线程不间断运行然后查看这两个线程的相关信息 可以发现当test程序跑起来后出现了两个test线程这两个线程的PID是相同的说明这两个线程来自同一个进程不过两个线程的LWP不同LWPlight weight process即轻量级进程LWP就是所谓的线程ID了并且第一个线程的PID和LWP相同这说明该线程是主线程CPU在调度时是以LWP为标识表示一个特定的执行流 上面只是创建单个线程那么如何同时创建多个线程呢 看下面的demo我们一次创建10个线程并且不停打印他们的序号 #includeiostream #includeunistd.h #includepthread.h #includecstring #includecstdlibusing namespace std;#define MAX 10void* _start_test(void* arg){while(true){sleep(1);cout (char*)arg endl;}return nullptr; }int main(){for (int i 0; iMAX; i){pthread_t tid;char buff[64];snprintf(buff, sizeof(buff), this is %d thread, i);pthread_create(tid, nullptr, _start_test, buff);}while(true){sleep(1);cout 我是主线程endl;}return 0; } 当执行结果出来后完全超出了我们的预期我们本想这10个线程各自打印各自的序号可是结果每个线程都打印序号9 出现这种情况的原因是线程被创建后的执行顺序是不确定的当第一个被创建的进程还没来得及执行它的start_routine函数时主线程就已经把所有的线程都创建完毕了buff是在循环里被被创建的出了循环后就被销毁然后再次创建因为都是在同一个栈里所以每次buff的地址都不变且buff的值不断被覆写直到最后一个线程创建完毕buff的值被覆写为序号9 此时循环退出buff也被销毁了但是由于main函数这个栈还在也没有开其他的栈因此原先buff指向的空间并没有被清理导致所有的线程都打印最后一次覆写buff的内容通过这个demo可得知线程除了独自的PCB独自的上下文结构独自的栈结构其他几乎所有内容都是共享的 每一个线程都有自己独立的栈这是因为一个线程在执行时可能会调用各种函数因此需要一个独立的栈这个栈里的内容不与其他线程共享 线程终止 会创建线程之后自然而然的会想到线程如何终止导致线程终止的原因有很多 1.执行完start_routine()后线程会自动return结束 2.使用pthread_exit()来终止当前线程但是要注意不要习惯性的使用exit()来终止线程exit()是用来终止进程的进程终止该进程内所有的线程都会终止 3.某个线程执行过程中出现错误触发OS检查会给当前线程的进程发送信号进程收到信号会终止该进程内其他所有线程都会终止 4. 一个线程可以调用pthread_ cancel()终止同一进程中的另一个线程 线程等待  同进程一样线程结束后其所申请的各种资源都是需要被回收的不然会产生类似僵尸进程一样的问题线程等待使用函数pthread_join() int pthread_join(pthread_t thread, void **retval); 第一个参数就是被等待的线程id第二个参数是获取该线程的返回值的还记得start_routine有一个void* 返回值吗这个返回值就是通过pthread_join获取的 注意OS维护的是轻量级进程PCB因为Linux特殊的线程方案可以说没有线程概念。程序员日常使用习惯了线程接口因此Linux提供了线程库线程库负责线程接口与轻量级进程接口之间的转换以及维护用户通过接口创建好的线程 分离线程  默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏 如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源使用接口int pthread_detach(pthread_t thread); 可以是线程组内其他线程对目标线程进行分离也可以是线程自己分离pthread_detach(pthread_self());   pthread_self()获取自己的线程id 需要注意的是一旦一个线程已经处于分离状态那么该线程就不能被等待 线程取消  线程取消也就是当线程跑起来后我们通过主线程或者其他线程可以取消这个线程继续运行 也可以自己取消自己 int pthread_cancel(pthread_t thread); 返回值成功返回0失败返回错误码 注意只有当该进程运行起来有自己的线程ID时才可以被取消 TCB PCB是Linux内核用来管理轻量级进程的内核因为Linux没有线程的概念程序员要使用线程的接口因此要通过线程库进行转接那么程序员每申请一个线程线程库就得维护好这个线程和轻量级进程进行转换那么TCB就是线程库维护线程的结构 由图中可以得知我们接收的所谓的线程id值其实就是库中维护的该线程TCB的起始地址  线程的优缺点  优点 线程的使用能非常大程度上发挥多核CPU的实力并且创建多个线程比创建多个进程的开销要小的多为什么呢 如果CPU执行时要切换一个进程那么要切换的内容至少包含页表虚拟地址空间PCB上下文数据 而切换一个线程那么只需要切换PCB上下文数据等主要内容 CPU在执行一个进程时会在寄存器中缓存该进程的很多热点数据例如虚拟地址空间页表等一旦切换进程这些热点数据要全部重新加载而切换线程这些数据不需要动 缺点  在运行计算密集型程序时线程需要不停的计算持续占有CPU切换到其它线程的时间就会延长导致效率低下 使用多线程编程会有互斥和同步等问题程序的编写和维护成本很高 C提供的线程库  虽说Linux提供了线程库但是Linux的线程接口和Windows下的线程接口很多都是不同的这就导致程序的可移植性很低 C11之后在语言层面上对Linux和windows平台下的线程接口再进行一次封装。如此以来用C线程库编写的多线程程序可以同时在这两个OS平台下执行代码的可移植性大大提高 下面的demo简单演示了如何使用C提供的线程库这部分属于C的知识了笔者将在C专栏中介绍其详细使用方法 #includethread #includeiostream #includeunistd.husing namespace std;void* start_routine() {int counter 10;while(counter--){sleep(1);cout 我是新创建的线程运行倒计时 counter endl;}return nullptr; }int main() {//创建一个线程并把执行函数传递过去thread t1(start_routine);cout 我是主线程 endl;//主线程阻塞等待回收子线程t1.join();cout 线程回收完毕准备退出endl;return 0; } 文章的最后大家可以尝试自己模仿C的线程库对Linux的线程库再进行一次封装
http://www.sczhlp.com/news/246815/

相关文章:

  • 效能建设网站泉州软件开发培训机构
  • 网站开发风险分析开发者模式打开有什么影响
  • 代做毕设的网站设计师常上的网站
  • 茶叶淘宝店网站建设ppt模板WordPress腾讯云cos存储插件
  • 10.31博客
  • 做网站销售怎么找客户网站建设常见问题处理
  • 茶叶公司网站源码动漫网站设计方案
  • 做兼职翻译的网站光谷网站建设制作
  • 网站建设公司哪家好要选磐石网络项目网站建设
  • 购物网站起名免费企业网站模板
  • 广州市医院网站建设哪家好在id打开wordpress
  • 三合一网站一般多少钱千万不要去代理记账公司上班
  • 夸克网站免费进入自动链接 wordpress
  • 甜水园网站建设上海网站设计开发
  • 网站软文推广范文旅游业网站建设方案特点
  • 制作论坛类网站模板免费下载网站前端设计与制作ppt
  • 做淘宝详情页好的网站网站标题主关键词
  • 做影视会员网站哈尔滨网页案例分析
  • 织梦可以放两个网站食品网站建设策划方案
  • 医院网站源码 asp咨询公司ppt
  • 建立网站的第一步苏州网站建设 苏州网络推广专家
  • 个人网站的名字如何用ps做网站
  • 织梦电影网站模板建设公司网址
  • 如何做网站的源码龙岩酷搜网
  • wordpress+整站下载沈阳营销型网站设计教程
  • 高端品销售网站上海网站建设哪家公司好
  • 网站内链技巧网站建设招聘
  • 网站建设 系统维护京网站建设
  • 2025年优秀的温升试验机行业内口碑厂家排行榜
  • 25.10.30