linux线程概念和控制
1.linux线程概念1.1 线程概念和实现原理1.1.1 线程概念在一个程序里的一个执行路线就叫做线程(thread)线程是一个进程内部的控制序列进程至少有一个执行线程线程在进程内部运行本质是在进程地址空间运行在linux系统重CPU看到的PCB比传统进程更加轻量化通过进程虚拟地址空间可以看到进程大部分资源将进程资源合理分配给每个执行流形成线程执行流1.1.2 实现原理理解线程必须清楚内核如何进行资源划分尤其是代码可以看到是一个进程两个线程pa -aL查看线程LWPlight weight process 轻量级进程编译的时候并没有带-lpthread指明链接的库因为ubuntu 24.04版本做了处理吧但是也要考虑到不同系统兼容性的问题如何通过页框中任意一个字节的地址获取页框的地址获取数据所在页框的起始地址addr(~0x0000 0FFF)因为页框的起始地址低12位为00x0000 0FFF取反后是0xFFFF F000高20位相与0还是0,1还是1低12位与0相与结果是0对内存的管理就转化为了对数组的增删查改比如要分配内存先找到数组中未分配的page也就是表示是否分配的标志位为未分配接着把标志位改为已分配并且给到进程页框的起始地址页框是否分配分配之后是否加载页框的数据是否虚假加载到磁盘等都需要进行管理struct page里多是union和位图所以并不大假设一个页表项12B标志位比如虚拟地址属于内核还是用户当前数据是否在内存权限等CR3寄存器存储的是页目录的起始物理地址不是虚拟因为页表本来就是完成虚拟地址到物理地址转化如果CR3存的是页目录的起始虚拟地址这个虚拟地址又要通过CR3转化没完没了高10位是在页目录中的下标中间10位是在页表中的下标用来定位页表中的页表项页表项存的是页框的起始物理地址起始物理地址的低12位为全0没必要存储所以在存储的时候把页框起始物理地址右移12位等到用的时候拿出来左移12位即可也就是只需要保存20位存进页表项还有12位可以拿来存标志位比如权限等最后12位是在页框中的偏移量物理页框号找到了由于虚拟地址的页和物理地址的页框大小一致所以低12位就是在页框中的偏移量算下来页目录页表一共有2^101 *2 ^10 *44MB4KB这是把页表拉满也就是把4GB物理内存全部映射因为一共2 ^10 *2 ^10个页表项每个页框是4KB也就是4GB单个进程不可能拥有全部内存因为OS本身也需要空间OS也会进行内存管理换入换出等所以单个进程页表远小于4MB大小并且由于懒加载机制需要的时候进行写时拷贝会把变量所在的4KB全部拷贝因为局部性原理修改某个变量很大程度上周围的变量也会被修改与其每次都缺页中断不如一次搞定空间换时间也是因为局部性原理磁盘上的数据可以提前加载到内存也就是预加载机制缺页中断等页表就更少CPU中的MMU硬件自动完成从虚拟地址到物理地址的映射转换因为频次高处理流程固定如果出现转换失败比如权限问题未命中共享写入缺页中断等异常再由OS介入处理页表是进程看到内存资源的窗口拥有更多的虚拟地址就拥有更多的内存资源划分区域也就是划分虚拟地址编译阶段本质就是划分物理内存CR3寄存器也称页目录基址寄存器属于进程的上下文刚开机时没有OS访问物理地址从磁盘中加载OS才有页表这个过程和CR0有关CR1保留CR2存储页错误线性地址引起崩溃的地址写到CR2内部这样可以通过调试器或者弹出的报错信息获取clone第一个参数是回调函数也就是创建从这个函数之后开始执行第二个参数是分配的栈顶第三个参数决定是否拷贝资源arg是泛型指针后续参数是父线程的id线程本地存储和子线程的id线程共享虚拟地址空间共享有效虚拟地址线程通过虚拟地址空间共享进程大部分资源进程是独立性大部分资源独占线程是共享性大部分资源共享肯定要涉及通信1.2 分页式存储管理1.2.1 虚拟地址和页表的由来如果没有虚拟内存和分页机制每一个用户程序在物理内存上对应的空间空间必须是连续的每一个程序的代码、数据长度不一按照上面的映射方式物理内存会被分割成各种离散、大小不同的块。一段时间后有些程序运行完会推出占据的物理内存空间被回收导致物理内存存在很多碎片我们希望OS提供给用户的空间是连续的但物理内存不需要连续把物理内存按照固定长度页框进行分割也叫物理页每个页框包含一个物理页(page)。一个页的大小等于页框的大小。大多数32位体系结构支持4KB的页64位体系结构一般支持8KB的页页框是一个存储区域页是一个数据块可以存放在任何页框或磁盘中CPU通过虚拟地址空间间接访问物理内存虚拟地址空间是OS为每一个正在执行的进程分配逻辑地址32位机器是0~4GB-1OS通过将虚拟地址空间和物理内存地址建立映射关系也就是页表记录每一对页和页框的映射关系从而CPU能间接访问物理内存地址也就是说将虚拟内存下的逻辑地址空间分为若干页将物理地址空间分为若干页框通过页表把连续的虚拟内存映射到若干个不连续的物理内存也解决使用连续物理内存造成的碎片问题会有外部碎片1.2.2 物理内存管理假设可用物理内存有4GB空间按照页框大小4KB划分有1M个页框OS对1M个物理页进行管理比兔哪些页正在被使用哪些页空闲等内核用struct page结构表示系统中的每个物理页出于节省内存的考虑用了大量的union/* include/linux/mm_types.h */structpage{/* 原⼦标志有些情况下会异步更新 */unsignedlongflags;union{struct{/* 换出⻚列表例如由zone-lru_lock保护的active_list */structlist_headlru;/* 如果最低为为0则指向inode * address_space或为NULL * 如果⻚映射为匿名内存最低为置位 * ⽽且该指针指向anon_vma对象 */structaddress_space*mapping;/* 在映射内的偏移量 */pgoff_t index;/* * 由映射私有不透明数据 * 如果设置了PagePrivate通常⽤于buffer_heads * 如果设置了PageSwapCache则⽤于swp_entry_t * 如果设置了PG_buddy则⽤于表⽰伙伴系统中的阶 */unsignedlongprivate;};struct{/* slab, slob and slub */union{structlist_headslab_list;/* uses lru */struct{/* Partial pages */structpage*next;#ifdefCONFIG_64BITintpages;/* Nr of pages left */intpobjects;/* Approximate count */#elseshortintpages;shortintpobjects;#endif};};structkmem_cache*slab_cache;/* not slob *//* Double-word boundary */void*freelist;/* first free object */union{void*s_mem;/* slab: first object */unsignedlongcounters;/* SLUB */struct{/* SLUB */unsignedinuse:16;/* ⽤于SLUB分配器对象的数⽬ */unsignedobjects:15;unsignedfrozen:1;};};};...};union{/* 内存管理⼦系统中映射的⻚表项计数⽤于表⽰⻚是否已经映射还⽤于限制逆向映射 搜索*/atomic_t _mapcount;unsignedintpage_type;unsignedintactive;/* SLAB */intunits;/* SLOB */};...#ifdefined(WANT_PAGE_VIRTUAL)/* 内核虚拟地址如果没有映射则为NULL即⾼端内存 */void*virtual;#endif/* WANT_PAGE_VIRTUAL */...}参数1.flags存放页状态包括页是否脏在加载到内存后是否进行修改是否锁定在内存防止内存页交换到磁盘flag的每一位单独表示一种状态至少可以同时表示出32种不同的状态。这些标志定义在linux/page-flags.h 中其中一些比特位比较重要比如PG_locked指定页是否锁定PG_untodata表示页数据已经从块设备读取并且没有出现错误2._mapcount表示页表中有多少项指向该页被引用多少次当为0时表示内核没有引用这一页就可以分配。3.virtual是页的虚拟地址通常情况下是页在虚拟内存中的地址有些内存所谓高端内存不永久映射到内核地址空间上这个域的值为NULL需要的时候动态映射这些页struct page与物理页相关但并非与虚拟页相关系统中每个物理页要分配page结构体如果所有页均如此则假设page结构体大小为32B则要消耗32MB与系统4GB相比管理系统中这么多物理页面代价不算太大页的大小对于内存利用和系统开销非常重要页太大页内剩余较大不能利用的空间页内碎片。页太小减小页内碎片大小但是页表过长占用内存频繁进行页转化加重系统开销页大小应适中通常为521B-8KBwindows/linux系统页框大小为4KB1.2.3 页表页表中的每一个表项指向一个物理页的开始地址在32位系统中虚拟内存最大空间为4GB每一个用户拥有的虚拟内存空间需要1M个表项虚拟内存看上去被虚线“分割”成一个个单元仍是连续的虚线单元仅表示与页表中每一个页表项的映射关系最终映射到相同大小的一个物理内存页页表中的物理地址和物理内存之间是随机的映射关系哪里可用指向哪里物理页虽然最终的物理内存是离散的但与虚拟内存对应的线性地址是连续的。处理器在访问数据、获取指令时用的都是线性地址只有连续即可通过页表找到物理地址假设在32位系统中地址长度为4B页表每一个表项是4B页表大小4MB/4KB占用1K个物理页使用页表的初衷就是把进程占用的空间划分为页可以不连续的存放在物理内存但是页表需要使用1K个连续的页框背道而驰由局部性原理很多时候进程再一段时间内只需要访问某几个页就可以正常运行没必要一次让所有物理页常驻内存解决大容量页表的方法把页表看成普通文件进行离散分配对页表再分页形成多级页表把单一页表拆成1024个体积更小的映射表1K每个表中的表项个数*1024 *表的个数仍然可以覆盖4GB物理内存空间每一个表是真正的页表一共由1M个页表一个页表4KB1K个页表就是4MB但一个应用程序不可能完全使用4GB空间也想只要几十个页表一个用户的代码段数据段栈段一共需要10MB需要3个页表即可每一个页表覆盖4MB内存1.2.4 页目录结构每一个页框由一个页表项指向1M个页表需要被管理所用到的表称为页目录表形成二级页表页目录表项存储的是页表的起始物理地址页目录的物理地址被CR3寄存器指向保存当前正在执行任务的页目录地址OS加载用户程序时为程序分配物理内存为用来保存程序页目录和页表分配物理内存1.2.5 两级页表的地址转换举例MMU的工作流程从CR3读取页目录基址虚拟地址高10位访问一级页表找到二级页表中间10位访问二级页表找到物理页框低12位是页内偏移量MMU是一种硬件电路速度快主要工作是进行内存管理地址转换只是业务之一MMU进行两次页表查询确定物理地址确认权限等问题后把物理地址发送到总线内存收到后读取相应地址数据并返回页表变为N级N次检索1次读写页表级数越多查询步骤越多CPU等待时间越长效率越低单级页表对连续内存要求高引入多级页表多级页表减少存储空间但是查询效率降低如何提高效率呢计算机科学中的所有问题都可以通过添加一个中间层来解决CPU和磁盘中间的内存硬件和软件之间的OSMMU引入快表而页表也因此被称作慢表快表也就是TLB缓存translation lookaside buffer学名转译后备缓冲器当CPU给MMU传虚拟地址后MMU先问TLB有没有如果有直接拿到物理地址发到总线给内存但TLB容量很小难免发生Cache MissMMU查询页表除了把地址发到总线传给内存也把映射关系写入页表刷新TLB为什么页表是10 10 12呢首先虚拟页和物理页框都是4KB1:1在虚拟转物理的时候没必要每一个字节都建立映射关系页与页框建立即可那页框的起始地址低12位为0没必要保存只需要高20位然而2^20 *44MB需要1K个连续页框来实现随机访问和刚开始连续虚拟地址映射到不连续物理地址背道而驰于是再拆分把1K个页表也不连续又催生出页目录对页表进行管理1.2.6 缺页异常CPU给MMU的虚拟地址TLB和页表都没有找到对应的物理页就是缺页异常Page Fault是由硬件中断触发的可以由软件逻辑纠正的错误。如果目标内存页在物理内存中没有对应的物理页或存在但无对应权限CPU无法获取数据CPU报告缺页错误CPU没有数据无法进行计算用户进程出现缺页中断进程从用户态切换到内核态把缺页中断交给内核的page fault handler处理缺页中断交给Page Fault Handler处理其根据缺页中断的不同类型进行不同处理Hard Page Fault也被称为Major Page Fault翻译为硬缺页错误/主要缺页错误物理内存中没有对应的物理页需要CPU打开磁盘设备读取到物理内存中再让MMU建立虚拟地址和物理地址的映射Soft Page Fault也被称为Minor Page Fault翻译为软缺页错误/次要缺页错误物理内存存在对应物理页可能是其它进程调入发出缺页异常的进行不知道此时MMU建立映射即可无需从磁盘读取写入内存一般出现在多进程共享内存区域Invalid Page Fault翻译为无效缺页错误比如进程访问的内存地址越界访问或者空指针解引用内核报segment fault错误中断进程直接挂回顾1.如何理解new和malloc2.如何理解写时拷贝3.申请内存再做什么4.如何区分是缺页还是越界5.线程资源划分本质是什么1.在堆区找未使用的空间建立vm_area_struct维护这块空间的起始位置和结束位置包括权限并把这个结构体对象放入mm_struct本质就是分配进行虚拟地址空间可以懒加载就是先不分配物理内存等到用的时候进行缺页中断再进行加载2.写时拷贝父进程创建子进程子进程拷贝父进程的页表当父或子进程写入时发现权限为只读MMU会进行缺页中断拷贝数据所在页重新建立映射关系修改页表权限位回到中断处进行写入3.分配进行虚拟地址空间建立vm_area_struct维护这块空间的起始位置和结束位置包括权限并把这个结构体对象放入mm_struct可以懒加载就是先不分配物理内存等到用的时候进行缺页中断再进行加载4.1页号合法性检查OS在处理中断或异常时首先检查触发事件的页号是否合法如果非法则为越界如果合法但页面不在内存则为缺页中断2内存映射检查OS检查触发事件的殉道者是否在当前进程的内存映射范围内如果在则为缺页中断否则为越界5.本质是划分虚拟地址空间从而划分进程资源给到不同的线程1.3 线程的优点创建/删除一个新线程的代价比创建一个新进程小得多创建线程在linux下时创建task_struct创建进程就要拷贝页表等与进程之间的切换相比线程之间的切换需要OS做的工作少很多最主要的区别是线程切换虚拟地址空间不变进程切换虚拟地址空间变了。两种上下文切换的处理通过OS内核完成内核切换过程伴随最显著的性能损坏是将寄存器中的内存切换出去另外一个隐藏的损耗是上下文的切换回扰乱处理器的缓存机制。一旦切换上下文处理器中所有已经缓存的内存地址一瞬间作废。还有一个显著的区别是当改变虚拟内存地址空间时处理的页表缓存TLB快表会全部被刷新导致内存的访问在一段时间内相当低效。线程切换不会出现这个问题以及硬件Cache其实寄存器还是小事因为大多数管理类寄存器比如CPU标志位CR类寄存器一般不会随着线程的切换而切换PC指针栈顶栈底指针等执行线程要切换但也就几个寄存器的事主要还是Cache因为CPU和硬盘的速度差别太大所以出现内存但是内存和CPU速度差别也是很大速度差也是数量级别的所以出现了CacheCPU寄存器和内存间的高速缓存硬件实现比内存快比CPU慢这样硬盘的数据可以预加载到内存内存的数据加载到CacheCPU直接和Cache打交道本质都是局部性原理每次进程切换都有一个Cache从冷到热的过程也就是每次进程切换要丢弃之前的Cache重新进行命中缓存每次CPU访问的数据根据一定的算法加载到Cache但是同一进程内的不同线程切换不需要丢弃Cache线程占用的资源比进程少能充分利用多处理器的可并行数量在等待慢速I/O操作结束的同时程序可执行其它的计算任务计算密集型应用为了能在多处理系统上系统将计算分到多个线程实现线程数CPU个数*每个CPU的核数如果CPU只有一个核创建10个线程但每个线程只能争抢这1个CPU也就是说本来所有的计算任务比如排序搜索等都是由一个CPU完成分成10个线程任务也都是由这1个CPU完成结果还要有线程切换的开销图什么所以对于计算密集型任务有多少核就建多少线程才能让多个线程并行执行I/O密集型应用为了提高性能将I/O操作重叠将计算分解到多个线程中实现比如同时下载多个应用程序但是问题是带宽不会一直增加随着线程的增多每个线程占据的带宽量会下降这样总体而言其实和单线程下载的I/O效率差不多所以这是理想情况因为带宽不会一直增加1.4 线程的缺点性能损失一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器如果计算型线程的数量比可用的处理器多可能会有较大的性能损失因为增加了额外的同步和调度开销而可用的资源不变健壮性降低编写多线程需要更全面深入的考虑在一个多线程程序里因时间分配上的细微偏差或者共享了不该共享的变量造成不良影响的可能性很大线程直接缺乏保护共享资源的机制缺乏访问控制进程是访问控制的基本粒度在一个线程中调用某些OS函数会对整个进程造成影响编程难度提高编写和调试一个多线程程序比单线程程度困难辩证着看吧其实一个进程内多个执行流任何一个执行流挂了整个进程也就是所有执行流都挂了所以要小心但是我们知道多进程要进行通信往往要费一番周折让多个进程看到同一份资源但是一个进程内的多个线程共享资源反而没有这样的麻烦所以如果说一个进程多执行流编程比单执行流麻烦这情有可原但是如果是多进程其实谁比谁麻烦并不明显可能这也和对于外文书籍的翻译有些原因可能翻译不是专业人士吧母啊1.5 线程异常单个线程出现除零/野指针导致线程崩溃线程所在进程崩溃因为线程是进程的执行分支线程出异常类似进程出异常触发信号机制终止进程进程终止进程内的所有心存随即退出1.6 线程用途合理地使用多线程能提高CPU密集型程序的执行效率提高IO密集型程序的用户体验比如一遍写代码一边下载开发工具2.Linux进程vs线程进程具有独立性线程共享地址空间共享进程资源2.1 进程和线程进程是资源分配的基本单位线程是调度的基本单位线程共享进程数据拥有属于自己的“私有”数据线程ID一组寄存器线程的上下文数据栈errno信号屏蔽字调度优先级栈是因为比如线程要传参肯定涉及到压栈弹栈寄存器是上下文因为线程是执行流不同线程的PC指针包括形成的临时变量不同2.2 线程共享进程资源同一地址空间因此Text SegmentData Segment都是共享的如果定义一个函数在各线程中都可以调用如果定义一个全局变量各线程都可以访问文件描述符表每种信号的处理方式SIG_IGNSIG_DFL或者自定义的信号处理函数当前工作目录用户id和组id进程和线程的关系如下图3.线程控制线程创建3.1 tid和lwp对于线程id和lwp下面可以看到tid是一个很大的数一个地址系统调用pthread_self获取所调用线程的id与此类似C语言封装文件描述符暴露给用户的是FILE3.2 代码验证3.2.1 共享资源全局变量m是主线程栈上的变量但是通过全局变量也就是指针p可以让新创建的线程看到主线程的变量m因为二者共享进程的虚拟地址空间下面的PrintName函数被该进程的所有线程共享也就是共享代码3.2.2 函数重入无论是上面的Routine还是PrintName都是多个执行流几乎同时访问一个函数都是函数重入问题3.2.3 线程异常进程中的任何一个线程出问题异常崩溃会导致进程中的所有线程崩溃进程挂掉因为并发导致的问题因为共享资源缺乏保护创建多个新线程有趣的是打印thread-10,10相同但是后面的tid不同说明不是同一个线程那为什么都是10呢其实是多个执行流几乎同时进入Routine函数被重入了name是共享资源缺乏对共享资源访问和修改的保护机制其它的类似当第二个线程调度的时候i已经是4了第三个线程调度i为8后来i为9后续调度7个新创建的线程都是10在新线程的开头加上分隔符从打印结果来看其实很乱比如一个线程被创建调度其实打印分隔符之后就下处理机了所以真实情况很难说那怎么解决呢可以动态申请因为每次申请的是不同的空间还比如多个执行流向显示器写入显示器也是共享资源也会存在并发问题下面明显6713所在线程的换行还没来得及打印就下处理机了各个线程的打印混杂在一起了3.2.4 传参可以传入类或者结构体对象的地址从而让线程执行任务如果是C语言可以使用函数指针进行回调3.2.5 线程终止从线程return主线程return相当于调用exit系统调用pthread_exit可以看到新创建的线程并没有一直阻塞pthread_exit结束了线程如果调用exit也可以终止线程但会终止线程所在进程使用pthread_cancel终止另外一个线程可以看到主线程终止了所有从线程从线程通过pthread_cancel终止主线程3.2.6 线程等待主线程必须要等待创建出来的从线程因为一方面要回收从线程的资源task_struct避免类似子进程中僵尸进程的问题必须另一方面主线程获取从线程的执行结果非必须void不是类型一方面没有偏移量char是1int是4但是void没有类型不知道要取多少字节linux下void是1字节但是没办法定义void类型的变量但是void*是指针32位下4B所以可以定义变量start_routine的返回值一方面可以通过return另一方面可以通过pthread_exit(void* retval)的参数返回如果从线程结束后间隔5s回收通过监控可以看到从线程先结束好像没有所谓类似僵尸进程的问题因为此时从线程的lwp已经消失了这个后续探讨对于从线程的返回值下面*retval就是线程的退出码为什么不考虑退出信号也就是异常分析的问题因为一旦异常导致线程退出线程所在进程就会退出根本不会存在主线程pthread_join从线程的场景所以退出信号交给进程的父进程处理而线程退出只需要通过退出码区分结果对还是不对也可以直接返回一个类对象从而获取更多信息返回值和参数可以是同一类型的指针如果线程被其它线程通过pthread_cancel取消则pthread_join等待该线程获取的返回值是-1综上调用pthread_join的函数挂起等待直到id为thread的线程终止thread线程的终止方法不同通过pthread_join得到的终止状态不同如果thread线程通过return返回value_ptr所指向的内存单元存放的是thread线程函数的返回值如果thread线程被别的线程调用pthread_cancel异常终止value_ptr指向单元存放的是常数PTHREAD_CANCELED如果thread线程自行调用pthread_exit终止value_ptr指向内存单元存放的是传给pthread_exit的参数如果不关心thread线程的终止状态传递NULL或nullptr给value_ptrPTHREAD_CANCELED也就是-13.2.7 线程分离线程可以调用exec系列的函数进行程序替换吗技术上可以但是因为多执行流会相互影响因为共享地址空间等一般会在线程内部通过fork创建子进程让子进程执行exec系列函数进程程序替换替换后的程序执行流的数目为1默认情况新创建的线程是joinable线程退出后需要对其进行pthread_join操作否则无法释放资源造成系统内存泄露但是如果不关系线程的返回值join是一种负担可以告诉他当线程退出时自动释放线程资源类似进程信号SIGCHLD改为SIG_IGN忽略退出的子进程由系统直接回收也就是分离线程因为等待线程是阻塞等待让线程结束时主线程不需要等待把目标线程设置为分离状态使用系统调用pthread_detach默认阻塞等待由主线程分离从线程线程是分离状态等待该线程就会出错从线程自己分离最佳实践一旦从进程被设置为分离主线程不能提前退甚至主线程本身是死循环但也没分离彻底即使从线程分离了出了异常整个进程还是挂4.线程地址空间4.1 概述pthread作为动态库映射到进程的虚拟地址空间以支持线程控制线程id是一个地址因为线程有多个所以肯定要进行管理先描述再组织linux内核只有轻量级进程lwp但是线程库有类似tcb的数据结构所谓的tcb也就是struct pthread每创建一个线程就在pthread库中为其分配struct pthread线程局部存储和线程栈而tid就是struct pthread的起始地址通过起始地址就可以找到线程的所有属性而所有进程内的所有线程都会在pthread库中struct pthread里存储的是线程属性也就是用户级线程struct pthread和struct task_struct是1:1的关系4.2 源码GLIBC-2.4/nptl/pthread_create.cstruct pthreadpthread_join就是通过tid也就是起始地址找到struct pthread找到其属性result线程属性也就是attrpthread_create.c内部的createthread.c看一下申请内存4.3 局部存储线程局部存储下面测试可以看到id加上__thread关键字之后主线程和从线程id值不会互相影响也就是在两个线程局部存储内部分别给id开辟了空间可以从地址看出来只能用来访问内置类型整型char或者类/结构体不行可以让不同线程使用同一变量名访问各自的局部存储也就是不同内存块下面是测试获取lwp5.C线程库具有很好的跨平台性C多线程本质在linux系统中C多线程操作是对pthread库的封装而为什么具有跨平台性技术角度所有平台的线程功能代码用C封装对外提供同一接口平台的背后是用户而语言追求跨平台本质是追求更多的用户因此C新特性支持通常以年为单位多线程中栈与堆的基本情况是A.多个线程共有一个栈各自有一个堆B.多个线程共有一个栈, 共有一个堆C.多个线程各自有一个栈共有一个堆D.多个线程各自有一个栈, 各自有一个堆C下面关于线程的叙述中正确的是A.不论是系统支持线程还是用户级线程其切换都需要内核的支持B.线程是资源的分配单位进程是调度和分配的单位C.不管系统中是否有线程进程都是拥有资源的独立单位D.在引入线程的系统中进程仍是资源分配和调度分派的基本单位C下面有关线程的说法错误的是多选A.每个线程有自己独立的地址空间B.耗时的操作使用线程提高应用程序响应C.多CPU系统中使用线程提高CPU利用率D.线程包含CPU现场可以独立执行程序ADA.线程没有独立的地址空间D.线程不能独立执行程序关于进程和线程下列说法正确的是多选A.线程是资源分配和拥有的单位B.线程和进程都可并发执行C.在linux系统中线程是处理器调度的基本单位D.线程的粒度小于进程占用资源更少因此通常多线程比多进程并发性更高E.不同的线程共享相同的栈空间BCD下述有关Linux进程和线程的描述正确的有多选A.在linux 中进程比线程安全的原因是进程之间不会共享数据B.进程有独立的地址空间线程没有单独的地址空间同一进程内的线程共享进程的地址空间C.进程——资源分配的最小单位线程——程序执行的最小单位D.进程和线程都有单独的地址空间BC进程和线程是操作系统中最基本的概念,下列有关描述错误的是A.进程是程序的一次执行而线程可以理解为程序中运行的一个片段B.由于线程没有独立的地址空间因此同一个进程的一组线程可以共享访问该进程大部分资源这些线程之间的通信也很高效C.线程之间的通信简单(共享地址空间和页表信息因此传参以及全局数据都可以实现通信)而不同进程之间的通信更为复杂通常需要调用内核实现D.线程有独立的虚拟地址空间但是拥有的资源相对进程来说只有运行所必须的栈寄存器等D关于多线程和多线程编程以下哪些说法正确的多选A.多进程之间的数据共享比多线程编程复杂B.多线程的创建切换销毁速度快于多进程C.对于大量的计算优先使用多进程D.多线程没有内存隔离单个线程崩溃会导致整个应用程序的退出ABD有关进程和线程的说法错误的是多选A.一个程序至少有一个进程一个进程至少有一个线程B.操作系统的最小调度单位是进程C.线程自己不拥有系统资源D.一个线程可以创建和撤销另一个线程AB关于多线程和多进程编程下面描述正确的是多选A.多进程里子进程可复制父进程的所有堆和栈的数据而线程会与同进程的其他线程共享数据但拥有自己的栈空间B.线程因为有自己的独立栈空间且共享数据所有执行的开销相对较大同时不利于资源管理和保护C.线程的通信速度更快切换更快因为他们在同一地址空间内且还共享了很多其他的进程资源比如页表指针这些是不需要切换的D.线程使用公共变量/内存时需要使用同步机制因为他们在同一地址空间内E.因多进程里每个子进程有自己的地址空间因此相互之间通信时线程不如进程灵活和方便ACD以下描述正确的有A.可以使用ps -l命令查看轻量级进程信息B.可以使用ps -L命令查看轻量级进程信息C.可以使用pthread_self接口获取轻量级进程IDD.可以使用getpid接口接口获取轻量级进程IDB以下描述正确的有多选A.pthread_create函数是一个库函数 代码当中如果使用该函数创建线程 则需要在编译的时候链接“libpthread.so”线程库B.哪个线程调用pthread_exit函数 哪个线程就退出。俗称“谁调用谁退出”C.在有多个线程的情况下主线程调用pthread_cancel(pthread_self()), 则主线程状态为Z 其他线程正常运行D.在有多个线程的情况下主线程从main函数的return返回或者调用pthread_exit函数则整个进程退出CD