版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 学号:课 程 设 计 课程名称操作系统学 院计算机科学与技术学院专 业软件工程专业班 级中国好学长系列姓 名小灰灰的爸爸指导教师刘 军 2013?7>2014学年 第1学期目录课程设计任务书1摘要21设计题目与要求21.1设计题目:内核定时器21.2设计要求:通过研究内核的时间管理算法,学习内核源代码;然后应用这些知识并且使用“信号”建立一种用户空间机制来测量一个多线程程序的执行时间。22 总的设计思想及系统平台、语言、工具22.1 设计思想:22.1.1 Linux内核对定时器的描述22.1.2 Linux 内核定时器42.1.3 Linux 信号signal处理机制72.1.4多线
2、程编程82.1.5内核定时器机制的实现102.2 系统平台:132.3 编程工具:133.数据结构与模块说明(功能与流程图)133.1 定时器使用:133.2 多线程程序:143.3程序流程图:154. 源程序:155.运行结果与运行情况166.调试记录:177.自我评析和总结:188.参考文献18评分表19课程设计任务书学生姓名:专业班级:指导教师: 刘军 工作单位: 计算机科学与技术学院 题目: 内核定时器初始条件: 操作系统:Linux 程序设计语言:C语言 3.有界缓冲区内设有20个储存单元,其初值为0,放入/取出的数据项按增序设定为1-20这20个整型数要求完成的主要任务: (包括课
3、程设计工作量及其技术要求,以及说明书撰写等具体要求) 1.技术要求: 通过研究内核的时间管理算法学习内核源代码。然后应用这些知识并且使用“信号”建立一种用户空间机制来测量一个多线程程序的执行时间。 实验条件要求:每人一台Linux主机且有超级用户权限。 2. 设计说明书内容要求: 1设计题目与要求 2总的设计思想及系统平台、语言、工具等 3数据结构与模块说明(功能与流程图) 4运行结果与运行情况 3. 调试报告: 1调试记录 2自我评析和总结时间安排: 序号阶段内容所需时间1消化资料、系统设计1天2编程、调试3天3撰写报告1天合计5天指导教师签名: 2013年12月26日系主任(或责任教师)签
4、名: 年月日内核定时器摘要 每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。内核时间指明线程执行操作系统代码已经经过了多少个100ns的CPU时间,linux是一个具有保护模式的操作系统。它一直工作在i386 cpu的保护模式之下。内存被分为两个单元: 内核区域和用户区域。一般地,在使用虚拟内存技术的多任务系统上,内核和应用有不同的地址空间,因此,在内核和应用之间以及在应用与应用之间进行数据交换需要专门的机制来实现,本文站在用户空间的角度,测试一个多线程程序的程序执行时间
5、。当一个进程希望获得信号量时, 如果信号量已经被占有, 则该进程将会被放到等待队列上sleep直到cpu将其唤醒。相对于spinlock来说开销太大,适用于长时间占有的lock。不可用于中断状态,因为它拥有信号量的进程可以sleep, 可以被抢占。1设计题目与要求1.1设计题目:内核定时器1.2设计要求:通过研究内核的时间管理算法,学习内核源代码;然后应用这些知识并且使用“信号”建立一种用户空间机制来测量一个多线程程序的执行时间。2 总的设计思想及系统平台、语言、工具2.1设计思想:时器:struct timer_list struct list_head list;unsigned long
6、 expires;unsigned long data;void *functionunsigned long; 各数据成员的含义如下:(1)双向链表元素list:用来将多个定时器连接成一条双向循环队列。(2)expires:指定定时器到期的时间,这个时间被表示成自系统启动以来的时钟滴答计数(也即时钟节拍数)。当一个定时器的expires值小于或等于jiffies变量时,我们就说这个定时器已经超时或到期了。在初始化一个定时器后,通常把它的expires域设置成当前expires变量的当前值加上某个时间间隔值(以时钟滴答次数计)。(3)函数指针function:指向一个可执行函数。当定时器到期时
7、,内核就执行function所指定的函数。而data域则被内核用作function函数的调用参数。内核函数init_timer用来初始化一个定时器。实际上,这个初始化函数仅仅将结构中的list成员初始化为空。如下所示(include/linux/timer.h):static inline void init_timerstruct timer_list * timertimer-list.next timer-list.prev NULL; 由于定时器通常被连接在一个双向循环队列中等待执行(此时我们说定时器处于pending状态)。因此函数time_pending就可以用list成员是否为空
8、来判断一个定时器是否处于pending状态。如下所示(include/linux/timer.h):static inline int timer_pending const struct timer_list * timerreturn timer-list.next ! NULL; 时间比较操作在定时器应用中经常需要比较两个时间值,以确定timer是否超时,所以Linux内核在timer.h头文件中定义了4个时间关系比较操作宏。这里我们说时刻a在时刻b之后,就意味着时间值ab。Linux强烈推荐用户使用它所定义的下列4个时间比较操作宏(include/linux/timer.h):#def
9、ine time_aftera,b longb - longa 0#define time_beforea,b time_afterb,a #define time_after_eqa,b longa - longb 0#define time_before_eqa,b time_after_eqb,a2.1.2Linux 内核定时器 定时器是管理内核时间的基础,用来计算流逝的时间,它以某种频率(节拍率)自行触发时钟中断,当时钟中断发生时,内核就通过一种特殊中断处理程序对其进行处理。但是原来的实现只能是time_t mytime形式的,经过简单的localtimemytime和ctime&am
10、p;mytime处理.精度是不够的,为了返回高精度的时间,这里使用了gettimeofday函数。这个syscall用来供用户获取timeval格式的当前时间信息(精确度为微秒级),以及系统的当前时区信息(timezone)。结构类型timeval的指针参数tv指向接受时间信息的用户空间缓冲区,参数tz是一个timezone结构类型的指针,指向接收时区信息的用户空间缓冲区。这两个参数均为输出参数,返回值0表示成功,返回负值表示出错。函数sys_gettimeofday的源码如下(kernel/time.c): asmlinkage long sys_gettimeofdaystruct tim
11、eval *tv, struct timezone *tz if tv struct timeval ktv; do_gettimeofday&ktv; if copy_to_usertv, &ktv, sizeofktv return -EFAULT; if tz if copy_to_usertz, &sys_tz, sizeofsys_tz return -EFAULT; return 0; 显然,函数的实现主要分成两个大的方面: (1)如果tv指针有效,则说明用户要以timeval格式来检索系统当前时间。为此,先调用do_gettimeofday函数来检索系统当
12、前时间并保存到局部变量ktv中。然后再调用copy_to_user()宏将保存在内核空间中的当前时间信息拷贝到由参数指针tv所指向的用户空间缓冲区中。 (2)如果tz指针有效,则说明用户要检索当前时区信息,因此调用copy_to_user宏将全局变量sys_tz中的时区信息拷贝到参数指针tz所指向的用户空间缓冲区中。 (3)最后,返回0表示成功。 函数do_gettimeofday的源码如下(arch/i386/kernel/time.c): /* * This version of gettimeofday has microsecond resolution * and better th
13、an microsecond precision on fast x86 machines with TSC*/ void do_gettimeofdaystruct timeval *tv unsigned long flags; unsigned long usec, sec; read_lock_irqsave&xtime_lock, flags; usec do_gettimeoffset; unsigned long lost jiffies - wall_jiffies; if lost usec + lost * 1000000 / HZ; sec xtime.tv_se
14、c; usec + xtime.tv_usec; read_unlock_irqrestore&xtime_lock, flags; while usec 1000000 usec - 1000000; sec+; tv-tv_sec sec; tv-tv_usec usec; 该函数的完成实际的当前时间检索工作。由于gettimeofday系统调用要求时间精度要达到微秒级,因此do_gettimeofday函数不能简单地返回xtime中的值即可,而必须精确地确定自从时钟驱动的Bottom Half上一次更新xtime的那个时刻到do_gettimeofday函数的当前执行时刻之间的具
15、体时间间隔长度,以便精确地修正xtime的值.假定被do_gettimeofday用来修正xtime的时间间隔为fixed_usec,而从wall_jiffies到jiffies之间的时间间隔是lost_usec,而从jiffies到do_gettimeofday函数的执行时刻的时间间隔是offset_usec。则下列三个等式成立: fixed_usec=(lost_usec+offset_usec) lost_usec=(jiffies-wall_jiffies)*TICK_SIZE=(jiffies-wall_jiffies)*(1000000/HZ) 由于全局变量last_tsc_low
16、表示上一次时钟中断服务函数timer_interrupt执行时刻的CPU TSC寄存器的值,因此我们可以用X86 CPU的TSC寄存器来计算offset_usec的值。也即: offset_usecdelay_at_last_interrupt+(current_tsc_low-last_tsc_low)*fast_gettimeoffset_quotient 其中,delay_at_last_interrupt是从上一次发生时钟中断到timer_interrupt服务函数真正执行时刻之间的时间延迟间隔。每一次timer_interrupt被执行时都会计算这一间隔,并利用TSC的当前值更新la
17、st_tsc_low变量(可以参见7.4节)。假定current_tsc_low是do_gettimeofday函数执行时刻TSC的当前值,全局变量fast_gettimeoffset_quotient则表示TSC寄存器每增加1所代表的时间间隔值,它是由time_init函数所计算的。 根据上述原理分析,do_gettimeofday函数的执行步骤如下: (1)调用函数do_gettimeoffset计算从上一次时钟中断发生到执行do_gettimeofday函数的当前时刻之间的时间间隔offset_usec。 (2)通过wall_jiffies和jiffies计算lost_usec的值。 (
18、3)然后,令secxtime.tv_sec,usecxtime.tv_usec+lost_usec+offset_usec。显然,sec表示系统当前时间在秒数量级上的值,而usec表示系统当前时间在微秒量级上的值。 (4)用一个while循环来判断usec是否已经溢出而超过106us=1秒。如果溢出,则将usec减去106us并相应地将sec增加1,直到usec不溢出为止。 (5)最后,用sec和usec分别更新参数指针所指向的timeval结构变量。至此,整个查询过程结束。函数do_gettimeoffset根据CPU是否配置有TSC寄存器这一条件分别有不同的实现。其定义如下(arch/i3
19、86/kernel/time.c): #ifndef CONFIG_X86_TSC static unsigned long do_slow_gettimeoffsetvoid static unsigned long *do_gettimeoffsetvoid do_slow_gettimeoffset; #else #define do_gettimeoffset do_fast_gettimeoffset #endif 显然,在配置有TSC寄存器的i386平台上,do_gettimeoffset()函数实际上就是do_fast_gettimeoffset函数。它通过TSC寄存器来计算do
20、_fast_gettimeoffset函数被执行的时刻到上一次时钟中断发生时的时间间隔值。其源码如下(arch/i386/kernel/time.c): static inline unsigned long do_fast_gettimeoffsetvoid register unsigned long eax, edx; /* Read the Time Stamp Counter */ rdtsceax,edx; /* relative to previous jiffy 32 bits is enough */ eax - last_tsc_low; /* tsc_low delta
21、*/ /* * Time offset tsc_low delta * fast_gettimeoffset_quotient * tsc_low delta * usecs_per_clock * tsc_low delta * usecs_per_jiffy / clocks_per_jiffy * * Using a mull instead of a divl saves up to 31 clock cycles * in the critical path*/ _asm_"mull %2" :"a" eax, "d" ed
22、x :"rm" fast_gettimeoffset_quotient, "0" eax; /* our adjusted time offset in microseconds */ return delay_at_last_interrupt + edx; 对该函数的注释如下: (1)先调用rdtsc函数读取当前时刻TSC寄存器的值,并将其高32位保存在edx局部变量中,低32位保存在局部变量eax中。 (2)让局部变量eax=tsc_low=eax-last_tsc_low;也即计算当前时刻的TSC值与上一次时钟中断服务函数timer_interr
23、upt执行时的TSC值之间的差值。 (3)显然,从上一次timer_interrupt到当前时刻的时间间隔就是(tsc_low*fast_gettimeoffset_quotient)。因此用一条mul指令来计算这个乘法表达式的值。 (4)返回值delay_at_last_interrupt+(tsc_low*fast_gettimeoffset_quotient)就是从上一次时钟中断发生时到当前时刻之间的时间偏移间隔值。2.1.3Linux 信号signal处理机制 信号signal机制是进程之间相互传递消息的一种方法,全称为软中断信号。系统调用signal用来设定某个信号的处理方法,其调用
24、声明的格式如下: void *signalint signum, void *handlerintint; 成功则返回该信号以前的处理配置,出错则返回SIG_ERR。在使用该调用的进程中加入以下头文件:几个常见信号:SIGINT: 当用户按某些终端键时, 引发终端产生的信号. 如Ctrl+C键, 这将产生中断信号SIGINT,它将停止一个已失去控制的程序。SIGSEGV: 由硬件异常除数为0, 无效的内存引用等等产生的信号。这些条件通常由硬件检测到, 并将其通知内核,然后内核为该条件发生时正在运行的进程产生该信号。SIGURG: 在网络连接上传来带外数据时产生。SIGPIPE: 在管道的读进程
25、已终止后, 一个进程写此管道时产生,当类型为SOCK_STREAM的socket已不再连接时, 进程写到该socket也产生此信号。SIGALRM: 进程所设置的闹钟时钟超时的时候产生。SIGABRT: 进程调用abort函数时产生此信号, 进程异常终止。SIGCHLD: 在一个进程终止或停止时, 它将把该信号发送给其父进程。 按系统默认, 将忽略此信号,如果父进程希望被告知其子进程的这种状态改变, 则应该捕捉此信号。通常是用wait系列函数捕捉, 如果不wait的话, 子进程将成为一个僵尸进程。SIGIO: 此信号指示一个异步I/O事件。SIGSYS: 该信号指示一个无效的系统调用。SIGT
26、STP: 交互式停止信号. Ctrl+Z, 按下时, 终端将产生此信号, 进程被挂起。 多线程是计算机同时运行多个执行线程的能力(这些线程可以是同一程序的组成部分,或者也可以是完全不同的程序)。Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。而Linux下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似fork。下面展示多线程程序部分050119.c。/* 050119.c */#include #i
27、nclude void threadvoidint i;fori0;i3;i+printf"This is a pthread.n" int pthread voidpthread_t id;int i,ret;retpthread_create&id,NULL,void * thread,NULL;ifret!0printf "Create pthread error!n"exit 1;fori0;i3;i+printf"This is the main process.n"pthread_joinid,NULL;retur
28、n 0;我们编译此程序:运行050119.out,我们得到如下结果:This is the main process.This is a pthread.This is the main process.This is the main process.This is a pthread.This is a pthread.再次运行,我们可能得到如下结果:This is a pthread.This is the main process.This is a pthread.This is the main process.This is a pthread.This is the main
29、process.前后两次结果不一样,这是两个线程争夺CPU资源的结果。上面的示例中,我们使用到了两个函数,pthread_create和pthread_join,并声明了一个pthread_t型的变量。pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:typedef unsigned long int pthread_t;它是一个线程的标识符。函数pthread_create用来创建一个线程,它的原型为:extern int pthread_create _P pthread_t *_thread, _const pthread_attr_t *
30、_attr,void *_start_routine void *, void *_arg;第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函 数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节 阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,
31、新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。函数pthread_join用来等待一个线程的结束。函数原型为:extern int pthread_join _P pthread_t _th, void *_thread_return;第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将 一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的 线程也就结束了;另一种方式是通过函数pthr
32、ead_exit来实现。它的函数原型为:extern void pthread_exit _P void *_retval _attribute_ _noreturn_;唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线 程则返回错误代码ESRCH。2.1.5.1动态定时器机制的初始化函数init_timervecs实现对动态定时器机制的初始化。该函数仅被sched_init
33、初始化例程所调用。动态定时器机制初始化过程的主要任务就是将tv1、tv2、tv5这5个结构变量中的定时器向量指针数组vec初始化为NULL。如下所示(kernel/timer.c):void init_timervecs voidint i; for i 0; i TVN_SIZE; i+ INIT_LIST_HEADtv5.vec + i;INIT_LIST_HEADtv4.vec + i;INIT_LIST_HEADtv3.vec + i;INIT_LIST_HEADtv2.vec + i;for i 0; i TVR_SIZE; i+INIT_LIST_HEADtv1.vec + i;
34、上述函数中的宏TVN_SIZE是指timer_vec结构类型中的定时器向量指针数组vec的大小,值为64。宏TVR_SIZE是指timer_vec_root结构类型中的定时器向量数组vec的大小,值为256。2.1.5.2将一个定时器插入到链表中 函数add_timer用来将参数timer指针所指向的定时器插入到一个合适的定时器链表中。它首先调用timer_pending函数判断所指定的定时器是否已经位于在某个定时器向量中等待执行。如果是,则不进行任何操作,只是打印一条内核告警信息就返回了;如果不是,则调用internal_add_timer函数完成实际的插入操作。其源码如下(kernel/t
35、imer.c):void add_timerstruct timer_list *timerunsigned long flags; spin_lock_irqsave&timerlist_lock, flags;if timer_pendingtimergoto bug;internal_add_timertimer;spin_unlock_irqrestore&timerlist_lock, flags;return;bug:spin_unlock_irqrestore&timerlist_lock, flags;printk"bug: kernel ti
36、mer added twice at %p.n",_builtin_return_address0; 函数internal_add_timer用于将一个不处于任何定时器向量中的定时器插入到它应该所处的定时器向量中去(根据定时器的expires值来决定)。如下所示(kernel/timer.c):static inline void internal_add_timerstruct timer_list *timer/* must be cli-ed when calling this*/unsigned long expires timer-expires;unsigned long
37、 idx expires - timer_jiffies;struct list_head * vec; if idx TVR_SIZE int i expires & TVR_MASK;vec tv1.vec + i; else if idx 1 TVR_BITS + TVN_BITS int i expires TVR_BITS & TVN_MASK;vec tv2.vec + i; else if idx 1 TVR_BITS + 2 * TVN_BITS int i expires TVR_BITS + TVN_BITS & TVN_MASK;vec tv3.v
38、ec + i; else if idx 1 TVR_BITS + 3 * TVN_BITS int i expires TVR_BITS + 2 * TVN_BITS & TVN_MASK;vec tv4.vec + i; else if signed long idx 0 /* can happen if you add a timer with expires jiffies,* or you set a timer to go off in the past*/vec tv1.vec + tv1.index; else if idx 0xffffffffUL int i expi
39、res TVR_BITS + 3 * TVN_BITS & TVN_MASK;vec tv5.vec + i; else /* Can only get here on architectures with 64-bit jiffies */ INIT_LIST_HEAD&timer-list;return;/* Timers are FIFO!*/list_add&timer-list, vec-prev; 对该函数的注释如下:(1)首先,计算定时器的expires值与timer_jiffies的插值(注意!这里应该使用动态定时器自己的时间基准),这个差值就表示这个定
40、时器相对于上一次运行定时器机制的那个时刻还需要多长时间间隔才到期。局部变量idx保存这个差值。(2)根据idx的值确定这个定时器应被插入到哪一个定时器向量中。其具体的确定方法我们在7.6.2节已经说过了,这里不再详述。最后,定时器向量的头部指针vec表示这个定时器应该所处的定时器向量链表头部。(3)最后,调用list_add函数将定时器插入到vec指针所指向的定时器队列的尾部。2.1.5.3修改一个定时器的expires值当一个定时器已经被插入到内核动态定时器链表中后,我们还可以修改该定时器的expires值。函数mod_timer实现这一点。如下所示(kernel/timer.c):int
41、mod_timerstruct timer_list *timer, unsigned long expires int ret;unsigned long flags; spin_lock_irqsave&timerlist_lock, flags;timer-expires expires;ret detach_timertimer;internal_add_timertimer;spin_unlock_irqrestore&timerlist_lock, flags;return ret; 该函数首先根据参数expires值更新定时器的expires成员。然后调用deta
42、ch_timer函数将该定时器从它原来所属的链表中删除。最后调用internal_add_timer函数将该定时器根据它新的expires值重新插入到相应的链表中。函数detach_timer首先调用timer_pending来判断指定的定时器是否已经处于某个链表中,如果定时器原来就不处于任何链表中,则detach_timer函数什么也不做,直接返回0值,表示失败。否则,就调用list_del函数将定时器从它原来所处的链表中摘除。如下所示(kernel/timer.c):static inline int detach_timer struct timer_list *timerif !tim
43、er_pendingtimerreturn 0;list_del&timer-list;return 1; 2.2.系统平台:一台Linux主机且有超级用户权限2.3编程工具:VI编辑器,Gedit编辑器3.数据结构与模块说明(功能与流程图)3.1.定时器使用:int gettimeofdaystruct timeval *tv,struct timezone *tz; strut timevallong tv_sec;/*秒数*/ long tv_usec; /*微秒数*/ ;这个syscall用来供用户获取timeval格式的当前时间信息(精确度为微秒级),以及系统的当前时区信息(
44、timezone)。结构类型timeval的指针参数tv指向接受时间信息的用户空间缓冲区,参数tz是一个timezone结构类型的指针,指向接收时区信息的用户空间缓冲区。这两个参数均为输出参数,返回值0表示成功,返回负值表示出错。实现过程如下:main struct timeval tpstart,tpend; /*申请struct timeval的变量,tv_sec返回的是秒数,tv_usec返回的是微秒数*/float timeuse; gettimeofday&tpstart,NULL; pthread; gettimeofday&tpend,NULL; timeuse1
45、000000*tpend.tv_sec-tpstart.tv_sec+ tpend.tv_usec-tpstart.tv_usec; timeuse/1000000; printf"Used Time:%f secn",timeuse; exit0; 3.2 多线程程序:进行多线程程序设计时,我们使用到了两个函数,pthread_create和pthread_join,并声明了一个pthread_t型的变量。pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义,它是一个线程的标识符。函数pthread_create用来创建一个线程
46、,函数pthread_join用来等待一个线程的结束。实现过程如下:int pthread_create&id,NULL,void * thread,NULL;pthread_joinid,NULL;void threadvoidint i;fori0;i3;i+printf"This is a pthread.n"int pthreadvoidpthread_t id;/* 声明了一个pthread_t型的变量*/int i,ret;retpthread_create&id,NULL,void * thread,NULL;ifret!0printf&quo
47、t;Create pthread error!n"exit1;fori0;i3;i+printf"This is the main process.n"pthread_joinid,NULL;return0;3.3程序流程图:4. 源程序: #include #include #include int gettimeofdaystruct timeval *tv,struct timezone *tz; int pthread_create&id,NULL,void * thread,NULL; /pthread_joinid,NULL; strut ti
48、mevallong tv_sec;/*秒数*/ long tv_usec; /*微秒数*/ ;void threadvoidint i;fori0;i3;i+printf"This is a pthread.n"int pthreadvoidpthread_t id;/* 声明了一个pthread_t型的变量*/int i,ret;retpthread_create&id,NULL,void * thread,NULL;ifret!0printf"Create pthread error!n"exit1;fori0;i3;i+printf"This is the main process.n"pthread_joinid,NULL;return0;main struct timeval tpstart,tpend; /*申请struct timeval的变量,tv_sec返回的是秒数,tv_usec返回的是微秒数*/float timeuse; gettimeofday&tpstart,NULL; pthread; gettimeofday&tpend,NULL; timeuse1000000*tpend.tv_sec-tpstar
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 30137-2024电能质量电压暂升、电压暂降与短时中断
- 2024版泥水作业班组承包协议书
- 二零二五年度股权收益权转让合同范本与收益分配3篇
- 二零二五年航空航天零部件制造合同协议模板2025版3篇
- 二零二五年金融产品居间服务协议范本3篇
- 二零二五年度智能化设备技术入股合作协议范本3篇
- GRC材质2024装饰构件定制合作协议版B版
- 二零二五版汽车租赁转让与保险责任合同2篇
- 2024混凝土施工劳务分包合同
- 2024年跨区域生态环境保护合作协议
- 2020小升初复习-小升初英语总复习题型专题训练-完形填空15篇
- 2023年浙江省公务员考试面试真题解析
- GB/T 5796.3-2022梯形螺纹第3部分:基本尺寸
- GB/T 16407-2006声学医用体外压力脉冲碎石机的声场特性和测量
- 简洁蓝色科技商业PPT模板
- 钱素云先进事迹学习心得体会
- 道路客运车辆安全检查表
- 宋晓峰辣目洋子小品《来啦老妹儿》剧本台词手稿
- 附录C(资料性)消防安全评估记录表示例
- 噪音检测记录表
- 推荐系统之协同过滤算法
评论
0/150
提交评论