差分

移動先: 案内検索

記憶管理

3,350 バイト追加, 2018年1月16日 (火) 10:07
/* Linuxのスワップ */
この書き込みや読み込みをする単位をページといい、サイズは32ビットアーキテクチャーのCPUは4KBのページサイズで、64ビットアーキテクチャーのCPUは8KBのものがほとんどです。<ref>これらはCPUのアーキテクチャーに依存するので異なる場合があります。例えばIBM POWER5+ や POWER6 プロセッサーは64KBのページサイズも利用することが出来ます。これらはCPUのアーキテクチャーに依存するので異なる場合があります。たとえばPowerPCは64KBのページサイズも利用することが出来ます、そのためパフォーマンスのために64KBのページサイズとして使う場合もあります。64KB pages on Linux for Power systems - http[https://www.ibm.com/developerworks/community/wikis/displayhome?lang=en#!/hpccentralwiki/Welcome%20to%20High%20Performance%20Computing%20%28HPC%29%20Central/page/64KB+%20pages%20on%20Linux%20for%20Power%20systems 64KB pages+on+Linux+for+Power+systems(IBMサイト)]
</ref>
実メモリに入りきらないものを外部記憶装置に書き出し、必要になったら実メモリに読み込もうというのが仮想記憶です。
実メモリアドレス空間より仮想アドレス空間よりが大きい状態になっていれば、実メモリ上にない仮想アドレスを要求することが発生します。
例えば32ビットアーキテクチャーの場合(2<sup>32</sup> / 4GB ) は仮想記憶空間として1048576(2<sup>20</sup>)のページを持つことになります。もし512MBの物理メモリしか搭載していないマシンは内部で131072ページしか持っていません。のページを持つことになります。<ref>IA-32アーキテクチャで物理アドレス拡張(PAE)を使うことはひとまずおいておきます。</ref>もし512MBの物理メモリしか搭載していないマシンは内部で131072ページしか持っていません。
;調べてみよう: 本来のスワップを持っているオペレーティングシステムには、どんなものがあるだろうか。
 
 
;補足: スワップの具体的な運用に関する私見 [[スワップの運用について考えてみる]]
=== Linuxのスワップ ===
一般的にはハードディスク上にスワップ用のパーティションを取って、そこをスワップ先に指定します。多くの場合はインストール時にセットアップするようになっています。通常はパーティションをマウントするための情報を書く/etc/fstabにスワップパーティションの割り当ての記述が作られているはずです。
<pre> /dev/hda3 none swap sw ,pri=0 0 0</pre>
スワップの状態は/proc/swapsを見るとわかります。下の例はサイズが1453872K (1.45GB)バイトである/dev/hda3パーティションがスワップに使われているという意味です。
<pre class="bash">
% cat /proc/swaps
Filename Type Size Used Priority
/dev/hda3 partition 1453872 0 -1
</pre>
たとえばswapf01という名前のスワップファイルを作ってシステムにスワップファイルとして登録する時は次のようにします。
<pre>
# /bin/dd if=/dev/zero of=swapf01 bs=8192 count=256
# /sbin/mkswap swapf01 2048
# sync
# swapon swapf0
</pre>
どのファイルはハードディスクのパーティションであっても構いません。複数のハードディスクを同時に利用している時、スワップファイルを各々のハードディスク上に設定しスワップのプライオリティを同じ値にすることで、スワップディスクへの読み込み/書き込みなどディスクI/Oが分散させることができます。これはraid0のような効果となり高速化することになります。プライオリティを指定して、例えば高速なハードディスク、あるいはスワップファイルアクセス時にディスクスラッシングを避けるために作業しているハードディスクとは物理的に別のハードディスクをアクセスするようにするなどといったことも可能です。
 
 
;補足: Linux 2.6.32でスワップについて試したことのメモランダム [[linuxのswapについて私が知っている二、三の事柄]]
== 局所参照性 ==
* プログラム: malloctest.c<syntaxhighlight lang='C' line="1" >
#include <stdlib.h>
main() { char *p; size_t areasize=1024*1024*512; if ((p=(char *)malloc(areasize)) == NULL) { perror("malloc"); } sleep(10); free(p); }</syntaxhighlight> * 実行例:  <pre class="bash">
$ cc malloctest.c
$ ./a.out &
---この並びは下の様になっています---
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
</pre>
次の ps コマンドを実行してみましょう。するとRSSでソートされて、USER / COMMAND / RSS / VSZの順で表示されます。
<pre class="bash">
$ ps -Ao user,args,rss,vsize --sort rss
USER COMMAND RSS VSZ root [keventd] 0 0 root [ksoftirqd_CPU0] 0 0
...
hironobu emacs20 -geometr 9076 11112/usr/bin/emacs23 15732 42140 canna hironobu gedit 23204 173232 hironobu /usropt/sbingoogle/chrome/chrome - 107380 359600</cannas 17540 19152pre>
keventdやksoftirqd_CPU0などRSSとVSZが両方0のものはカーネルレベルで動いているスレッドです。ここではユーザレベルのプロセスであるemacs20と/usr/sbin/cannaserverのメモリの使い方に着目しましょう。emacs20はRSSは9076KBで、仮想記憶も含めたサイズは11112KBです。多くの場合、十分に実メモリに余裕があっても、プロセス中で生成された記憶空間がすべて実メモリの上に載っているわけではありません。必要のあるもののみ実メモリ上にページインされます。CD-ROMから立ち上げるため仮想記憶のスワップ先がないKNOPPIXでも同じです。RSSもVSZも2つとも表示されて両者に差があります。keventdやksoftirqd_CPU0などRSSとVSZが両方0のものはカーネルレベルで動いているスレッドです。ここではユーザレベルのプロセスであるemacs23のメモリの使い方に着目しましょう。emacs20はRSSは15732KBで、仮想記憶も含めたサイズは42140KBです。多くの場合、十分に実メモリに余裕があっても、プロセス中で生成された記憶空間がすべて実メモリの上に載っているわけではありません。必要のあるもののみ実メモリ上にページインされます。
;調べてみよう: vmstatで観察してみましょう。vmstatは仮想記憶のステータス観察するためのツールです。1秒毎に表示するオプションで動作させながら、先程のプログラムを改造し徐々に記憶を取るようなプログラムにして動かし、観察してみましょう。
<pre class="bash">
% vmstat 1 <- 1秒毎に表示
procs memory swap io system cpu
2 1 0 29964 1140 368 2128 316 124 79 310 485 817 37 9 54
...
</pre>
 
== コピーオンライトとその実際 ==
 
 
Linuxのカーネルではプロセスが親から引き継がれたメモリ領域や実行コードなどを引き継いでいても、そこに書き換えが発生するまで、実際のメモリ領域はとりません。書き込みがあって始めてメモリ領域を確保し、中身をコピーして別なものにします。このことをコピーオンライト(CoW: Copy on Write)といいます。このような仕組みにより、高速に新しいプロセスを生成したり、あるいはメモリの効率的な利用が出きるようにしています。
 
 
/proc/(プロセスid)/smapsは、カーネル内の該当プロセスのメモリ利用状況を表示するAPIです。それを使って実験したいと思います。
dashはDebian版軽量シェルでシステムのシェルスクリプトを実行するのに使われます。
さて、$$はシェル自身のプロセスidですので、/proc/$$/smapsとすると現在使っているシェルのメモリ利用状況がわかります。
注目するのは Shared_Clean と Private_Clean の値です。
Shared_Clean はシェアしているメモリです。
Private_Clean は自らのメモリです。
クリーンな、という意味は、まだそのページは変更されていない(書き込みがおこっていない)という意味です。
まず最初、dashを起動してdashシェル環境でメモリ利用状況をみます。
この時、引き継ぐものがないのでPrivate_Cleanが76kB (= Rssの値と同じ)、Shared_Cleanが0kBとなっています。
次にdashの中でさらにdashを起動します。つまり最初のdashが親プロセスとなった新しいdashです。
この上で両方の値をみるとPrivate_Cleanが0kB、Shared_Cleanが76kB(= Rssの値と同じ)となっています。
つまり、子プロセス側となったdashのメモリは、この時点ではシェアしているものを使っていることがわかります。
 
<pre class="bash">
$ dash
$ cat /proc/$$/smaps | head -9
08048000-08060000 r-xp 00000000 08:01 4825 /bin/dash
Size: 96 kB
Rss: 76 kB
Pss: 76 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 76 kB
Private_Dirty: 0 kB
Referenced: 76 kB
$ dash
$ cat /proc/$$/smaps | head -9
08048000-08060000 r-xp 00000000 08:01 4825 /bin/dash
Size: 96 kB
Rss: 76 kB
Pss: 34 kB
Shared_Clean: 76 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 76 kB
</pre>
== mmap ==
* プログラムmmap.c <syntaxhighlight lang='C' line="1" >#include <stdio.h>#include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> main() { int fd; struct stat st; char *p; int i; fd=open("dat",O_RDWR); fstat(fd, &st); p=mmap(0,st.st_size,(PROT_READ|PROT_WRITE),MAP_PRIVATE,fd,0); for(i=0; i < st.st_size -1 ; i++) { printf("[%c]",p[i]); } close(fd); }</syntaxhighlight> * 実行<pre class="bash">
$ echo 'abcdefg' > dat
$ cc mmap.c
$ ./a.out
[a][b][c][d][e][f][g]$
</pre>