进程和线程的CPU亲和性
linux进程、线程与cpu的亲和性(affinity)

linux进程、线程与cpu的亲和性(affinity)最近的⼯作中对性能的要求⽐较⾼,下⾯简单做⼀下总结:⼀、什么是cpu亲和性(affinity) CPU的亲和性,就是进程要在指定的 CPU 上尽量长时间地运⾏⽽不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将制定的进程或线程绑定到相应的cpu上;在多核运⾏的机器上,每个CPU本⾝⾃⼰会有缓存,缓存着进程使⽤的信息,⽽进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会⼀直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有⼀定的提⾼。
软亲和性(affinity): 就是进程要在指定的 CPU 上尽量长时间地运⾏⽽不被迁移到其他处理器,Linux 内核进程调度器天⽣就具有被称为软 CPU 亲和性(affinity)的特性,这意味着进程通常不会在处理器之间频繁迁移。
这种状态正是我们希望的,因为进程迁移的频率⼩就意味着产⽣的负载⼩。
硬亲和性(affinity):简单来说就是利⽤linux内核提供给⽤户的API,强⾏将进程或者线程绑定到某⼀个指定的cpu核运⾏。
解释:在linux内核中,所有的进程都有⼀个相关的数据结构,称为 task_struct。
这个结构⾮常重要,原因有很多;其中与亲和性(affinity)相关度最⾼的是 cpus_allowed 位掩码。
这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器⼀⼀对应。
具有 4 个物理 CPU 的系统可以有 4 位。
如果这些CPU 都启⽤了超线程,那么这个系统就有⼀个 8 位的位掩码。
如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运⾏。
因此,如果⼀个进程可以在任何 CPU 上运⾏,并且能够根据需要在处理器之间进⾏迁移,那么位掩码就全是 1。
实际上,这就是 Linux 中进程的缺省状态;(这部分内容在这个博客中有提到⼀点:) cpus_allowed⽤于控制进程可以在哪⾥处理器上运⾏sched_set_affinity()(⽤来修改位掩码)sched_get_affinity()(⽤来查看当前的位掩码)⼆、进程与cpu的绑定 sched_setaffinity可以将某个进程绑定到⼀个特定的CPU。
5、CPU的线程与操作系统的线程有何关系?操作系统中的进程和线程是什么关系?

5、CPU的线程与操作系统的线程有何关系?操作系统中的进程和线程是什么关系?CPU中的线程和操作系统(OS)中的线程即不同,在调度的时候⼜有些关联。
CPU中的线程,我们叫它们Thread,和OS中的线程的名字⼀样。
它来⾃同步多线程(SMT,Simultaneous Multi-threading)的概念。
我们现在在Intel的CPU上看到它,实际上这并不是Intel的发明创造。
它最早起源于学术圈,在硬件上IBM实现也⽐Intel早。
最早Intel使⽤了这种技术时候就叫做SMT,但后⾯改叫做HT (Hyper Threading),可能是这样更清楚(毕竟最多两个thread,⽐IBM怪物要少),更朗朗上⼝吧。
我们现在看到CPU,很多都⽀持HT,经常看到的2C4T的意思就是2核4线程(core,Thread)。
1个内核中的thread是对称的和对等的,在软件上没有任何区别,BIOS也只有通过⼀些特殊⼿段才能区分。
实际上,2C4T中的4个thread调度起来没有本质区别,它们都有⾃⼰单独的⾝份证号码:APIC ID。
调度起来只要知道别⼈的APIC ID,就⽤⾃⼰的Local APIC寄存器发出两个IPI(Inter-Processor Interrupts)就好了,那个被指明的倒霉蛋就莫名其妙的开始被调度去指定的地址执⾏指令了(尽管是实模式)。
当然也可以⼴播IPI让所有别的thread都去执⾏指定任务。
更多相关内容见:实际上CPU中Thead有多少,操作系统并不⾃⼰探测,是BIOS通过ACPI报告给OS的,那么BIOS是怎么知道有多少个Thread呢?就是通过⼴播IPI让各个thread⾃⼰来签到的,是不是很简单?操作系统中的ThreadOS中的Thread有⾃⼰的栈空间,和同⼀进程中的其他线程共享地址空间等等,这些基本知识因为⼴为⼈所知,这⾥就不罗嗦了。
此Thread⾮彼Thread操作系统中的进程可以很多,进程中的线程就更多了,常常有⼏⼗个上百个。
CPU调度——精选推荐

CPU调度CPU调度引⼊了线程,对于⽀持它们的操作系统,是内核级的线程被操作系统调度,⽽不是进程。
不过,术语线程调度或进程调度常常被交替使⽤。
在讨论普通调度概念时使⽤进程调度,特别指定为线程概念时使⽤线程调度。
基本概念CPU-I/O区间周期CPU的成功调度依赖于进程的如下属性:进程执⾏由CPU执⾏和I/O等待周期组成。
进程在这两个状态之间切换。
进程执⾏从CPU区间(CPU burst)开始,在这之后是I/O区间(I/O burst),接着是另⼀个CPU区间,然后是另⼀个I/O区间,如此进⾏下去。
CPU调度程序每当CPU空闲时,操作就必须从就绪队列中选择⼀个进程来执⾏。
进程选择由短期调度程序(short-term scheduler)或CPU调度程序执⾏。
调度程序从内核中选择⼀个能够执⾏的进程,并为之分配CPU。
就绪队列不必是先进先出(FIFO)队列。
就绪队列可实现为FIFO队列,优先队列,树或简单的⽆序链表。
不过从概念上来说,就绪队列内的所有进程都要排队以等待在CPU上运⾏。
队列中的记录通常为进程控制块(PCB)。
抢占调度CPU调度决策可在如下4种环境下发⽣:当⼀个进程从运⾏状态切换到等待状态(例如,I/O请求,或调⽤wait等待⼀个⼦进程的终⽌)。
当⼀个进程从运⾏状态切换到就绪状态(例如,当出现中断时)当⼀个进程从等待状态切换到就绪状态(例如,I/O完成)当⼀个进程终⽌对于第1和第4两种情况,没有选择⽽只有调度。
⼀个新进程(如果就绪队列中已有⼀个进程存在)必须被选择执⾏。
不过,对于第2和第3两种情况,可以进⾏选择。
当调度只能发⽣在第1和第4两种情况下时,称调度⽅案是⾮抢占的(nonpreemptive)的或协作的(cooperative);否则,称调度⽅案是抢占的(preemptive)。
采⽤⾮抢占调度,⼀旦CPU分配给⼀个进程,那么该进程会⼀直使⽤CPU知道进程终⽌或切换到等待状态。
中断能随时发⽣,⽽且不能总是被内核所忽视,所以受中断影响的代码段必须加以保护以避免同时访问。
taskset机理

taskset机理taskset机制是一种用于管理进程CPU亲和性的机制。
CPU亲和性是指将进程绑定到特定的CPU核心上,以提高系统性能和资源利用率。
本文将介绍taskset机制的原理、使用方法以及相关注意事项。
一、taskset机制的原理在多核系统中,操作系统会将进程分配到不同的CPU核心上执行。
然而,进程的切换和迁移会带来一定的开销,影响系统性能。
为了最大程度地减少进程切换和迁移的开销,可以使用taskset机制将进程绑定到特定的CPU核心上,使其始终在该核心上执行。
taskset机制的原理是通过设置进程的CPU亲和性掩码来实现的。
CPU亲和性掩码是一个位图,每一位代表一个CPU核心,进程可以绑定到对应位为1的核心上执行。
通过设置不同的掩码,可以实现进程在多个核心之间的切换和迁移。
二、taskset机制的使用方法在Linux系统中,可以使用taskset命令来设置进程的CPU亲和性。
其基本的使用方法如下:taskset -c <cpu_list> <command>其中,<cpu_list>表示要绑定的CPU核心列表,可以使用逗号分隔不同的核心编号,也可以使用连字符表示一个范围。
例如,0,2,4-7表示绑定0号、2号以及4到7号核心。
而<command>则表示要执行除了使用taskset命令外,还可以通过在程序中调用sched_setaffinity函数来设置进程的CPU亲和性。
这个函数的使用方法类似于taskset命令。
三、taskset机制的注意事项在使用taskset机制时,需要注意以下几点:1. 确定绑定的CPU核心数目。
过多的核心绑定可能导致负载不均衡,影响系统整体性能;而过少的核心绑定可能无法充分利用系统资源。
2. 避免绑定关键进程。
某些关键进程,如系统进程或关键服务进程,最好不要进行CPU绑定,以免影响系统的稳定性和可用性。
3. 考虑NUMA架构。
操作系统进程与线程的区别与联系

操作系统进程与线程的区别与联系在操作系统中,进程(Process)和线程(Thread)是两个重要的概念。
它们都代表了程序运行的基本单位,但在功能和使用等方面存在一些区别与联系。
本文将针对操作系统进程与线程的区别与联系展开讨论。
一、概念区别进程是指程序在执行过程中分配和管理资源的基本单位。
一个程序可以被看作一个进程,它包含了相关的代码、数据和运行时环境。
每个进程都有独立的内存空间、寄存器集合和执行状态。
进程间的切换是由操作系统负责调度和管理的。
线程是进程中的一个执行流,是指处理器执行的最小单位。
一个进程可以包含多个线程。
线程共享进程的地址空间和其他资源,包括代码段、数据段和打开的文件等。
由于线程共享资源,线程之间的切换更加轻量级。
二、功能区别1. 并行与并发:进程是操作系统进行资源分配和调度的基本单位,不同进程之间可以并行执行,即多个进程在不同的处理器上同时执行。
而线程是进程内的执行流,同一进程的多个线程可以并发执行,即多个线程在单个处理器上轮流执行。
2. 线程之间的通信:线程之间共享同一进程的资源,可以通过共享内存、全局变量等实现线程间的数据传递和通信。
而不同进程之间的通信通常需要使用进程间通信(IPC)机制,例如管道、信号量、消息队列等。
3. 系统开销:创建、切换和销毁进程所需的系统开销大于线程,因为进程间的切换需要保存和恢复更多的上下文信息。
线程切换相对轻量级,开销更小。
4. 容错性:由于进程间相互独立,一般情况下一个进程的崩溃不会影响其他进程的正常运行。
而线程共享进程的资源,一个线程的异常可能会导致整个进程的崩溃。
三、联系与互动进程和线程之间并不是完全独立的,它们存在联系与互动。
1. 进程可以包含多个线程,多线程可以提高程序的并发性和响应速度。
在多核处理器上,多线程可以实现真正的并行执行。
2. 线程在进程中共享相同的地址空间和资源,可以通过共享内存进行高效的数据共享和通信。
3. 进程和线程都需要操作系统进行管理和调度,分配资源,并确保它们按照正确的顺序执行。
CPU处理方式和亲和度

CPU处理方式和亲和度CPU处理方式和亲和度000一中断中断方式与轮询方式1 中断的基本概念程序中断通常简称中断,是指CPU在正常运行程序的过程中,由于预选安排或发生了各种随机的内部或外部事件,使CPU中断正在运行的程序,而转到为相应的服务程序去处理,这个过程称为程序中断。
80x86微处理器的中断 80x86微处理器的中断类型一般分为2类,即由于执行某些指令引起的软中断和由处理器以外其他控制电路发出中断请求信号引起的硬中断。
CPU要从主程序转入中断服务程序,必须知道该中断服务程序的入口地址,即中断向量。
80x86为CPU的PC机共有256个中断向量。
中断的一般过程:主程序只是在设备A,B,C数据准备就绪时,才去处理A,B ,C,进行数据交换。
在速度较慢的外围设备准备自己的数据时,CPU照常执行自己的主程序。
在这个意义上说,CPU和外围设备的一些操作是并行地进行的,因而同串行进行的程序查询方式相比,计算机系统的效率是大大提高了。
实际的中断过程还要复杂一些,下图示出了中断处理过程的详细流程图.当CPU执行完—条现行指令时,如果外设向CPU发出中断请求、那么CPU在满足响应条件的情况下,将发出中断响应信号,与此同时关闭中断(“中断屏蔽”触发器置“1”),表示CPU不再受理另外—个设备的中断。
这时、CPU将寻找中断请求源是哪个设备。
并保存CPU自己的程序计数器(Pc)的内容.然后,它将转移到处理该中断源的中断服务程序.CPU在保存现场信息,设备(如文换数据)以后.将恢复现场信息.在这些动作完成以后,开放中断(“中断屏蔽”触发器置‘o”),并返网到原来被中断的主程序的下一条指令。
(1) 尽管外界中断请求是随机的,但CPU只有在当前一条指令执行完毕后,即转入公操作时才受理设备的中断请求,这样才不致于使当前指令的执行受到干扰。
公操作是指一条指令执行结束后CPU所进行的操作,如中断处理、直接内存传送、取下条指令等。
CPU进程与线程的关系及区别

CPU进度与线程的关系和差异篇一:进度和线程的差异进度和线程的差异线程是指进度内的一个执行单元,也是进度内的可调换实体 .与进度的差异 :(1)地址空间 :进度内的一个执行单元 ;进度最少有一个线程 ;它们共享进度的地址空间 ;而进度有自己独立的地址空间 ;(2)资源拥有 :进度是资源分配和拥有的单位 ,同一个进度内的线程共享进度的资源(3)线程是办理器调换的基本单位,但进度不是 .4)二者均可并发执行 .进度和线程都是由操作系统所领悟的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进度和线程的差异在于:简而言之 ,一个程序最少有一个进度,一个进度最少有一个线程.线程的划分尺度小于进度,使得多线程程序的并发性高。
别的,进度在执行过程中拥有独立的内存单元,而多个线程共享内存,进而极大地提高了程序的运行效率。
线程在执行过程中与进度还是有区其他。
每个独立的线程有一个程序运行的入口、序次执行序列和程序的出口。
但是线程不能够够独立执行,必定依存在应用程序中,由应用程序供应多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分能够同时执行。
但操作系统并没有将多个线程看做多个独立的应用,来实现进度的调换和管理以及资源分配。
这就是进度和线程的重要差异。
进度是拥有必然独立功能的程序关于某个数据会集上的一次运行活动 ,进度是系统进行资源分配和调换的一个独立单位 .线程是进度的一个实体 ,是 CPU调换和分配的基本单位 ,它是比进度更小的能独立运行的基本单位 .线程自己基本上不拥有系统资源,只拥有一点在运行中必不能少的资源 (如程序计数器 ,一组寄存器和栈 ),但是它可与同属一个进度的其他的线程共享进度所拥有的全部资源.一个线程能够创办和撤掉另一个线程 ;同一个进度中的多个线程之间能够并发执行 .和"stdio.h " 差异#include"stdio.h "当要调用某个函数时先在用户自已编写的文件中查找,若是找不到再到库文件里去找,而#include 是直接到库文件里去找因此若是是调用自己写的函数的话就用 #include"stdio.h ",这种形式而调用标准库函数的话就用 #include 这种形式,能够提高速度篇二:进度线程差异与联系定义:一程序可是一组指令的有序会集二进度是拥有必然独立功能的程序关于某个数据会集上的一次运行活动,是系统进行资源分配和调换的一个独立单位;三线程是进度的一个实体,是 CPU 调换和分配的基本单位,它是比进度更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不能少的资源(如程序计数器 ,一组寄存器和栈 ),一个线程能够创办和撤掉另一个线程;一进度与线程差异与联系(1)划分尺度 :线程更小,因此多线程程序并发性更高;(2)资源分配:进度是资源分配的基本单位,同一进度内多个线程共享其资源;(3)地址空间:进度拥有独立的地址空间,同一进度内多个线程共享其资源;(4)办理器调换:线程是办理器调换的基本单位;(5)执行:每个线程都有一个程序运行的入口,序次执行序列和程序的出口,但线程不能够单独执行,必定组成进度,一个进度最少有一个主线程。
linux cgroup cpu实现原理

linux cgroup cpu实现原理Linux的cgroup(control group)是一种资源管理机制,它可以对进程或进程组进行资源限制和分配,其中包括CPU、内存、磁盘、网络等资源。
本文将重点介绍cgroup在CPU资源管理方面的实现原理。
在多任务操作系统中,CPU资源是有限的,为了实现公平和合理的资源分配,需要对CPU进行调度和限制。
cgroup通过将进程或进程组划分为不同的控制组,来实现对CPU资源的管理。
每个控制组都有自己的资源限制和优先级设置,从而达到对CPU资源的有效分配和控制。
cgroup的CPU资源管理主要通过两种方式实现:CPU时间片轮转调度和CPU带宽控制。
cgroup使用CPU时间片轮转调度算法来进行CPU资源的分配。
每个控制组根据其设置的优先级,按照时间片的方式依次获得CPU资源。
时间片长度可以根据实际需求进行调整,以实现不同控制组之间的资源比例分配。
当一个控制组的时间片用尽后,调度器会将CPU资源切换到下一个控制组,从而实现对CPU资源的公平分配。
cgroup还可以通过CPU带宽控制来限制一个控制组的CPU使用率。
CPU带宽控制是通过设置CPU的cfs_period_us和cfs_quota_us参数来实现的。
其中,cfs_period_us表示一个周期的长度,cfs_quota_us表示一个周期内该控制组被允许使用CPU的时间长度。
根据这两个参数的设置,可以计算出一个周期内该控制组的CPU使用率。
例如,如果cfs_period_us为100ms,cfs_quota_us为50ms,则该控制组的CPU使用率为50%。
通过设置不同的cfs_quota_us值,可以实现对不同控制组的CPU使用率进行限制。
当一个控制组的CPU使用时间超过其设置的cfs_quota_us值时,调度器会将其放入等待队列,直到下一个周期开始时才重新分配CPU资源。
除了以上两种方式,cgroup还支持其他一些CPU资源管理的特性,如CPU集合、CPU亲和性设置等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
进程和线程的亲缘性(affinity)是指可以将进程或者是线程强制限制在可用的CPU子集上运行的特性,它一定程度上把进程/线程在多处理器系统上的调度策略暴露给系统程序员。
CPU的数量和表示在有n个CPU的Linux上,CPU是用0...n-1来进行一一标识的。
CPU的数量可以通过proc文件系统下的CPU相关文件得到,如cpuinfo和stat:$ cat /proc/stat | grep "^cpu[0-9]\+" | wc -l8$ cat /proc/cpuinfo | grep "^processor" | wc -l8在系统编程中,可以直接调用库调用sysconf获得:sysconf(_SC_NPROCESSORS_ONLN);进程的亲缘性Linux操作系统在2.5.8引入了调度亲缘性相关的系统调用:int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);其中sched_setaffinity是设定进程号为pid的进程调度亲缘性为mask,也就是说它只能在mask中指定的CPU 之间进行调度执行;sched_getaffinity当然就是得到进程号为pid的进程调度亲缘性了。
如果pid为0,则操纵当前进程。
第二个参数指定mask所指空间的大小,通常为sizeof(cpu_set_t)。
第三个参数mask的类型为cpu_set_t,即CPU集合,GNU的c库(需要在include头文件之前定义__USE_GNU)还提供了操作它们的宏:void CPU_CLR(int cpu, cpu_set_t *set);int CPU_ISSET(int cpu, cpu_set_t *set);void CPU_SET(int cpu, cpu_set_t *set);void CPU_ZERO(cpu_set_t *set);如果我们所关心的只是CPU#0和CPU#1,想确保我们的进程只会运作在CPU#0之上,而不会运作在CPU#1之上。
下面程序代码可以完成此事:cpu_set_t set;int ret, i;CPU_ZERO(&set);CPU_SET(0, &set);CPU_CLR(1, &set);ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);if( ret == -1){perror("sched_se");}for( i=0; i < 3; i++){int cpu;cpu = CPU_ISSET(i, &set);printf("cpu = %i is %s/n", i, cpu? "set" : "unset");}Linux只提供了面向线程的调度亲缘性一种接口,这也是上面只提调度亲缘性而不直言进程亲缘性的原因。
当前Linux系统下广泛采用的线程库NPTL(Native Posix Thread Library)是基于线程组来实现的,同一个线程组中的线程对应于一组共享存储空间的轻量级进程,它们各自作为单独调度单位被内核的调度器在系统范围内调度,这种模型也就是我们通常所说的1-1线程模型。
正因如此,目前线程的调度范围(可以用函数pthread_attr_getscope和pthread_attr_setscope获取和设置)只能是系统级而不能是进程级。
c库的GNU扩展所提供的有关线程亲缘性的API如下:int pthread_attr_setaffinity_np (pthread_attr_t *__attr, size_t __cpusetsize, __const cpu_set_t *__cpuset);int pthread_attr_getaffinity_np (__const pthread_attr_t *__attr, size_t __cpusetsize, cpu_set_t *__cpuset);int pthread_setaffinity_np (pthread_t __th, size_t __cpusetsize, __const cpu_set_t *__cpuset);intpthread_getaffinity_np (pthread_t __th, size_t __cpusetsize, cpu_set_t *__cpuset);亲缘性的继承调度亲缘性是被fork出来的子进程所继承的,即使子进程通过exec系列函数更换了执行镜像。
因为Linux操作系统下进程和线程的创建都是通过系统调用clone来实现的,所以实际上调度亲缘性也是被用pthread_create创建的线程所继承的。
这意味着,如果主线程在创建其它线程之前设定亲缘性,那么它所设定的亲缘性将被继承,因为这时所有线程的亲缘性相同(假设之后没有任何线程私自设置亲缘性),我们就可以认为前面设置的是进程亲缘性,而不管它所调用的函数是sched_setaffinity还是pthread_setaffnity_np。
下面创建两个并发线程分别绑定在CPU0和CPU1上。
#define _GNU_SOURCE#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <pthread.h>#include <sys/time.h>#include <math.h>#include <time.h>#include <sched.h>#include <sys/types.h>int x1;int x2;double waste_time(long n){double res = 0;long i = 0;while (i <n * 500000) {i++;res += sqrt(i);}return res;}void* proc1(void*arg){cpu_set_t mask ;CPU_ZERO(&mask);CPU_SET(0,&mask);int ret = 0;ret = pthread_setaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask ); if(ret < 0){printf("pthread_setaffinity_np err \n");return ;}while(1){if(x1 > 900000000){break;}x1++;}waste_time(1);ret =pthread_getaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask ); if(ret < 0){printf("pthread_getaffinity_np err \n");return ;}int j;for( j = 0;j < CPU_SETSIZE;j++){if(CPU_ISSET(j,&mask))printf(" thread[%d] bind cpu[%d]\n",pthread_self(),j);}}void* proc2(void* arg){cpu_set_t mask ;CPU_ZERO(&mask);CPU_SET(2,&mask);int ret = 0;ret =pthread_setaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask ); if(ret < 0){printf("pthread_setaffinity_np err \n");return ;}while(1){if(x2 > 900000000){break;}x2++;}waste_time(1);ret = pthread_getaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );if(ret < 0){printf("pthread_getaffinity_np err \n");return ;}int j;for( j = 0;j < CPU_SETSIZE;j++){if(CPU_ISSET(j,&mask))printf(" thread[%d] bind cpu[%d]\n",pthread_self(),j);}}void main(){int ret;pthread_t t1,t2;struct timeval time1,time2;ret = gettimeofday(&time1,NULL);ret = pthread_create(&t1,NULL,proc1,NULL);ret = pthread_create(&t2,NULL,proc2,NULL);pthread_join(t1,NULL);pthread_join(t2,NULL);ret = gettimeofday(&time2,NULL);printf("time spend:[%d]s [%d]ms \n",_sec - _sec,(_usec - _usec)/1000); }。