続: 結局は psutil の方がより救世主なの (psuti.Process の nice)

psutil に触れたその時点でもう言わずもがなな人相手には言わずもがな、な内容なので、「ワタシが書くことか?」とも思うようなそんな。

Unix ユーザと Windows ユーザの違いの一つとして、「タスク(あるいは「ジョブ」、「プロセス」)」についての「優先度」が身近なのかどうか、てのがある。

命名の妙だよな、と思うのだよね。「Unix の教科書」的なものを開けばかなり最初の方に登場しそうなのが「やったねあんたはえらいnice値」で、なんでそう教科書的かというと、「Unix文化」をよく表した命名だから、なの。

しょっぱなから「パーソナルなコンピュータ」としてのスタートだった MSDOS, Windows, Mac OS らと違って、もともと Unix は「多重ログイン上等」のもとに誕生した OS である。つまり、「たくさんのユーザが一台の Unix 機にログインして使うので、「譲り合いの心が必要である」」と。そうして「プロセスの優先度を調整するプログラム」に「nice」という名前がついた。そう、「譲ってあげるオレ、ナイスでしょ?」てこと。ゆえ、Unix ユーザは、(少なくとも昔は)初心者であっても「プロセスの優先度を変更できる」ことを常識として知っていた。

一方で、Windows にはそんな文化はない上に、マイクロソフトが「そういうことはあなたは意識すべきではない」として、原則巧妙に隠しているので、実は Windows でも同じことが出来ることを、おそらく一般ユーザの多くが知らない。Unix 経験者のほうが気づきやすいとは思うが、そうでない場合でこれを知っているのは、特に Windows サービスなどのシステムプログラミング経験者で、一般のデスクトップアプリケーション開発しか経験がないと、技術者であっても結構知らないんではないかと想像する。出来るよ:

問題は、今の Windows 10 タスクマネージャでこの「優先度の設定」メニューを「どうやって出すか」だ。わからない人はわからない。気づかない人は多分一生気づかない。

そんなわけで、Windows 文化において、マイクロソフト自身以外でこの「優先度」を活用していると思われるのは「Windows を何かしらのサーバに採用した(奇特な)組織」くらいではないのかと思う。以外では、個々のアプリケーションが自身の優先度を変えるために使う例がわずかばかりある程度である(たとえば 7-zip などのアーカイバや、ビデオ編集ソフトなどの重いやつら)。

てわけで、Windows で Unix と同じようなノリで優先度を変えようとするのは「タスクマネージャがあぁなので」鬱陶しくて、普通はそんなに日常にはしてないだろう。そんなあなたに?:

suspend, resume ネタで書いたコードとほとんど同じである
 1 # -*- coding: utf-8 -*-
 2 from __future__ import print_function
 3 from __future__ import unicode_literals
 4 
 5 import os
 6 import sys
 7 import pipes
 8 import re
 9 import psutil
10 
11 
12 def _str(s):
13     return s.encode(sys.getfilesystemencoding(), errors="xmlcharrefreplace").decode(
14         sys.getfilesystemencoding())
15 
16 
17 _prio_nmap = {
18     "1": ("ABOVE_NORMAL_PRIORITY_CLASS", psutil.ABOVE_NORMAL_PRIORITY_CLASS),
19     "2": ("BELOW_NORMAL_PRIORITY_CLASS", psutil.BELOW_NORMAL_PRIORITY_CLASS),
20     "3": ("HIGH_PRIORITY_CLASS", psutil.HIGH_PRIORITY_CLASS),
21     "4": ("IDLE_PRIORITY_CLASS", psutil.IDLE_PRIORITY_CLASS),
22     "5": ("NORMAL_PRIORITY_CLASS", psutil.NORMAL_PRIORITY_CLASS),
23     "6": ("REALTIME_PRIORITY_CLASS", psutil.REALTIME_PRIORITY_CLASS),
24 }
25 
26 
27 if __name__ == '__main__':
28     import argparse
29     ap = argparse.ArgumentParser()
30     ap.add_argument("--search_cmdline_regexp", default=".*")
31     ap.add_argument("--search_cmd_regexp", default=".*")
32     args = ap.parse_args()
33     def _catcmdl(cmdl):
34         return " ".join([pipes.quote(a) for a in cmdl])
35     ma = {
36         "cmd": re.compile(args.search_cmd_regexp),
37         "cmdline": re.compile(args.search_cmdline_regexp),
38         }
39     for p in psutil.process_iter():
40         cmdl = ""
41         try:
42             cmdl = p.cmdline()
43         except Exception as e:
44             print(e, file=sys.stderr)
45         if not cmdl:
46             continue
47         tcmdl = _catcmdl(cmdl)
48         if ma["cmd"].search(cmdl[0]) and ma["cmdline"].search(tcmdl):
49             orig = p.nice()
50             print(_str(tcmdl), "|", p.nice())
51             q = input("{}? ".format(" / ".join(
52                 ["{}: {}".format(n, _prio_nmap[n][0]) for n in sorted(_prio_nmap.keys())])))
53             if q in _prio_nmap:
54                 p.nice(_prio_nmap[q][1])
55                 print(orig, "=>", p.nice())

どういうときに使いたいかって、たぶんだけど「優先度を上げたい」時ではない気がする。まさに ffmpeg なんだけど、「どうせ何時間もかかるんなら 1時間も2時間も同じろ」として優先度を下げてノンビリ処理してもらおうぜ、みたいなことをね、たぶんしたいことが多いと思うんだ。他のあらゆる作業ができなくなるほど凄まじく CPU 時間を使ってしまうことも多いからね。


2021-04-11追記:
思った以上に日常使い出来ると感じ始めてる。ゆえに、ちと整理して Gist 管理してみることにする:

まぁこういうの、欲張りだせばまだまだ色々あるんだけどさ。

なお、「WM_CLOSE を SendMessage」みたいなことも出来たらいいなぁと思ったが、やっぱりノリが Unix なんで、これは psutil はまかなってくれないみたい。これをやりたい場合、どこまでさかのぼらなければならんだろうか? ウィンドウハンドルが必要だろうから…、C まで戻らんと出来ないか? うーん、そのうち考えたいなぁ。