優先度と言えば通常はCPUの優先度で、これを低く設定するとそのプロセスはCPUを使いづらくなる。あるメインのタスクがあってそれを重点的にやりたい場合、このタスクの優先度を上げて、これとは別の忙しそうなタスクの優先度を下げる。こうすることによりメインのタスクは他のタスクに邪魔されずにスムーズに作業が進む。
しかしCPUの優先度を下げてもそのプロセスが大量のデータをハードディスクでやり取りしているとうまくいかなくなる。メインタスクがハードディスクにアクセスしようとしてもCPUの優先度はこの場合関係がないので、待たされることになり作業が捗らない。
そこで使うのがIOの優先度なのだが、WindowsにしてもLinuxにしてもドキュメントやAPI整備が整っていない。
Windows
もっともよくドキュメントが整備されているのがSetPriorityClass()で指定できるバックグラウンド設定だ。この設定だけは他の設定と違い、BEGINとENDで開始と終了を指定する。なぜこんなことになっているのかといえばバックグラウンドの設定だけはCPUだけじゃなくIO優先度や他の優先度(メモリ)も変更するからだと思われる。しかし制限もあり、バックグラウンドの場合だけは他のプロセスの設定を変更できない(実際にエラーになる)。
そこでこれを実現したいのだがドキュメントがないのでネットで調べるとntdll.dllで定義されるNtSetInformationProcessを使うことがわかった。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// http://blog.misterfoo.com/2010/07/process-priority-utility.html // these values determined by poking around in the debugger - use at your own risk! const DWORD ProcessInformationMemoryPriority = 0x27; const DWORD ProcessInformationIoPriority = 0x21; const DWORD DefaultMemoryPriority = 5; const DWORD LowMemoryPriority = 3; const DWORD DefaultIoPriority = 2; const DWORD LowIoPriority = 1; const DWORD VeryLowIoPriority = 0; typedef NTSTATUS(NTAPI *FNNtSetInformationProcess)( HANDLE process, ULONG infoClass, void* data, ULONG dataSize); |
この関数は挙動が不明なところがあって、デバッガでステップオーバーすると失敗するが、普通に実行すると成功したりする。この関数でメモリとIOの優先度を変更できる。
IOの優先度を最低にするには以下のように行う。
1 2 3 4 5 6 |
ULONG io = VeryLowIoPriority; ntStatus = fnNtSetInformationProcess( hProcess, ProcessInformationIoPriority, &io, sizeof(io)); |
0が返ってくれば成功。hProcessをOpenProcess()で取得する場合はフラグにPROCESS_SET_INFORMATIONを指定して開く。
Linux
Linuxの場合はコマンドのioniceがあるので、このソースを見ればいい。しかしこれもドキュメントやヘッダファイルがないようである。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
static inline int ioprio_set(int which, int who, int ioprio) { return syscall(SYS_ioprio_set, which, who, ioprio); } enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, }; enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, }; #define IOPRIO_CLASS_SHIFT (13) #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) |
ioniceから持ってきたであろう上のソースをコピペして以下のように呼ぶ。
1 2 |
int ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE,7); err = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio); |
ioprioは2つの値DATAとCLASSから構成されていてCLASSで優先度を指定する。DATAはスルー。Linuxの場合、優先度をある程度上げるときはroot権限が必要な模様でその失敗の場合-1が返る。