退出时间 用英国格林威治时间1 6 0 1年1月1日午夜后1 0 0 n s的时间间隔表示的英国绝对值,用于指明线程退出的时间。如果线程仍然在运行,退出时间则未定义 一个相对值,用于指明线程执行操作系统代码已经经过了多少个1 0 0 n s的C P U时间 一个相对值,用于指明线程执行应用程序代码已经经过了多少个1 0 0 n s的C P U时间 内核时间 用户时间 使用这个函数,可以通过使用下面的代码确定执行复杂的算法时需要的时间量:
__int64 FileTimeToQuadWord(PFILETIME pft) {
return (Int64ShllMod32(
pft->dwHighDateTime, 32) | pft->dwLowDateTime); }
void PerformLongOperation() {
FILETIME ftKernelTimeStart, ftKernelTimeEnd; FILETIME ftUserTimeStart, ftUserTimeEnd;
FILETIME ftDummy;
__int64 qwKernelTimeElapsed, qwUserTimeElapsed, qwTotalTimeElapsed;
//Get starting times.
GetThreadTimes(GetCurrentThread(), &ftDummy,
&ftDummy, &ftKernelTimeStart, &ftUserTimeStart);
//Perform complex algorithm here.
//Get ending times.
GetThreadTimes(GetCurrentThread(), &ftDummy, &ftDummy, &ftKernelTimeEnd, &ftUserTimeEnd);
//Get the elapsed kernel and user times by //converting the start and end times //from FILETIMEs to quad words, and then
//subtract the start times from the end times.
qwKernelTimeElapsed =
FileTimeToQuadWord(&ftKernelTimeEnd) -
FileTimeToQuadWord(&ftKernelTimeStart);
qwUserTimeElapsed =
FileTimeToQuadWord(&ftUserTimeEnd) - FileTimeToQuadWord(&ftUserTimeStart);
//Get total time duration by adding the kernel //and user times.
qwTotalTimeElapsed = qwKernelTimeElapsed + qwUserTimeElapsed;
//The total elapsed time is in //qwTotalTimeElapsed. }
注意,G e t P r o c e s s Ti m e s是个类似G e t T h r e a d Ti m e s的函数,适用于进程中的所有线程:
BOOL GetProcessTimes(HANDLE hProcess,
PFILETIME pftCreationTime, PFILETIME pftExitTime, PFILETIME pftKernelTime, PFILETIME pftUserTime);
G e t P r o c e s s Ti m e s返回的时间适用于某个进程中的所有线程(甚至是已经终止运行的线程)。例如,返回的内核时间是所有进程的线程在内核代码中经过的全部时间的总和。 Windows 98 遗憾的是, G e t T h r e a d Ti m e s和G e t P r o c e s s Ti m e s这两个函数在Wi n d o w s9 8中不起作用。在Windows 98中,没有一个可靠的机制可供应用程序来确定线程或进程已经使用了多少C P U时间。
对于高分辨率的配置文件来说, G e t T h r e a d Ti m e s并不完美。Wi n d o w s确实提供了一些高分辨率性能函数:
BOOL QueryPerformanceFrequency( LARGE_INTEGER* pliFrequency);
BOOL QueryPerformanceCounter( LARGE_INTEGER* pliCount);
虽然这些函数认为,正在执行的线程并没有得到抢占的机会,但是高分辨率的配置文件是为短期存在的代码块设置的。为了使这些函数运行起来更加容易一些,我创建了下面这个C + +类:
class CStopwatch {
public:
CStopwatch() {
QueryPerformanceFrequency(&m_liPerfFreq); Start(); }
void Start() {
QueryPerformanceCounter(&m_liPerfStart); }
__int64 Now() const {
//Returns # of milliseconds since //Start was called
LARGE_INTEGER liPerfNow;
QueryPerformanceCounter(&liPerfNow);
return (((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000)/ m_liPerfFreq.QuadPart); }
private:
//Counts per second
LARGE_INTEGER m_liPerfFreq;
//Starting count
LARGE_INTEGER m_liPerfStart; };
使用这个类如下:
//Create a stopwatch timer
//(which defaults to the current time). CStopwatch stopwatch;
//Execute the code I want to profile here.
//Get how much time has elapsed up to now. __int64 qwElapsedTime = stopwatch.Now();
//qwElapsedTime indicates how long
//the profiled code executed in milliseconds.
7.6 运用结构环境
现在应该懂得环境结构在线程调度中所起的重要作用了。环境结构使得系统能够记住线程的状态,这样,当下次线程拥有可以运行的C P U时,它就能够找到它上次中断运行的地方。 知道这样低层的数据结构也会完整地记录在Platform SDK文档中确实使人吃惊。不过如果查看
该文档中的C O N T E X T结构,会看到下面这段文字:
“C O N T E X T结构包含了特定处理器的寄存器数据。系统使用C O N T E X T结构执行各种内部操作。目前,已经存在为I n t e l、M I P S、A l p h a和P o w e r P C处理器定义的C O N T E X T结构。若要了解这些结构的定义,参见头文件Wi n N T. h”。
该文档并没有说明该结构的成员,也没有描述这些成员是谁,因为这些成员要取决于Windows 2000在哪个C P U上运行。实际上,在Wi n d o w s定义的所有数据结构中, C O N T E X T结构是特定于C P U的唯一数据结构。
那么C O N T E X T结构中究竟存在哪些东西呢?它包含了主机C P U上的每个寄存器的数据结构。在x 8 6计算机上,数据成员是E a x、E b x、E c x、E d x等等。如果是A l p h a处理器,那么数据成员包括I n t V 0、I n t T 0、I n t T 1、I n t S 0、I n t R a和I n t Z e r o等等。下面这个代码段显示了x86 CPU的完整的C O N T E X T结构:
typedef struct _CONTEXT {
//
//The flags values within this flag control //the contents of a CONTEXT record. //
// If the context record is used as an // input parameter, then for each portion // of the context record controlled by a flag // whose value is set, it is assumed that
// that portion of the context record contains // valid context. If the context record
// is being used to modify a threads context, // then only that portion of the threads // context will be modified. //
// If the context record is used as an
// IN OUT parameter to capture the context of // a thread, then only those portions of the // thread's context corresponding to set flags // will be returned. //
// The context record is never used as an OUT // only parameter.
DWORD ContextFlags;
// This section is specified/returned if
// CONTEXT_DEBUG_REGISTERS is set in ContextFlags. // Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL.
DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7;
//
// This section is specified/returned if the // ContextFlags word contians the flag // CONTEXT_FLOATING_POINT. //
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the // ContextFlags word contians the flag // CONTEXT_SEGMENTS. //
DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs;
//
// This section is specified/returned if the // ContextFlags word contians the flag // CONTEXT_INTEGER. //
DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax;
//
// This section is specified/returned if the // ContextFlags word contians the flag //CONTEXT_CONTROL. //