メモリがカツカツな時お母さんに片付けろと怒られたい

# 2020年になってもメモリの心配をしながら生きている
8GBのノートPCで、VSCode + 何らかのビルド + Chromeでタブ20個とか開いていると**あっという間にメモリが足りなくなる**。OSごとストールとか大変に厳しいユーザ体験である。

思えば幼少時から**物を出しっぱなしにしては母に叱責を受ける**事が多々あった。
単なるメモリ容量の不足というより、癒やし難い子供の頃からの性分がそもそもの原因かも知れない。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202996/60e40c40-9feb-a3fa-b920-f8f12b8c976d.png" width="200">

もはや自分の振る舞いを矯正するよりも**その都度母に怒られたほうが良さそう**な気がして来た。
というわけで、メモリがカツカツな時デスクトップに不要なソフトを閉じるよう警告を出してくれる**母の如きアプリ**を作成する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202996/b56a3bc1-1b60-9b63-77e3-d70558b85181.gif">



# 環境・使用言語

- Ubuntu18.04 + gnome3環境
- python3 
  - ※コマンドをパイプで繋いでもできる内容だが、ubuntuのシステムでもう既にpython3が動いているからpython3で良いかという消極的理由


# 利用可能なメモリ容量を取得する
Linuxで利用可能なメモリ容量を取得する時、一般的には**freeコマンド**を使う。

```shell
$ free
              total        used        free      shared  buff/cache   available
Mem:        8124668     4831144      267628      201468     3025896     2774924
Swap:       2097148         512     2096636
```
このうち利用可能な実メモリ容量は**Mem行のavailableの列**である。
ざっくり言うとこの値は　**[空き容量 ＋ 使用済みのキャッシュ等すぐに解放可能な容量]** で計算されている（らしい）。

このfreeコマンドの内容は、/proc/meminfo疑似ファイルの内容を整形したものである。
pythonから利用するにはファイルの方が扱いやすいので**/proc/meminfoから情報を拾う**。

```shell
~$ cat /proc/meminfo 
MemTotal:        8124668 kB
MemFree:          328384 kB
MemAvailable:    2640012 kB
Buffers:          740992 kB
・・・(略)
```

インタラクティブシェル上でファイルをOpenしてみる。
split()関数によって改行・空白で区切り、何番目にAvailableが来ているかを確かめる。

```python
>>> f = open('/proc/meminfo')
>>> f.read().split()
['MemTotal:', '8124668', 'kB', 'MemFree:', '318860', 'kB', 'MemAvailable:', '2631456', 'kB', ...]
>>> f.seek(0)
0
>>> f.read().split()[7]
'2625528'
```
**インデックスが7の要素が0.5GB(500,000kB)を切った時に通知を出せば良いわけである。**
（0.5GBの根拠はなんとなくである）

ちなみに、上の例からも分かるようにOpenした後も**Readする度に内容が更新**される。

# デスクトップに通知を飛ばす

notify-sendコマンドを使う。書式は以下の通り。

```shell
$ notify-send -u critical --icon=アイコン名 "タイトル" "メッセージ"
```
- -u には通知の種別を指定する。内容的に怒られなのでcriticalで良いと思う。
- --iconでアイコンを指定できる。カスタムアイコンも指定できる。後述。
- メッセージには簡単なマークアップを使用できる。

## アイコンを作る

いらすとやさんから母親アイコンをお借りして使用する（実写の母親も検討したが迫力があり精神の憔悴を感じた）。

![shikaruhaha.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202996/c9494021-1b8a-80ea-be60-66f3e47354a6.png)

出来上がったアイコンを**shikaruhaha.png**として**/usr/share/pixmap**配下に置く。
(/usr/share/pixmap以外に配置する場合にはpathを指定しなければならない)。
--**iconで指定する際には拡張子を省く。**

```shell
$ notify-send -u critical --icon=shikaruhaha "母の声" "<b>J( 'д')し  たかし！散らかしっぱなしにしてるんじゃないよ！</b>"
```

上を実行するとこうなる。

![notify.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202996/e7981ba2-7c7a-b8d1-6947-db09103afa0c.png)




# コード
```python:memwarn.py
#!/usr/bin/python3
import time
import subprocess

with open("/proc/meminfo") as f:
    while(True):
        f.seek(0)
        arr = f.read().split()
        available = int(arr[7])
        if available < 500000:
            subprocess.call(['notify-send', "-u", "critical", "--icon=shikaruhaha", "母の声", "<b>J( 'д')し  たかし！散らかしっぱなしにしてるんじゃないよ！</b>"])
        time.sleep(2)

```
- 2秒間隔でループ
- f.seek(0)で読み込み位置を強制的に戻す（一度読み込むと読み込み位置が末尾になるため）
- シェルの実行には**subprocess.call**を使用する。シェルのコマンドを空白で区切った文字列のリストを渡す。
- **memwarn.py** というファイル名で実行権限を付与し、**/usr/libexec/**以下に配置する。

# デプロイ
X環境でのログイン後にスタートさせる。
以下のファイルを/etc/xdg/autostart/以下に配置する。ログイン後、自動的に起動するようになる。

```:/etc/xdg/autostart/memwarn.desktop
[Desktop Entry]
Name=MemoryMother
Type=Application
Exec=/usr/libexec/memwarn.py
OnlyShowIn=GNOME;
NoDisplay=true
```

これでおしまい
 

# まとめ

- メモリ増設しろとか言わないで
- いくら何でも依存心が強すぎでは
- **実は"たかし"という名前ではない**

# 参考

- https://ota42y.com/blog/2015/09/09/free-command/
- http://www.nowhere.co.jp/blog/archives/20171004-065710.html
- https://wiki.archlinux.jp/index.php/%E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA