OS Xのスレッド:外部プログラムのすべてのスレッドのCPU使用率を取得する方法は?

こんにちは、Khabrovites-makovody様!



[Mac] OS Xには、プロセスとCPU時間によって占有されているメモリを簡単に表示するアクティビティモニターという素晴らしい組み込みツールがあります。 まあ、これは非常に良いですが、時々あなたは奇妙な何かが欲しいです。 たとえば、スレッドを持つスレッドの数と、各スレッドが消費するCPUの量を確認します。 ここでは、既にActivity Monitorが何らかの方法で私たちを支援することはできません。経験豊富なLinuxはprocfs



ファイルシステムをここで見つけられません。 この問題を自分で解決する必要があります。



今日は、プロセスのPIDをこのプログラムの各スレッドのCPU使用率(および一般的な使用法)の入力および出力情報として使用する小さなコンソールプログラムの作成方法について説明します。



純粋なCで記述します。ソースファイルは1つだけであるため、このような小さなプロジェクトにはXcodeを使用せず、通常のMakefileとします。



まず、少しの理論。 プログラムからサードパーティプログラムに接続し、そのスレッドのリストを要求し、各スレッドのプロパティを取得する必要があります。 これを行うには、タスクとそのスレッドを操作するための関数task_for_pid()



およびtask_threads()



を使用する必要があります。



しかし、悲しいかな。 これらの関数を使用するには、プログラムに特別な権限が必要です(これをthreadmon



と呼びましょうが、これは重要ではありません)。 Mac OS X 10.5までの有能な情報源によると、何も必要ありませんでしたが、セキュリティ上の理由から、そのような制限が導入されました。 そして、これはすべて、証明書で実行可能ファイルに署名する必要があることを意味します。また、関数を呼び出す前に、 セキュリティフレームワークを介してそれらを実行する権利をユーザーに要求します。 さて、最初から始めましょう:ユーザーに権利を要求する関数を書きます:



 #include <Security/Authorization.h> int acquireTaskportRight() { OSStatus stat; AuthorizationItem taskport_item[] = {{"system.privilege.taskport:"}}; AuthorizationRights rights = {1, taskport_item}, *out_rights = NULL; AuthorizationRef author; AuthorizationFlags auth_flags = kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed | ( 1 << 5); stat = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, auth_flags, &author); if (stat != errAuthorizationSuccess) { return 1; } stat = AuthorizationCopyRights(author, &rights, kAuthorizationEmptyEnvironment, auth_flags, &out_rights); if (stat != errAuthorizationSuccess) { return 1; } return 0; }
      
      





実際、この関数はtask_for_pid()



呼び出しに成功するために必要なタスクポート特権の特権をユーザーに要求します。 ここで、 main()



関数の先頭でacquireTaskportRight()



を呼び出し、戻り値を確認する必要がありますacquireTaskportRight()



すべては問題ありません。それ以外の場合-特権は受信されません。 さて、さらに書いてください。 入力プログラムが、情報を受け取るプロセスのpidを受け取るようにします。 main()



関数に書き込みます:



 int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage:\n %s <PID>\n", argv[0]); return -1; } if (acquireTaskportRight()) { printf("No rights granted by user or some error occured! Terminating.\n"); return -2; } char* end; pid_t pid = strtol(argv[1], &end, 10); if (*end) { printf("Error: invalid PID given: \"%s\", terminating.\n", argv[1]); return -3; } printf("Starting threadmon for PID %d\n", pid); // TODO: the rest }
      
      





次に、最も興味深いものに移ります。 pidからタスクとそのすべてのスレッドを受け取ります。



  task_t port; kern_return_t kr = task_for_pid(mach_task_self(), pid, &port); if (kr != KERN_SUCCESS) { printf("task_for_pid() returned %d, terminating.\n", kr); return -4; } thread_array_t thread_list; mach_msg_type_number_t thread_count; thread_info_data_t thinfo; mach_msg_type_number_t thread_info_count; thread_basic_info_t basic_info_th; // get threads in the task kr = task_threads(port, &thread_list, &thread_count); if (kr != KERN_SUCCESS) { printf("task_threads() returned %d, terminating.\n", kr); return -5; }
      
      





さて、これは小さなことです。受け取ったすべてのスレッドを調べて、必要な情報を引き出します。



  long tot_cpu = 0; int j; for (j = 0; j < thread_count; j++) { thread_info_count = THREAD_INFO_MAX; kr = thread_info(thread_list[j], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count); if (kr != KERN_SUCCESS) { printf("Thread %d: Error %d\n", thread_list[j], kr); continue; } basic_info_th = (thread_basic_info_t)thinfo; if (!(basic_info_th->flags & TH_FLAGS_IDLE)) { tot_cpu = tot_cpu + basic_info_th->cpu_usage; printf("Thread %d: CPU %d%%\n", thread_list[j], basic_info_th->cpu_usage); } } printf("---\nTotal: CPU %ld%%\n", tot_cpu); return 0;
      
      





まあ、私たちはほとんど使用できる完全に実行可能なプログラムを得ました。 小さなニュアンス:プログラムにはまだ権利がありません。 私たちはそれらを与えませんでした。 さらに2つのことを行う必要があります。Info.plistを追加し、結果のバイナリに署名します。



このリストのようなものを作成します。



 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleIdentifier</key> <string>com.silvansky.threadmon</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>threadmon</string> <key>CFBundleVersion</key> <string>1.0</string> <key>SecTaskAccess</key> <array> <string>allowed</string> </array> </dict> </plist>
      
      





最後のキーSecTaskAccess



に注意をSecTaskAccess



必要があります。 ここで、Makefileに変更を加える必要があります。リンク中にリストを追加します。



 LOPTS=-framework Security -framework CoreFoundation -sectcreate __TEXT __info_plist ./Info.plist
      
      





さて、今ではほとんどすべて、システムはプログラムが正常に機能するために必要な権利を理解します。 しかし、開発者証明書で署名するまで、彼はプログラムに彼らを与えません。



ここでは、証明書とキー、開発者IDなどについて長い間話すことができますが、状況を簡単に説明するだけです。開発者ID証明書がある場合は、安全に署名します。 そうでない場合は、 Keychainを使用してコード署名用の自己署名証明書を生成できます。 しかし、私の最後の方法はうまくいきませんでしたが、これはOS X 10.8の問題であると言われています。初期のシステムでは起動するはずです。



ただし、このユーティリティを実行する前に毎回sudo



を入力するのがsudo



なければ、再度署名する必要はありません。 =)



私たちは署名します:



 codesign -s "your-certificate-name" ./threadmon
      
      





テスト:



 $ ps -A | grep Xcode 775 ?? 617:02.82 /Applications/Xcode.app/Contents/MacOS/Xcode -psn_0_348245 73761 ttys005 0:00.00 grep Xcode $ ./threadmon 775 Starting threadmon for PID 775 Thread 6147: CPU 55% Thread 6403: CPU 0% Thread 6659: CPU 0% Thread 6915: CPU 0% Thread 7171: CPU 0% Thread 7427: CPU 0% Thread 7683: CPU 0% Thread 7939: CPU 0% Thread 8195: CPU 0% Thread 8451: CPU 0% Thread 8707: CPU 0% Thread 8963: CPU 0% Thread 9219: CPU 0% Thread 9475: CPU 0% Thread 9731: CPU 0% Thread 9987: CPU 0% Thread 10243: CPU 0% Thread 10499: CPU 0% Thread 10755: CPU 0% Thread 11011: CPU 0% Thread 11267: CPU 0% Thread 11523: CPU 22% Thread 11779: CPU 7% Thread 12035: CPU 32% Thread 12291: CPU 46% Thread 12547: CPU 14% Thread 12803: CPU 0% --- Total: CPU 176%
      
      





まあ、かなり価値があります! 完全なソースコードについては、通常どおり、 githubに送信します。



PS:コードは、StackOverflowおよびさまざまな個人ブログへの回答から取得したスニペットを使用しています。

PPS:メソッドをよりよく理解している場合は、コードの明白な欠陥と暗黙の欠陥を参照してください。コメントを書いてください。



All Articles