Linux利用多核多线程进行程序优化

Linux利用多核多线程进行程序优化
Linux利用多核多线程进行程序优化

利用多核多线程进行程序优化

简介:大家也许还记得2005 年 3 月C++ 大师Herb Sutter 在Dr.Dobb’s Journal 上发表了一篇名为《免费的午餐已经结束》的文章。文章指出:现在的程序员对效率、伸缩性、吞吐量等一系列性能指标相当忽视,很多性能问题都仰仗越来越快的CPU 来解决。但CPU 的速度在不久的将来,即将偏离摩尔定律的轨迹,并达到一定的极限。所以,越来越多的应用程序将不得不直面性能问题,而解决这些问题的办法就是采用并发编程技术。

样例程序

程序功能:求从1一直到APPLE_MAX_VALUE (100000000)相加累计的和,并赋值给apple 的a和b;求orange 数据结构中的a[i]+b[i ] 的和,循

环ORANGE_MAX_VALUE(1000000)次。

说明:

1. 由于样例程序是从实际应用中抽象出来的模型,所以本文不会进行test.a=test.b=

test.b+sum、中间变量(查找表)等类似的优化。

2. 以下所有程序片断均为部分代码,完整代码请参看本文最下面的附件。

清单1. 样例程序

回页首K-Best 测量方法

在检测程序运行时间这个复杂问题上,将采用Randal E.Bryant和David R. O’Hallaron提出的K 次最优测量方法。假设重复的执行一个程序,并纪录K 次最快的时间,如果发现测量的误差ε 很小,那么用测量的最快值表示过程的真正执行时间,称这种方法为“ K 次最优(K-Best)方法”,要求设置三个参数:

K: 要求在某个接近最快值范围内的测量值数量。

ε 测量值必须多大程度的接近,即测量值按照升序标号V1, V2, V3, … , Vi, … ,同时必须满足(1+ ε)Vi >= Vk

M: 在结束测试之前,测量值的最大数量。

按照升序的方式维护一个K 个最快时间的数组,对于每一个新的测量值,如果比当前K 处的值更快,则用最新的值替换数组中的元素K ,然后再进行升序排序,持续不断的进行该过程,并满足误差标准,此时就称测量值已经收敛。如果M 次后,不能满足误差标准,则称为不能收敛。

在接下来的所有试验中,采用K=10,ε=2%,M=200 来获取程序运行时间,同时也对K 次最优测量方法进行了改进,不是采用最小值来表示程序执行的时间,而是采用K 次测量值的平均值来表示程序的真正运行时间。由于采用的误差ε 比较大,在所有试验程序的时间收集过程中,均能收敛,但也能说明问题。

为了可移植性,采用gettimeofday() 来获取系统时钟(system clock)时间,可以精确到微秒。

回页首测试环境

硬件:联想Dual-core 双核机器,主频2.4G,内存2G

软件:SuseLinunx Enterprise 10,内核版本:linux-2.6.16

回页首软件优化的三个层次

医生治病首先要望闻问切,然后才确定病因,最后再对症下药,如果胡乱医治一通,不死也残废。说起来大家都懂的道理,但在软件优化过程中,往往都喜欢犯这样的错误。不分青红皂白,一上来这里改改,那里改改,其结果往往不如人意。

一般将软件优化可分为三个层次:系统层面,应用层面及微架构层面。首先从宏观进行考虑,进行望闻问切,即系统层面的优化,把所有与程序相关的信息收集上来,确定病因。确定病因后,开始从微观上进行优化,即进行应用层面和微架构方面的优化。

1. 系统层面的优化:内存不够,CPU 速度过慢,系统中进程过多等

2. 应用层面的优化:算法优化、并行设计等

3. 微架构层面的优化:分支预测、数据结构优化、指令优化等

软件优化可以在应用开发的任一阶段进行,当然越早越好,这样以后的麻烦就会少很多。在实际应用程序中,采用最多的是应用层面的优化,也会采用微架构层面的优化。将某些优化和维护成本进行对比,往往选择的都是后者。如分支预测优化和指令优化,在大型应用程序中,往往采用的比较少,因为维护成本过高。

本文将从应用层面和微架构层面,对样例程序进行优化。对于应用层面的优化,将采用多线程和CPU 亲和力技术;在微架构层面,采用Cache 优化。

回页首并行设计

利用并行程序设计模型来设计应用程序,就必须把自己的思维从线性模型中拉出来,重新审视整个处理流程,从头到尾梳理一遍,将能够并行执行的部分识别出来。

可以将应用程序看成是众多相互依赖的任务的集合。将应用程序划分成多个独立的任务,并确定这些任务之间的相互依赖关系,这个过程被称为分解(Decomosition)。分解问题的方式主要有三种:任务分解、数据分解和数据流分解。关于这部分的详细资料,请参看参考资料一。

仔细分析样例程序,运用任务分解的方法,不难发现计算apple 的值和计算orange 的值,属于完全不相关的两个操作,因此可以并行。

改造后的两线程程序:

清单2. 两线程程序

更甚一步,通过数据分解的方法,还可以发现,计算apple 的值可以分解为两个线程,一个用于计算apple a的值,另外一个线程用于计算apple b的值(说明:本方案抽象于实际的应用程序)。但两个线程存在同时访问apple 的可能性,所以需要加锁访问该数据结构。改造后的三线程程序如下:

清单3. 三线程程序

这样改造后,真的能达到我们想要的效果吗?通过K-Best 测量方法,其结果让我们大失所望,如下图:

图1. 单线程与多线程耗时对比图

为什么多线程会比单线程更耗时呢?其原因就在于,线程启停以及线程上下文切换都会引起额外的开销,所以消耗的时间比单线程多。

为什么加锁后的三线程比两线程还慢呢?其原因也很简单,那把读写锁就是罪魁祸首。通过Thread Viewer 也可以印证刚才的结果,实际情况并不是并行执行,反而成了串行执行,如图2:

图2. 通过Viewer 观察三线程运行情况

其中最下面那个线程是主线程,一个是addx线程,另外一个是addy线程,从图中不难看出,其他两个线程为串行执行。

通过数据分解来划分多线程,还存在另外一种方式,一个线程计算从1

到APPLE_MAX_VALUE/2 的值,另外一个线程计算从

APPLE_MAX_VALUE/2+1到APPLE_MAX_VALUE 的值,但本文会弃用这种模型,有兴趣的读者可以试一试。

在采用多线程方法设计程序时,如果产生的额外开销大于线程的工作任务,就没有并行的必要。线程并不是越多越好,软件线程的数量尽量能与硬件线程的数量相匹配。最好根据实际的需要,通过不断的调优,来确定线程数量的最佳值。

回页首加锁与不加锁

针对加锁的三线程方案,由于两个线程访问的是apple 的不同元素,根本没有加锁的必要,所以修改apple 的数据结构(删除读写锁代码),通过不加锁来提高性能。

测试结果如下:

图3. 加锁与不加锁耗时对比图

其结果再一次大跌眼镜,可能有些人就会越来越糊涂了,怎么不加锁的效率反而更低呢?将在针对Cache 的优化一节中细细分析其具体原因。

在实际测试过程中,不加锁的三线程方案非常不稳定,有时所花费的时间相差4倍多。

要提高并行程序的性能,在设计时就需要在较少同步和较多同步之间寻求折中。同步太少会导致错误的结果,同步太多又会导致效率过低。尽量使用私有锁,降低锁的粒度。无锁设计既有优点也有缺点,无锁方案能充分提高效率,但使得设计更加复杂,维护操作困难,不得不借助其他机制来保证程序的正确性。

回页首针对Cache 的优化

在串行程序设计过程中,为了节约带宽或者存储空间,比较直接的方法,就是对数据结构做一些针对性的设计,将数据压缩(pack) 的更紧凑,减少数据的移动,以此来提高程序的性能。但在多核多线程程序中,这种方法往往有时会适得其反。

数据不仅在执行核和存储器之间移动,还会在执行核之间传输。根据数据相关性,其中有两种读写模式会涉及到数据的移动:写后读和写后写,因为这两种模式会引发数据的竞争,表面上是并行执行,但实际只能串行执行,进而影响到性能。

处理器交换的最小单元是cache 行,或称cache 块。在多核体系中,对于不共享cache 的架构来说,两个独立的cache 在需要读取同一cache 行时,会共享该cache 行,如果在其中一个cache 中,该cache 行被写入,而在另一个cache 中该cache 行被读取,那么即使读写的地址不相交,也需要在这两个cache 之间移动数据,这就被称为cache 伪共享,导致执行核必须在存储总线上来回传递这个cache 行,这种现象被称为“乒乓效应”。同样地,当两个线程写入同一个cache 的不同部分时,也会互相竞争该cache 行,也就是写后写的问题。上文曾提到,不加锁的方案反而比加锁的方案更慢,就是互相竞争cache 的原因。

在X86 机器上,某些处理器的一个cache 行是64字节,具体可以参看Intel 的参考手册。既然不加锁三线程方案的瓶颈在于cache,那么让apple 的两个成员a和b位于不同的cache 行中,效率会有所提高吗?

修改后的代码片断如下:

清单4. 针对Cache的优化

测量结果如下图所示:

图4. 增加Cache 时间耗时对比图

小小的一行代码,尽然带来了如此高的收益,不难看出,我们是用空间来换时间。当然读者也可以采用更简便的方法:__attribute__((__aligned__(L1_CACHE_BYTES))) 来确定cache 的大小。

如果对加锁三线程方案中的apple 数据结构也增加一行类似功能的代码,效率也是否会提升呢?性能不会有所提升,其原因是加锁的三线程方案效率低下的原因不是Cache 失效造成的,而是那把锁。

在多核和多线程程序设计过程中,要全盘考虑多个线程的访存需求,不要单独考虑一个线程的需求。在选择并行任务分解方法时,要综合考虑访存带宽和竞争问题,将不同处理器和不同线程使用的数据放在不同的Cache 行中,将只读数据和可写数据分离开。

回页首CPU 亲和力

CPU 亲和力可分为两大类:软亲和力和硬亲和力。

Linux 内核进程调度器天生就具有被称为CPU 软亲和力(affinity)的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。但不代表不会进行小范围的迁移。

CPU 硬亲和力是指进程固定在某个处理器上运行,而不是在不同的处理器之间进行频繁的迁移。这样不仅改善了程序的性能,还提高了程序的可靠性。

从以上不难看出,在某种程度上硬亲和力比软亲和力具有一定的优势。但在内核开发者不断的努力下,2.6内核软亲和力的缺陷已经比2.4的内核有了很大的改善。

在双核机器上,针对两线程的方案,如果将计算apple 的线程绑定到一个CPU 上,将计算orange 的线程绑定到另外一个CPU 上,效率是否会有所提高呢?

程序如下:

清单5. CPU 亲和力

测量结果为:

图5. 采用硬亲和力时间对比图(两线程)

其测量结果正是我们所希望的,但花费的时间还是比单线程的多,其原因与上面分析的类似。进一步分析不难发现,样例程序大部分时间都消耗在计算apple 上,如果将计算a和b的值,分布到不同的CPU 上进行计算,同时考虑Cache 的影响,效率是否也会有所提升呢?

图6. 采用硬亲和力时间对比图(三线程)

从时间上观察,设置亲和力的程序所花费的时间略高于采用Cache 的三线程方案。由于考虑了Cache 的影响,排除了一级缓存造成的瓶颈,多出的时间主要消耗在系统调用及内核上,可以通过time 命令来验证:

通过设置CPU 亲和力来利用多核特性,为提高应用程序性能提供了捷径。同时也是一把双刃剑,如果忽略负载均衡、数据竞争等因素,效率将大打折扣,甚至带来事倍功半的结果。

在进行具体的设计过程中,需要设计良好的数据结构和算法,使其适合于应用的数据移动和处理器的性能特性。

回页首总结

根据以上分析及实验,对所有改进方案的测试时间做一个综合对比,如下图所示:

图7. 各方案时间对比图

单线程原始程序平均耗时:1.049046s,最慢的不加锁三线程方案平均耗时:2.217413s,最快的三线程( Cache 为128)平均耗时:0.826674s,效率提升约26%。当然,还可以进一步优化,让效率得到更高的提升。

从上图不难得出结论:采用多核多线程并行设计方案,能有效提高性能,但如果考虑不全面,如忽略带宽、数据竞争及数据同步不当等因素,效率反而降低,程序执行越来越慢。

如果抛开本文开篇时的限制,采用上文曾提到的另外一种数据分解模型,同时结合硬亲和力对样例程序进行优化,测试时间为0.54s,效率提升了92%。

软件优化是一个贯穿整个软件开发周期,从开始设计到最终完成一直进行的连续过程。在优化前,需要找出瓶颈和热点所在。正如最伟大的C 语言大师Rob Pike 所说:

如果你无法断定程序会在什么地方耗费运行时间,瓶颈经常出现在意想不到的地方,所以别急于胡乱找个地方改代码,除非你已经证实那儿就是瓶颈所在。

将这句话送给所有的优化人员,和大家共勉。

回页首参考资料

?请参考书籍《多核程序设计技术》,了解更多关于多线程设计的理念

?请参考书籍《软件优化技术》,了解更多关于软件优化的技术

?请参考书籍《UNIX编程艺术》,了解更多关于软件架构方面的知识

?参考文章《CPU Affinity》,了解更多关于CPU亲和力的信息

?参考文章《管理处理器的亲和性(affinity)》,了解更多关于CPU亲和力的信息

实验七:Linux多线程编程(实验分析报告)

实验七:Linux多线程编程(实验报告)

————————————————————————————————作者:————————————————————————————————日期:

实验七:Linux多线程编程(4课时) 实验目的:掌握线程的概念;熟悉Linux下线程程序编译的过程;掌握多线程程序编写方法。 实验原理:为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。 1 多线程概念 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间。 使用多线程的理由之二是线程间方便的通信机制。同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。2多线程编程函数 Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义: typedef unsigned long int pthread_t; 它是一个线程的标识符。 函数pthread_create用来创建一个线程,它的原型为: extern int pthread_create((pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)); 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。 函数pthread_join用来等待一个线程的结束。函数原型为: extern int pthread_join(pthread_t th, void **thread_return); 第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。 函数pthread_exit的函数原型为: extern void pthread_exit(void *retval); 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。 3 修改线程的属性 线程属性结构为pthread_attr_t,它在头文件/usr/include/pthread.h中定义。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。 设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。 另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。 4 线程的数据处理

LINUX 内核的几种锁介绍

spinlock(自旋锁)、 mutex(互斥量)、 semaphore(信号量)、 critical section(临界区) 的作用与区别 Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。 Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。 Binary semaphore与Mutex的差异: 在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。而semaphore可以由其它进程释放(这时的semaphore实际就是个原子的变量,大家可以加或减),因此semaphore 可以用于进程间同步。Semaphore的同步功能是所有系统都支持的,而Mutex能否由其他进程释放则未定,因此建议mutex只用于保护critical section。而semaphore则用于保护某变量,或者同步。 另一个概念是spin lock,这是一个内核态概念。spin lock与semaphore的主要区别是spin lock是busy waiting,而semaphore是sleep。对于可以sleep 的进程来说,busy waiting当然没有意义。对于单CPU的系统,busy waiting 当然更没意义(没有CPU可以释放锁)。因此,只有多CPU的内核态非进程空间,

Linux多线程编程的基本的函数

Posix线程编程指南(一) 线程创建与取消 这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第一篇将向您讲述线程的创建与取消。 线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。 1.2 创建线程 POSIX通过pthread_create()函数创建线程,API定义如下: 与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运行 start_routine(arg)函数。thread返回创建的线程ID,而attr是创建线程时设置的线程属性(见下)。pthread_create()的返回值表示线程创建是否成功。尽管arg是void *类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,start_routine()可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由pthread_join()获取。 1.3 线程创建属性 pthread_create()中的attr参数是一个结构指针,结构中的元素分别对应着新线程的运行属性,主要包括以下几项: __detachstate,表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为 PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。

Linux中直接IO机制的介绍

Linux 中直接 I/O 机制的介绍https://www.360docs.net/doc/e511707720.html,/developerworks/cn/linux/l-cn-...

https://www.360docs.net/doc/e511707720.html,/developerworks/cn/linux/l-cn-...

当应用程序需要直接访问文件而不经过操作系统页高速缓冲存储器的时候,它打开文件的时候需要指定 O_DIRECT 标识符。 操作系统内核中处理 open() 系统调用的内核函数是 sys_open(),sys_open() 会调用 do_sys_open() 去处理主要的打开操作。它主要做了三件事情:首先,它调用 getname() 从进程地址空间中读取文件的路径名;接着,do_sys_open() 调用 get_unused_fd() 从进程的文件表中找到一个空闲的文件表指针,相应的新文件描述符就存放在本地变量 fd 中;之后,函数 do_?lp_open() 会根据传入的参数去执行相应的打开操作。清单 1 列出了操作系统内核中处理 open() 系统调用的一个主要函数关系图。 清单 1. 主要调用函数关系图 sys_open() |-----do_sys_open() |---------getname() |---------get_unused_fd() |---------do_filp_open() |--------nameidata_to_filp() |----------__dentry_open() 函数 do_?ip_open() 在执行的过程中会调用函数 nameidata_to_?lp(),而 nameidata_to_?lp() 最终会调用 __dentry_open()函数,若进程指定了 O_DIRECT 标识符,则该函数会检查直接 I./O 操作是否可以作用于该文件。清单 2 列出了 __dentry_open()函数中与直接 I/O 操作相关的代码。 清单 2. 函数 dentry_open() 中与直接 I/O 相关的代码 if (f->f_flags & O_DIRECT) { if (!f->f_mapping->a_ops || ((!f->f_mapping->a_ops->direct_IO) && (!f->f_mapping->a_ops->get_xip_page))) { fput(f); f = ERR_PTR(-EINVAL); } } 当文件打开时指定了 O_DIRECT 标识符,那么操作系统就会知道接下来对文件的读或者写操作都是要使用直接 I/O 方式的。 下边我们来看一下当进程通过 read() 系统调用读取一个已经设置了 O_DIRECT 标识符的文件的时候,系统都做了哪些处理。函数read() 的原型如下所示: ssize_t read(int feledes, void *buff, size_t nbytes) ; 操作系统中处理 read() 函数的入口函数是 sys_read(),其主要的调用函数关系图如下清单 3 所示: 清单 3. 主调用函数关系图 sys_read() |-----vfs_read() |----generic_file_read() |----generic_file_aio_read() |--------- generic_file_direct_IO()

实验四 Linux进程互斥

实验四 Linux进程互斥 一、实验目的 熟悉Linux下信号量机制,能够使用信号量实现在并发进程间的互斥和同步。 二、实验题目 使用共享存储区机制,使多个并发进程分别模拟生产者-消费者模式同步关系、临界资源的互斥访问关系,使用信号量机制实现相应的同步和互斥。 三、背景材料 (一)需要用到的系统调用 实验可能需要用到的主要系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。 fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行; wait() 等待子进程执行完成; shmget() 建立一个共享存储区; shmctl() 操纵一个共享存储区; s hmat() 把一个共享存储区附接到进程内存空间; shmdt() 把一个已经附接的共享存储区从进程内存空间断开; semget() 建立一个信号量集; semctl() 操纵一个信号量集,包括赋初值; semop() 对信号量集进行wait和signal操作; signal() 设置对信号的处理方式或处理过程。 (二)模拟生产者-消费者的示例程序 本示例主要体现进程间的直接制约关系,由于使用共享存储区,也存在间接制约关系。进程分为服务进程和客户进程,服务进程只有一个,作为消费者,在每次客户进程改变共享存储区内容时显示其数值。各客户进程作为生产者,如果共享存储区内容已经显示(被消费),可以接收用户从键盘输入的整数,放在共享存储区。 编译后执行,第一个进程实例将作为服务进程,提示: ACT CONSUMER!!! To end, try Ctrl+C or use kill. 服务进程一直循环执行,直到用户按Ctrl+C终止执行,或使用kill命令杀死服务进程。 其他进程实例作为客户进程,提示: Act as producer. To end, input 0 when prompted. 客户进程一直循环执行,直到用户输入0。 示例程序代码如下: #include #include #include #include #include #include #include #include

linux下的多线程编程常用函数

Linux下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特 有的系统调用,他的使用方式类似fork. int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg); 返回值:若是成功建立线程返回0,否则返回错误的编号 形式参数: pthread_t *restrict tidp 要创建的线程的线程id指针 const pthread_attr_t *restrict attr 创建线程时的线程属性 void* (start_rtn)(void) 返回值是void类型的指针函数 void *restrict arg start_rtn的行参 进行编译的时候要加上-lpthread 向线程传递参数。 例程2: 功能:向新的线程传递整形值 #include #include #include void *create(void *arg) { int *num; num=(int *)arg; printf("create parameter is %d \n",*num); return (void *)0; } int main(int argc ,char *argv[]) { pthread_t tidp; int error; int test=4; int *attr=&test; error=pthread_create(&tidp,NULL,create,(void *)attr); if(error) { printf("pthread_create is created is not created ... \n"); return -1; } sleep(1); printf("pthread_create is created ...\n");

linux通讯

线程+定时实现linux下的Qt串口编程 2010-06-26 10:49 转: 线程+定时实现linux下的Qt串口编程 作者:lizzy115 时间:2010,5,14 说明:本设计采用的是线程+定时实现linux下的Qt串口编程,而非网上资料非常多的Qt编写串口通信程序全程图文讲解系列,因为Qt编写串口通信程序全程图文讲解系列是很好实现,那只是在windows下面的,可是在linux 下面实现串口的通信并非如此,原因在于QextSerialBase::EventDriven跟QextSerialBase::Polling这两个事件的区别,EventDriven属于异步,Polling 属于同步,在windows下面使用的是EventDriven很容易实现,只要有数据就会触发一个串口事件,网上说linux下面需要的是Polling,可是还是不行的,只要串口有数据的时候他会在QByteArray temp = myCom->readAll(); 这句一直读取数据,没能退出,直到断掉串口的时候才能把接受到的串口数据通过 ui->textBrowser->insertPlainText(temp);打印在界面上,一直没能解决这个问题,所以只好采用线程+定时实现linux下的Qt串口编程进行设计。 一、安装环境: 系统平台:Ubuntu-8.04,内核2.6.24-27-generic,图形界面 二、软件需求及下地地址: Qt版本 qt-linux-SDK-4.6.2 注意:此处使用的是qt-linux-SDK-4.6.2版本,编译通过了,之后需要把他移植到qt-embedded-linux-opensource-src-4.5.3.tar.gz,通过qte编译后移植到开发板中,采用的测试开发板为Micro2440, 下载地址:略 三、程序编写过程 程序编程流程: 先新建一个工程空白工程,再建立Ui文件,通过designer进行Ui 界面设计,设计完保存,编译生成ui_mainwindow.h头文件,编写线程头文件及线程处理.cpp文件,建立串口处理头文件及 .cpp文件,最后完成main.cpp 文件。

操作系统课内实验大纲(2014)

操作系统原理课内实验大纲(2014版) 实验一:用户接口实验 实验目的 1)理解面向操作命令的接口Shell。 2)学会简单的shell编码。 3)理解操作系统调用的运行机制。 4)掌握创建系统调用的方法。 操作系统给用户提供了命令接口和程序接口(系统调用)两种操作方式。用户接口实验也因此而分为两大部分。首先要熟悉Linux的基本操作命令,并在此基础上学会简单的shell 编程方法。然后通过想Linux内核添加一个自己设计的系统调用,来理解系统调用的实现方法和运行机制。在本次实验中,最具有吸引力的地方是:通过内核编译,将一组源代码变成操作系统的内核,并由此重新引导系统,这对我们初步了解操作系统的生成过程极为有利。 实验内容 1)控制台命令接口实验 该实验是通过“几种操作系统的控制台命令”、“终端处理程序”、“命令解释程序”和“Linux操作系统的bash”来让实验者理解面向操作命令的接口shell和进行简单的shell 编程。 查看bash版本。 编写bash脚本,统计/my目录下c语言文件的个数 2)系统调用实验 该实验是通过实验者对“Linux操作系统的系统调用机制”的进一步了解来理解操作系统调用的运行机制;同时通过“自己创建一个系统调用mycall()”和“编程调用自己创建的系统调用”进一步掌握创建和调用系统调用的方法。 编程调用一个系统调用fork(),观察结果。

编程调用创建的系统调用foo(),观察结果。 自己创建一个系统调用mycall(),实现功能:显示字符串到屏幕上。 编程调用自己创建的系统调用。 实验要求 1)按照实验内容,认真完成各项实验,并完成实验报告。 2)实验报告必须包括:程序清单(含注释)、实验结果、实验中出现的问题、观察到 的现象的解释和说明,以及实验体会。

Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。 线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。 Hello World(线程创建、结束、等待) 创建线程 pthread_create 线程创建函数包含四个变量,分别为: 1. 一个线程变量名,被创建线程的标识 2. 线程的属性指针,缺省为NULL即可 3. 被创建线程的程序代码 4. 程序代码的参数 For example: - pthread_t thrd1? - pthread_attr_t attr? - void thread_function(void argument)? - char *some_argument? pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument); 结束线程 pthread_exit 线程结束调用实例:pthread_exit(void *retval); //retval用于存放线程结束的退出状态 线程等待 pthread_join pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:pthread_t th //th是要等待结束的线程的标识 void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。 调用实例:pthread_join(thrd1, NULL); example1: 1 /************************************************************************* 2 > F i l e N a m e: t h r e a d_h e l l o_w o r l d.c 3 > A u t h o r: c o u l d t t(f y b y) 4 > M a i l: f u y u n b i y i@g m a i l.c o m 5 > C r e a t e d T i m e: 2013年12月14日 星期六 11时48分50秒 6 ************************************************************************/ 7 8 #i n c l u d e 9 #i n c l u d e 10 #i n c l u d e

11 12 v o i d p r i n t_m e s s a g e_f u n c t i o n (v o i d *p t r)? 13 14 i n t m a i n() 15 { 16 i n t t m p1, t m p2?

linux线程

关于linux线程 在许多经典的操作系统教科书中, 总是把进程定义为程序的执行实例, 它并不执行什么, 只是维护应用程序所需的各种资源. 而线程则是真正的执行实体.为了让进程完成一定的工作, 进程必须至少包含一个线程. 如图1. 进程所维护的是程序所包含的资源(静态资源), 如: 地址空间, 打开的文件句柄集, 文件系统状态, 信号处理handler, 等; 线程所维护的运行相关的资源(动态资源), 如: 运行栈, 调度相关的控制信息, 待处理的信号集, 等; 然而, 一直以来, linux内核并没有线程的概念. 每一个执行实体都是一个task_struct结构, 通常称之为进程. 如图2. 进程是一个执行单元, 维护着执行相关的动态资源. 同时, 它又引用着程序所需的静态资源.通过系统调用clone创建子进程时, 可以有选择性地让子进程共享父进程所引用的资源. 这样的子进程通常称为轻量级进程.linux上的线程就是基于轻量级进程, 由用户态的pthread库实现的.使用pthread以后, 在用户看来, 每一个task_struct就对应一个线程, 而一组线程以及它们所共同引用的一组资源就是一个进程.但是, 一组线程并不仅仅是引用同一组资源就够了, 它们还必须被视为一个整体.对此, POSIX标准提出了如下要求: 1, 查看进程列表的时候, 相关的一组task_struct应当被展现为列表中的一个节点; 2, 发送给这个"进程"的信号(对应kill系统调用), 将被对应的这一组task_struct所共享, 并且被其中的任意一个"线程"处理; 3, 发送给某个"线程"的信号(对应pthread_kill), 将只被对应的一个task_struct接收, 并且由它自己来处理; 4, 当"进程"被停止或继续时(对应SIGSTOP/SIGCONT信号), 对应的这一组task_struct 状态将改变; 5, 当"进程"收到一个致命信号(比如由于段错误收到SIGSEGV信号), 对应的这一组task_struct将全部退出; 6, 等等(以上可能不够全); linuxthreads

跟我学Linux编程-13-多线程编程-性能

多线程编程-性能 在上一章节中,我们介绍了线程互斥锁的使用。通过互斥锁,使得每个线程只能串行地运行临界区代码,从而有效地避免了多线程冲突。串行的代码运行方式削弱了多线程并发运行的特性,因此线程锁也潜在地降低了程序性能,如何杜绝线程冲突,又尽可能不影响程序效率,是我们每一个线程从员需要认真考虑的事情。 减少线程性能下降的方法有如下几点: 1 尽可能减小互斥锁的颗粒,使串行代码的比例减小从而提高效率,这在上一章节末已提过。 2 加锁解锁之间的代码,运行时间尽可能减少,避免有sleep或死循环一类的超时等待。 3 对不同的冲突资源使用不同的线程锁,避免不相关的线线之间因锁反而产生关联。 4 使用pthread_mutex_trylock代替pthread_lock在检测和处理锁冲突,实现单线程对多个对像的非阻塞处理。 今天我们重点介绍pthread_mutex_trylock的使用,并通过实例的来展现其用法,探究其提高程序效果的原理。 首先,我们来看pthread_lock与pthread_mutex_trylock的函数原型: int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock( pthread_mutex_t *mutex); 可以看到,这两个函数的原型非常想像,功能也比较类似,都是尝试对给定的锁对像进行加锁,如果成功,则线程获得该锁,并返回0;不同点在于当锁已被其他线程占有的情况下,pthread_mutex_lock会阻塞,直至锁被其它线程释放并且本线程获得锁;如pthread_mutex_trylock则不然,如果锁当前已被其他线程占有,则立刻返回失败(非0值),我们的程序可判读返回值并进行下一步的处理(如处理另一个任务),避免线程被阻塞,从而提高了线程并发度,提升程序性能。我们接下来看例子: #include #include #include typedef struct { int m_cnt[3]; pthread_mutex_t m_mutex; } count_t; #define COUNT_CNT 20 #define CYC_CNT 10000 count_t g_counts[COUNT_CNT]; void *thread_task1(void *arg)

linux驱动工程师面试题整理

1、字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件。 评:这只是其中一种方式,也叫手动创建设备文件。还有UDEV/MDEV自动创建设备文件的方式,UDEV/MDEV是运行在用户态的程序,可以动态管理设备文件,包括创建和删除设备文件,运行在用户态意味着系统要运行之后。那么在系统启动期间还有devfs创建了设备文件。一共有三种方式可以创建设备文件。 2、写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?答:中断处理例程应该尽量短,把能放在后半段(tasklet,等待队列等)的任务尽量放在后半段。 评:写一个中断服务程序要注意快进快出,在中断服务程序里面尽量快速采集信息,包括硬件信息,然后推出中断,要做其它事情可以使用工作队列或者tasklet方式。也就是中断上半部和下半部。 第二:中断服务程序中不能有阻塞操作。为什么?大家可以讨论。 第三:中断服务程序注意返回值,要用操作系统定义的宏做为返回值,而不是自己定义的OK,FAIL之类的。 3、自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么? 答:使用自旋锁的进程不能睡眠,使用信号量的进程可以睡眠。中断服务例程中的互斥使用的是自旋锁,原因是在中断处理例程中,硬中断是关闭的,这样会丢失可能到来的中断。 4、原子操作你怎么理解?为了实现一个互斥,自己定义一个变量作为标记来作为一个资源只有一个使用者行不行? 答:原子操作指的是无法被打断的操作。我没懂第二句是什么意思,自己定义一个变量怎么可能标记资源的使用情况?其他进程又看不见这个变量 评:第二句话的意思是: 定义一个变量,比如int flag =0; if(flag == 0) { flag = 1; 操作临界区; flag = 0;

LINUX内核经典面试题.docx

LINUX内核经典面试题2015-05-02 23:08:14 分类:LIMX 原文地址:LINUX内核经典面试题作者:sunjiangang-ok 1)Linux中主要有哪儿种内核锁? 2)Linux屮的用户模式和内核模式是什么含意? 3)怎样申请大块内核内存? 4)用户进程间通信主要哪儿种方式? 5)通过伙伴系统中请内核内存的函数有哪些? 6)通过slab分配器巾请内核内存的函数有? 7)Linux的内核空间和用户空间是如何划分的(以32位系统为例)? 8)vmalloc()中请的内存有什么特点? 9)用户程序使用malloc()中请到的内存空间在什么范围? 10)在支持并使能MMU的系统中,Linux内核和用户程序分别运行在物理地址模式还是虚拟地址模式? 11)ARM处理器是通过儿级也表进行存储空间映射的? 12)Linux是通过什么组件来实现支持多种文件系通的? 13)Linux虚拟文件系统的关键数据结构有哪些?(至少写出四个) 14)对文件或设备的操作函数保存在那个数据结构中? 15)Linux中的文件包括哪些? 16)创建进程的系统调用有那些? 17)调用schedule()ffl行进程切换的方式有儿种? 18)Linux调度程序是根据进程的动态优先级还是静态优先级来调度进程的? 19)进程调度的核心数据结构是哪个? 20)如何加载、卸载一个模块? 21)模块和应用程序分别运行在什么空间? 22)Linux中的浮点运算由应用程序实现还是内核实现? 23)模块程序能否使用可链接的库函数? 24)TLB中缓存的是什么内容? 25)Linux中有哪儿种设备? 26)字符设备駆动程序的关键数据结构是哪个? 27)设备驱动程序包扌舌哪些功能函数? 28)如何唯一标识一个设备? 29)Linux通过什么方式实现系统调用? 30)Linux软中断和工作队列的作用是什么? 1. Linux中主要有哪几种内核锁?

跟我学Linux编程-12-多线程编程-同步

多线程编程-同步 在上一章节中,我们通过程序示例,见证了单线程世界中不可能发生的事件(一个数既是奇数又是偶数)在多线程环境中是怎样分分钟发生的,我通过细分程序执行步骤,分析了奇异事件发生的过程,并探明了其原因:一个线程在对全局变量gcnt进行两次判读的过程中,另一个线刚好改变了这个变量的值。在多线程编程术语中,称这两个线程同时进入了临界区域。 所谓临界区域,是指多线程环境下两个及以上线程同时执行可能会导致冲突的一段代码。在上一章节的示例中,这几行代码就是一个临界区域: gcnt++; if (gcnt % 2) { if (!(gcnt % 2)) printf("[%d] : %d\n", id, gcnt); } 冲突之所以会发生,是因为临界区域的代码,通常需要很多个CPU指令周期才能完成,其运行过程随时可能被打断(进行了线程调试),CPU去运行另外的线程,如果这个线程刚好也进入了临界区域,则异常的程序状态极可能会发生。 如果当某个线程进入临界区域,在其退出区域之前,其他的线程无论如何也不能进入该区域,那么冲突就不会发生。Linux提供了这种保证多线程进入临界区域互斥的机制,这正是本章节所要介绍的内容:线程锁。 我们今天的示例程序还是在上一章节的示例上改进而来的,我们的任务就是使用线程锁,保证“一个数既是奇数又是偶数”的奇异事件在多线程环境下也不发生,代码如下: #include #include #include int gcnt = 0; pthread_mutex_t g_mutex; void *thread_task(void *arg) { int id = (int)arg; while (1) { pthread_mutex_lock(&g_mutex); gcnt++; if (gcnt % 2)

Linux进程同步

Linux进程同步 1.概述 Linux系统同一时间可能有多个进程在执行,因此需要一些同步机制来同步各个进程对于共享资源的访问。在Linux内核中有相应的同步技术实现,包括原子操作、信号量、读写信号量、自旋锁和等待队列等等。本文从同步的机制出发,重点研究讨论了Linux系统非阻塞的同步机制、Linux系统内核同步机制、Linux系统多线程的同步机制。 我们在实际生活中经常碰到这样的一类问题:有时候我们在使用打印机实现某种功能的时候,有可能使多个任务的打印结果交织在一起,造成混乱。这时多任务之间的同步操作便显得非常重要。比如在工业控制中的多个相互合作的任务,可以将数据采集、数据处理和数据输出划分为不同的任务。在它们之间建立一种必要的通信机制,使得采集任务可以通知数据处理任务。在新采集的数据已经处理完毕的时候,数据处理任务可以及时的通知输出任务,告诉输出任务需要实现的结果已经经过计算完成,需要通过输出设备输出。多任务的引入主要有以下有点:多任务的引入改善了系统的资源利用率,并且提高了系统的吞吐量,尤其是在嵌入式多任务操作系统中,但是与此同时多任务的引入也带来了另外的问题,那就是多个任务间如何协调、合作共同完成一个大的系统功能。特别是当我们在竞争使用临界资源,或是需要相互通知某些事件发生时。 另外在Linux操作系统里,在某一个时间段里,有些资源可能被很多内核同时来调用,这时我们便需要一套同步机制来同步各内核执行单元对共享数据的访问。尤其是在多核通信的机制上更需要同步机制来协调进程之间的通信。在Linux内核中有相应的技术实现,主要包括原子操作、信号量、读写信号量、自旋锁和等待队列。利用常用的同步机制可以有效地实现多任务、多内核之间的优化,实现其之间的合理调度。 2.同步机制 2.1临界资源与临界区 我们把在一段时间内只允许一个任务访问的资源叫做临界资源。这里与vxworks中的信号量的使用比较的类似。任务在占有CPU之后,还需要相应的资源才可以正常的执行。如果此时任务需要的资源被其它任务所占有,那么当前任务必须等待前一个任务完成并释放该资源后,才可以执行。把程序中使用临界资源的代码称为临界区。如果此刻临界资源未能访问,该任务便可以进入临界区,并将其设置为被访问状态。然后,即可以对临界资源进行操作。待任务完成后释放其占用的资源。

linux

Linux Linux 简介 Linux内核最初只是由芬兰人李纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。 Linux能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。 Linux的发行版 Linux的发行版说简单点就是将Linux内核与应用软件做一个打包。 目前市面上较知名的发行版有:Ubuntu、RedHat、CentOS、Debain、Fedora、SuSE、OpenSUSE、TurboLinux、BluePoint、RedFlag、Xterm、SlackWare等。 Linux应用领域 今天各种场合都有使用各种Linux发行版,从嵌入式设备到超级计算机,并且在服务器领域确定了地位,通常服务器使用LAMP(Linux + Apache + MySQL + PHP)或LNMP(Linux + Nginx+ MySQL + PHP)组合。 目前Linux不仅在家庭与企业中使用,并且在政府中也很受欢迎。 巴西联邦政府由于支持Linux而世界闻名。 有新闻报道俄罗斯军队自己制造的Linux发布版的,做为G.H.ost项目已经取得成果. 印度的Kerala联邦计划在向全联邦的高中推广使用Linux。 中华人民共和国为取得技术独立,在龙芯过程中排他性地使用Linux。 在西班牙的一些地区开发了自己的Linux发布版,并且在政府与教育领域广泛使用,如Extremadura地区的gnuLinEx和Andalusia地区的Guadalinex。 葡萄牙同样使用自己的Linux发布版Caixa Mágica,用于Magalh?es笔记本电脑和e-escola政府软件。 法国和德国同样开始逐步采用Linux。 Linux vs Window 目前国内Linux更多的是应用于服务器上,而桌面操作系统更多使用的是Window。主要区别如下: 比较 Windows Linux 界面界面统一,外壳程序固定所有Windows程序菜单几乎一致, 快捷键也几乎相同 图形界面风格依发布版不同而不同,可能互不兼容。GNU/Linux的终端机是从 UNIX传承下来,基本命令和操作方法也几乎一致。 驱动程序驱动程序丰富,版本更新频繁。默认安装程序里面一般包 含有该版本发布时流行的硬件驱动程序,之后所出的新硬 件驱动依赖于硬件厂商提供。对于一些老硬件,如果没有 了原配的驱动有时很难支持。另外,有时硬件厂商未提供 所需版本的Windows下的驱动,也会比较头痛。 由志愿者开发,由Linux核心开发小组发布,很多硬件厂商基于版权考虑并未提 供驱动程序,尽管多数无需手动安装,但是涉及安装则相对复杂,使得新用户面 对驱动程序问题(是否存在和安装方法)会一筹莫展。但是在开源开发模式下, 许多老硬件尽管在Windows下很难支持的也容易找到驱动。HP、Intel、AMD等硬 件厂商逐步不同程度支持开源驱动,问题正在得到缓解。 使用使用比较简单,容易入门。图形化界面对没有计算机背景 知识的用户使用十分有利。 图形界面使用简单,容易入 门。 文字界面,需要学习才能掌握。 学习系统构造复杂、变化频繁,且知识、技能淘汰快,深入学 习困难。 系统构造简单、稳定,且知识、技能传承性好,深入学习相对容易。 软件每一种特定功能可能都需要商业软件的支持,需要购买相 应的授权。 大部分软件都可以自由获取,同样功能的软件选择较少。 Linux 安装 略 Linux 系统启动过程linux启动时我们会看到许多启动信息。

linux内核编程规范

竭诚为您提供优质文档/双击可除 linux内核编程规范 篇一:00-linux系统编程常识-王保明 linux系统编程-应用编程常识--专题讲座 writtenby王保明 计算机系统组成 1计算机系统硬件组成 2操作系统 篇二:linux2.6驱动开发系列教程 [置顶]linux2.6驱动开发系列教程 这段时间一直在做android下的驱动,android驱动底层跟linux如出一辙,所以这里准备做一个专题,把linux 驱动做一个总结,为android接下来的驱动开发打好基础,大致的思想如下: 一、linux驱动基础开发 0、 1、 2、 3、

4、linux驱动基础开发4——linux字符驱动模型(memdriver内存读写) 5、linux驱动基础开发5——linux设备文件注册(devfs、mdev、sys、proc)讲解 6、linux驱动基础开发6——linuxgpio驱动实例分析(s3c2440/6410io操作) 7、linux驱动基础开发7——linux1*3io键盘驱动实例分析 8、linux驱动基础开发8——linux中断机制讲解与实 例分析(s3c2440/6410外部中断机制) 9、linux驱动基础开发9——linux数据缓冲机制(kfifo)讲解与实例分析 10、linux驱动基础开发10——linux并发、同步、互 斥机制(信号量、互斥锁、等待任务队列)讲解与实例分析 11、linux驱动基础开发11——linux周期性事件(内 核定时器)讲解 12、linux驱动基础开发12——linux周期性事件(内 核线程)讲解 13、linux驱动基础开发13——linux任务阻塞 (select\poll)机制讲解 14、linux驱动基础开发14——linux异常处理(内核 信号)讲解

linux多线程编程

2.终止线程 (2) 3. 等待线程终止 (2) pthread_exit和pthread_join进一步说明: (3) 4.分离线程 (7) 5.获取线程标识符 (8) 6.比较线程ID (8) 7.一次性初始化 (8) 8. 设置线程的调度策略和优先级 (9) 9. 获取线程的优先级 (11) 10.取消线程 (12) 取消线程,是否会释放线程的所有资源?例子: (14) 设置取消类型 (16) 11.初始化属性 (17) 12.设置分离状态 (18) 13.设置范围 (18) 14. 设置继承的调度策略 (18) 16. 设置调度参数 (19) 17.初始化互斥锁 (21) 18.销毁互斥锁 (21) 19.锁定互斥锁 (22) 20.解除锁定互斥锁 (23) 21. 互斥锁的类型: (23) 22. 初始化互斥锁属性对象 (23) 23. 销毁互斥锁属性对象 (23) 24.设置互斥锁类型的属性 (24) 互斥锁动态初始化和静态初始化区别: (26) 销毁互斥锁:事实上没做任何销毁操作,如下: (27) 非递归类型的互斥锁解锁和加锁操作: (27) 29.初始化条件变量 (27) 30.基于条件变量阻塞 (27) 31.解除阻塞一个线程 (28) 31.解除阻塞所有线程 (29) 33. 在指定的时间之前阻塞 (30) 32.唤醒丢失问题 (31) 33. 计数信号量概述 (31) 34. 初始化信号 (31) 35. 增加信号 (31) 36. 基于信号计数进行阻塞 (32) 37.多线程链表添加删除例子(使用条件变量实现互斥): (32) 38.为线程特定数据创建键 (34) 39. 删除线程特定数据键 (35) 40.设置线程特定数据 (35) 41. 获取线程特定数据 (35)

相关文档
最新文档