返回首页

{A}{S0}目录{A2}{A3}{A4}{A5}{A6}{A7}{A8}{A9}{A10}{A11}简介
这篇文章是后续我{A12}提示显示了如何获得整体处理器的使用率。在这里,我们将研究如何获得每个进程(甚至每个线程)的使用统计。
的基本算法是相当简单:查询每个正在运行的线程在内核和用户空间所花费的时间金额。调用这个查询A。等待一些时间预定量。重复的查询和调用这个查询B。从时间查询答:这告诉我们,线程使用处理器在等待所花费的时间量,减去查询B的时间。
我们将简单地套用该算法的每一个线程在系统中的每个过程!
我们将开始的秘密武器,它告诉我们花了一个线程运行的时间量,{A3}。
作为一个侧面说明,连接的演示应用程序使用{A14}。我认为写他们只与Windows Mobile 6 SDK的使用,但Boost库做了这样一个令人难以置信的工作,使代码更具可读性,异常安全的,可用的,我决定使用它们。如果你不使用Boost(你真的应该!),在这篇文章中提出的概念仍然适用。请注意,文章本身只使用彗星03和Windows Mobile SDK。{A15} GetThreadTimes
{A16}为我们提供了一般信息系统中的每个正在运行的线程。特别是,我们有兴趣知道线程已经花了多少时间在内核模式下执行,并在用户模式下执行的线程花费了多少时间。 GetThreadTimes为我们提供了这些值的FILETIME结构。要利用它们,我们将不得不将其转换为毫秒。

///<span class="code-comment"> Convert a FILETIME to ticks (ms)

</span>DWORD GetThreadTick( const FILETIME& time )

{

    __int64 tick = MAKEDWORDLONG( time.dwLowDateTime, time.dwHighDateTime );

    return static_cast< DWORD >( tick /= 10000 );

}



FILETIME creation = { 0 },

         exit = { 0 },

         kernel = { 0 },

         user = { 0 };

::GetThreadTimes( ( HANDLE )thread_id,

                  &creation,

                  &exit,

                  &kernel,

                  &user )



//<span class="code-comment"> time in ms spent in kernel space



</span>DWORD kernel_tics = GetThreadTick( kernel );



//<span class="code-comment"> time in ms spent in user space

</span>DWORD user_tics = GetThreadTick( user );

现在,我们可以计算出每个线程使用的处理器花费的时间,我们必须找到一种方法,列出每一个线程在一个进程中运行。为此,我们将使用{A4}。{A18} ToolHelp API
{A19}是一套诊断工具,在Windows Mobile核心OS,使我们能够采取堆,模块,线程运行在一个单一的时间点的过程中使用的快照。在这个例子中,我们能够遍历每个线程在系统中运行。 THREADENTRY32结构告诉我们,每个线程的ID和它的父进程ID。{C}
不幸的是,如果你是实际系统上运行此代码,因为它是,你会很快发现,只有2进程的线程显示:您的过程和NK.exe。为了解决这个权限的限制,我们将着眼于{A20} API。{A21}
{A22}是Platform Builder的pkfuncs.h API的一部分,通常只需要访问整个虚拟地址空间的驱动器使用。为了确保我们明智地使用这些惊人的宇宙的权力,我们将定义一个结构,保证我们恢复我们原来的权限时,我们的函数完成即使它抛出一个异常。
///<span class="code-comment"> Temporarily grant phenomenal cosmic powers. This may not be necessary for

</span>///<span class="code-comment"> versions of windows mobile earlier than 6.

</span>struct CosmicPowers

{

    CosmicPowers()

    {

        old_permissions_ = ::SetProcPermissions( 0xFFFFFFFF );

    }



     CosmicPowers()

    {

        ::SetProcPermissions( old_permissions_ );

    }



private:

    DWORD old_permissions_;

}; // struct CosmicPowers

我们现在要做的是创建一个线程迭代函数CosmicPowers的实例,我们应该看到,从系统中的每个进程的线程。如果你没有访问Platform Builder中,都好。 MSDN上给你{A23},并告诉您它是由coredll.lib出口(即每个人都有)。
现在,我们已经开始收集线程处理器的使用率统计,我们需要的所有组件!{A24}收集线程统计
现在我们必须考虑我们要如何来存储线程​​使用的统计信息。对于这个例子,我选择了嵌套STD:地图关联容器。这让我们很容易因此保持代码的简洁大方的数据阵列式访问。
///<span class="code-comment"> Time a thread has spent working

</span>struct thread_times {

    ///<span class="code-comment"> Time a thread has spent in kernel space

</span>    FILETIME kernel;

    ///<span class="code-comment"> Time a thread has spent in user space

</span>    FILETIME user;

};



///<span class="code-comment"> Time each process has spent working

</span>///<span class="code-comment"> @param DWORD - Thread ID

</span>///<span class="code-comment"> @param thread_times - Thread working times

</span>typedef std::map< DWORD, thread_times > Threads;



///<span class="code-comment"> Time each Process has spent working

</span>///<span class="code-comment"> @param DWORD - Process ID

</span>///<span class="code-comment"> @param Threads - Process' thread working times

</span>typedef std::map< DWORD, Threads > Processes;

现在,我们准备把它一起。在这个例子中,我们将遍历每个线程在系统中运行,获得该线程使用CPU花费的时间和返回容器关联的进程ID线程ID线程使用。
///<span class="code-comment"> Gets the list of currently running processes

</span>Processes GetProcessList()

{

    Processes process_list;

    CosmicPowers we_are_powerful;



    HANDLE snapshot = ::CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );

    if( INVALID_HANDLE_VALUE != snapshot )

    {

        DWORD process_total = 0;



        THREADENTRY32 te = { 0 };

        te.dwSize = sizeof( THREADENTRY32 );



        if( ::Thread32First( snapshot, &te ) )

        {

            do

            {

                FILETIME creation = { 0 },

                         exit = { 0 },

                         kernel = { 0 },

                         user = { 0 };



                if( ::GetThreadTimes( ( HANDLE )te.th32ThreadID,

                                      &creation,

                                      &exit,

                                      &kernel,

                                      &user ) )

                {

                    thread_times t = { kernel, user };

                    process_list[ te.th32OwnerProcessID ][ te.th32ThreadID ] = t;

                }

            } while( ::Thread32Next( snapshot, &te ) );

        }



        ::CloseToolhelp32Snapshot( snapshot );

    }



    return process_list;

}

现在,我们的统计是完全32位的进程标识符,这是不向任何人在寻找一个想知道它的处理器有多少时间的应用程序非常有用。因此,我们需要一个{A25}的方式。{A26}有用的名称关联的PID
因为用户知道他们的应用程序名称和不通过PID,这将是很好,我们联想到我们的信息与该进程的名称。在与我们用于存储数据的关联容器的早期使用,我们将使用另一个。这人会与他们的可执行文件的名称关联的唯一的32位的进程标识符。
///<span class="code-comment"> Associates process IDs to process names

</span>///<span class="code-comment"> @param DWORD - Process identifier

</span>///<span class="code-comment"> @Param std::wstring - process' executable's name

</span>typedef std::map< DWORD, std::wstring > ProcessNames;

我们将再次转{A19},但这个时候,我们会得到一个正在运行的进程,而不是更大的线程列表的快照。
对于一个简单的例子,如果你想打印在进程列表中的每个进程的名称,你现在可以这样做:
Processes procs = GetProcessList();

ProcessNames names = GetProcessNameList();

for( Processes::const_iterator p = procs.begin(); p != procs.end(); ++p )

    NKDbgPrintfW( L"%s\r\n", names[ p->first ].c_str() );

现在我们有一个完整的快照每个线程处理器的系统,这个时间点的使用情况统计。我们甚至不知道该快照中的每一道工序的名称。但是,获得%的使用情况,我们必须知道一个线程花费多少时间跑过来一个固定的时间内。所以,现在我们必须{A28}。{A29}计算过程使用统计
下面的代码看起来大和恐吓,它主要是统计计算。它遵循的算法是相当简单:获取的名称和所有正在运行的进程的PID的初步清单。得到一个在内核和用户时间花了多久,每个PID的初步清单。延迟间隔已过期后,得到另一个列表中的每个PID和它的内核和用户时间。计算过程中的每个内核和用户空间花了多少时间等待间隔。如果任何进程的PID是不是在我们的名称列表,刷新我们的进程的名称列表。这意味着我们有一个新的进程。报告统计信息,用户莫名其妙。 重复步骤3。
//<span class="code-comment"> how often should we snapshot the system for new data?

</span>DWORD interval = 3000;



//<span class="code-comment"> initial list of process IDs and names

</span>PI::ProcessNames names = PI::GetProcessNameList();



//<span class="code-comment"> initial list of thread statistics

</span>PI::Processes old_list = PI::GetProcessList();



DWORD start = ::GetTickCount();



while( true )

{

    Sleep( interval );



    PI::Processes new_list = PI::GetProcessList();

    DWORD duration = ::GetTickCount() - start;



    DWORD system_total = 0;

    for( PI::Processes::const_iterator p2 = new_list.begin();

         p2 != new_list.end();

         ++p2 )

    {

        PI::Processes::const_iterator p1 = old_list.find( p2->first );

        if( p1 != old_list.end() )

        {

            DWORD user_total = 0;

            DWORD kernel_total = 0;



            for( PI::Threads::const_iterator t2 = p2->second.begin();

                 t2 != p2->second.end();

                 ++t2 )

            {

                PI::Threads::const_iterator t1 = p1->second.find( t2->first );

                if( t1 != p1->second.end() )

                {

                    kernel_total += PI::GetThreadTick( t2->second.kernel ) -

                                    PI::GetThreadTick( t1->second.kernel );

                    user_total += PI::GetThreadTick( t2->second.user ) -

                                  PI::GetThreadTick( t1->second.user );

                }

            }



            float user_percent = ( user_total ) / 

			static_cast< float >( duration ) * 100.0f;

            float kernel_percent = ( kernel_total ) / 

			static_cast< float >( duration ) * 100.0f;

            system_total += user_total + kernel_total;



            //<span class="code-comment"> locate the process name by its ID

</span>            PI::ProcessNames::const_iterator found_name = names.find( p2->first );



            //<span class="code-comment"> if the process ID isn't in the name list, it must be new.

</span>            //<span class="code-comment"> refresh the name list and try again.

</span>            if( found_name == names.end() )

            {

                names = PI::GetProcessNameList();



                found_name = names.find( p2->first );



                //<span class="code-comment"> still can't find the process ID? Just move on.

</span>                if( found_name == names.end() )

                    continue;



                //<span class="code-comment"> At this point we have the process name, kernel time and user

</span>                //<span class="code-comment"> time. use this information in good health!

</span>                //<span class="code-comment">



</span>                //<span class="code-comment"> user_percent = % of the time this process spent in user code

</span>                //<span class="code-comment"> kernel_percent = % of the time this process spent in kernel code

</span>                //<span class="code-comment"> found_name = name of the process' executable.

</span>            }

        }

    }



    //<span class="code-comment"> calculate the total processor percent used

</span>    float percent_used = system_total / static_cast< float >( duration ) * 100.0f;



    old_list = new_list;

    start = ::GetTickCount();

}
{A30}结论
在这篇文章中,我们已经讨论了如何获得许可,以收集所有正在运行的线程的信息,如何让每个线程处理器,数据结构组织的信息是有用的花费的时间,以及最后如何使用该信息来计算每个进程的CPU占用率统计。例如对于如何显示给用户的信息,请在所附的示例应用程序的外观。{A31}历史二月十八日,2011年:初始版本

回答

评论会员:S 时间:2