Solaris のリンカーはファイル単位2016年07月07日 15時28分18秒

Solaris のリンカーがアーカイブファイルからオブジェクトファイルを取得するのはファイル単位だ。ar 等を見ても、静的ライブラリのシンボルを並び替えることはしない。また、複数の関数が一つのファイルに定義されていると、ファイルにある全ての関数が実行形式のファイルに取り込まれる。

さて、ここでのファイル単位とは、コンパイル毎のファイルに相当する。別の言い方をすれば、.o ファイル単位だ。元が、C 言語や Fortran でも大して変わらない。Solaris の .a のアーカイブファイルは .o ファイルを寄せ集めた物になる。動作的には tar に似ている。そのため、.a ファイルから一つの関数を探すことになっていても、全ての .o ファイルを取り込むわけではない。

少し具体例をあげるとする。main 関数が 関数 a を呼ぶとしよう。そして、そして、関数 a、関数 b、関数 c が一つの .c ファイルに実装されて、アーカイブファイルに取り込まれているとする。そうすると、このプログラムをリンクすると、プログラムは不要ではあるが、関数 b と関数 c の実装を取り込むことになる。

リンカーは基本的に全ての未解決シンボルを探そうとする。もし関数 b や関数 c が他の関数を呼んでいると、それらの実装も取り込むことになる。そして、この関数呼び出しの連鎖は終点まで続く。

そのため、Solaris 上では、実装を一つのファイルにあまりに無造作にかつ、大量に詰め込んでライブラリを作成すると、プログラムが肥大化する弊害が起きる。

kmem_size は maxssiz と maxdsiz に影響される2016年03月24日 12時13分24秒

i386 で ZFS を使うと、vm.kmem_size を大きく割り当ててカーネルメモリを増やす必要がある。zfs モジュールを読み込むと 512MB は最低でも割り当てるようにと表示される。

最近気が付いたのだが、kern.maxdsiz と kern.maxssiz が vm.kmem_size に影響するようだ。これらを初期値よりも多めに割り当てていた。残念ながら何故大きくしていたのかを思い出せない。恐らく、wine の為だったのだと思うのだが…。

どちらがどれだけ影響するかは調べていないが、これらの設定を削除した後に、vm.kmem_size がより大きく割り当てられる様になり、それに伴って vfs.zfs.arc_max も大きく設定出来るようになった。

FreeBSD で NFS ファイルを swapon する2016年03月07日 15時46分00秒

折角、NFS ファイルを swapon 出来るので、実演。操作自体はデバイスを swapon するのと何ら変わりは無い。
$ mount -t nfs 192.168.1.1:/mnt/nfs /mnt/nfs
$ dd if=/dev/zero of=/mnt/nfs/192.168.1.2.swap bs=1M count=1024
$ swapon /mnt/nfs/192.168.1.2.swap

swapctl や systat -swap でスワップの状態を調べられるが、スワップファイルの名前は出てこない。

% swapctl -l
Device:       1024-blocks     Used:
/dev/da0s3b     1048576     63116
/dev/#NODEV     1048576     31868
systat は継続的に利用状況を見るのに便利。
% systat -swap
                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average   |

Disk      1K-blocks  Used /0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100
da0s3b      1048448 63756 XXXX
[NFS swap]  1048448 32140 XX
Total       2096896 95896 XXX

FreeBSD で swapon を出来るファイルの種類2016年03月04日 14時14分52秒

FreeBSD では、swapon コマンドで、スワップ領域を追加できる。FreeBSD が書き込み方を制御する方法として、「デバイス」と「NFS ファイル」の二形式がある。

一般的なのは、スワップパーティションを準備して、/etc/fstab に記述する方法。最初のスワップ領域は大体こうして設定する。複数のスワップデバイスを指定することも可能だ。

スワップファイルによる追加の方法もハンドブックにも書かれているが、md デバイスを通してのみ。ファイルへ直接書くわけではなく、md デバイスとしての中継が入る。

どちらの形式にしても、基本的にはデバイスを操作しての読み書きとなる。

NFS 上のファイルを swapon をすると特別に扱われる。ディスクレスブート等をしたときにも使われる。これらの処理は VOP を通して行っている。これらのファイルは swapctl 等でもデバイスとは異なった表示がされる。

これらの処理は、swap_pager.c に記述されている。この NFS ファイルの処理は、swapon 時のみにファイルシステムを点検しているのみなので、ufs でも利用可能な望みはある。後で、実験してみよう。

FreeBSD のスワップの仕組みを追う - swap_pager_putpages と vm_pageout_cluster2016年02月17日 15時02分03秒

最近、FreeBSD の swapping の実装を調べている。

pagger の実装は各サブシステム毎に実装され、struct pageops を経て、vm から実装される。スワップデバイスへの書き出しは、pgo_putpages の動作の一つ。swappagerops は swap_pager_putpages 関数が設定されている。

swap_pager_putpages() 関数が、実際のスワップデバイスへの書き出しを行う最終関数。既に、何を書き出すかが決められていて、その vm page が渡される。一回に書き出されるページ数も上限があって、BLIST_MAX_ALLOC 以下で無ければならない。この値は FreeBSD 11 CURRENT 現在の値で 32。また、他にも一回の書き出しの限界は nsw_cluster_max が min((MAXPHYS/PAGE_SIZE), MAX_PAGEOUT_CLUSTER) と設定されており、MAX_PAGEOUT_CLUSTER は初期値では 16 となっている。

また、vm がスワップを行うときに、連続した vm page を一括して書き出すように努めている。swap_pager_putpages() 関数がその仕事を行う。vm_pageout_page_count がその時に、一回にまとめる数で、VM_PAGEOUT_PAGE_COUNT は 16 に設定されている。

FreeBSD 10.x の systatm -vm2015年10月16日 16時20分49秒

FreeBSD 10 系での systat -vm の画面。
    2 users    Load  0.34  0.48  0.25                  Oct 13 13:53

Mem:KB    REAL            VIRTUAL                       VN PAGER   SWAP PAGER
        Tot   Share      Tot    Share    Free           in   out     in   out
Act  113596   17720   703456    25104 1672360  count   112
All  239340   20096  1371300    36548          pages  2099
Proc:                                                            Interrupts
  r   p   d   s   w   Csw  Trp  Sys  Int  Sof  Flt    110 ioflt   373 total
  2          74      2088 5961 6412  373   36 5765    225 cow      11 atkbd0 1
                                                     4705 zfod        acpi0 9
 4.2%Sys   0.6%Intr  5.4%User  1.2%Nice 88.7%Idle       5 ozfod       psm0 12
|    |    |    |    |    |    |    |    |    |           %ozfod       ata0 14
==>>>-                                                    daefr       wpi0 uhci3
                                       108 dtbuf      130 prcfr   216 hpet0 fxp0
Namei     Name-cache   Dir-cache    111566 desvn      301 totfr    21 uhci0 ehci
   Calls    hits   %    hits   %      4687 numvn      262 react       hdac0 256
    1582    1532  97                  2896 frevn          pdwak   125 ahci0 257
                                                          pdpgs
Disks   md0  ada0   da0   cd0 pass0 pass1 pass2        85 intrn
KB/t   0.00 68.16  6.67  0.00  0.00  0.00  0.00    229548 wire
tps       0   127     5     0     0     0     0     70256 act
MB/s   0.00  8.48  0.03  0.00  0.00  0.00  0.00     54656 inact
%busy     0    97     3     0     0     0     0      3248 cache

FreeBSD 10.1 RELEASE で vt コンソール2015年01月13日 11時18分47秒

前降りにも書いたが、FreeBSD 10.1 RELEASE の ports の XOrg を使おうとすると、vt コンソール が必須になる。

FreeBSD 10.1 では、まだ以前の sc コンソールが初期値なっていて、/boot/loader.conf で vt コンソールに変更する必要がある。11-CURRENT では既に vt コンソールがデフォルトになっている。

man vt でも出てくるが、/boot/loader.conf で変更できるのは二つ。


hw.vga.textmode="1"
kern.vty="vt"

kern.vty にて、vt コンソールに変更する。hw.vga.textmode にすると従来の vga を用いたコンソールになり、見た目はほぼ一緒になる。しかし、Xorg を起動すると vga の設定が変更されてしまうようで、画面が変わってしまう。

VT: replacing driver "vga" to new "fb".

と出ている。

使った感じとしては vt コンソールは高速のスクロールに違和感があり、また遅い感じがする。大抵の場合は screen を通して使っているので、気にはならない。

KVA_PAGES は繊細な設定2015年01月12日 04時16分41秒

FreeBSD KVA_PAGES でもっとメモリを割り当てるにて、KVA_PAGES の値を 1024 にまで増やした。なお、実験には FreeBSD 10.1 RELEASE を用いている。

4GB のメモリが搭載されていた機械では問題なく起動する。そして、PAE を使う理由でもある。

試しに、物理メモリが 2GB の機械と 1GB の機械でもこのカーネルを試してみた。なんと、1GB の方はディスクを認識し終ったあたりで、再起動がかかってしまう。2GB の方はカーネルがプロセスを生成することができなくなり、init の起動に失敗してしまう。以下がそのときのカーネルパニックの様子。おそらく 1GB の方も同じ問題で失敗している。


kdb_backtrace
panic
exit1
kern_execve
sys_execve
start_init
fork_exit
fork_trampoline

4GB のメモリが入っていない機械で PAE を使っても利益は無いので、直接的な被害は無い。

FreeBSD PAE で ZFS に KVA_PAGES でもっとメモリを割り当てる2014年12月30日 14時02分52秒

FreeBSD の PAE カーネルで ZFS モジュールを使うで PAE カーネルにて ZFS を使えるようにはなっていた。しかし、これだけではカーネルメモリの割り当ては非 PAE からは増えない様だ。

カーネルへの仮想メモリ領域の割り当てを増やすのに KVA_PAGES を設定する必要がある。

sys/i386/conf/NOTE に以下のように書いてある。

# Change the size of the kernel virtual address space.  Due to
# constraints in loader(8) on i386, this must be a multiple of 4.
# 256 = 1 GB of kernel address space.  Increasing this also causes
# a reduction of the address space in user processes.  512 splits
# the 4GB cpu address space in half (2GB user, 2GB kernel).  For PAE
# kernels, the value will need to be double non-PAE.  A value of 1024
# for PAE kernels is necessary to split the address space in half.
# This will likely need to be increased to handle memory sizes >4GB.
# PAE kernels default to a value of 512.
#
options         KVA_PAGES=260
ただ、PAE カーネルでのデフォルトの値は記述と違い変わっていないようだ。両方とも 260 になっている。10.1 RELEASE で確認した。PAE カーネルを作って、大きい tmpfs 等を使っていたが、zfs もメモリの割り当てが小さいので気が付いた。

取り敢えず、KVA_PAGES=1024 で試した所、kmem の割り当てが増えているのが確認できた。sysctl で変更前と変更後を比較した。


@@ -2402,15 +2492,15 @@
 vm.v_pageout_free_min: 34
 vm.swap_enabled: 1
 vm.md_malloc_wait: 0
-vm.kmem_size: 429916160
+vm.kmem_size: 859832320
 vm.kmem_zmax: 65536
 vm.kmem_size_min: 12582912
-vm.kmem_size_max: 429916160
+vm.kmem_size_max: 859832320
 vm.kmem_size_scale: 3
-vm.kmem_map_size: 47108096
-vm.kmem_map_free: 382808064
+vm.kmem_map_size: 30310400
+vm.kmem_map_free: 829521920
 vm.swap_total: 2140618752
-vm.swap_reserved: 1034051584
+vm.swap_reserved: 929083392
 vm.overcommit: 0
 vm.swzone: 36175872
 vm.swap_maxpages: 4194304
...
 vfs.tmpfs.rename_restarts: 0
 vfs.tmpfs.memory_reserved: 4194304
-vfs.zfs.arc_max: 268697600
-vfs.zfs.arc_min: 33587200
+vfs.zfs.arc_max: 536870912
+vfs.zfs.arc_min: 67108864
 vfs.zfs.arc_average_blocksize: 8192
 vfs.zfs.arc_free_target: 6655
 vfs.zfs.arc_meta_used: 0
-vfs.zfs.arc_meta_limit: 67174400
+vfs.zfs.arc_meta_limit: 134217728
 vfs.zfs.l2arc_write_max: 8388608
 vfs.zfs.l2arc_write_boost: 8388608
 vfs.zfs.l2arc_headroom: 2

kmem と zarc がほぼ二倍の大きさになっている。

FreeBSD truss を使ってシステムコールを追跡2014年12月10日 13時00分11秒

FreeBSD にもかなり前からシステムコールを追跡する truss コマンドが存在する。Solaris のものを参考に書かれたようだ。ktrace にかなり類似している。

truss は初期値では、出力が標準エラーに出される。折角なので、ktrace と比べてみる。


% truss echo |& less
mmap(0x0,32768,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 671481856 (0x28060000)
issetugid(0x2805f500,0xbfbfefc6,0x20,0x0,0x0,0x0) = 0 (0x0)
lstat("/etc",{ mode=drwxr-xr-x ,inode=22,size=2560,blksize=16384 }) = 0 (0x0)
lstat("/etc/libmap.conf",{ mode=-rw-r--r-- ,inode=908,size=109,blksize=16384 }) = 0 (0x0)
open("/etc/libmap.conf",O_CLOEXEC,027757756764)  = 3 (0x3)
fstat(3,{ mode=-rw-r--r-- ,inode=908,size=109,blksize=16384 }) = 0 (0x0)
mmap(0x0,109,PROT_READ,MAP_PRIVATE,3,0x0)        = 671514624 (0x28068000)
close(3)                                         = 0 (0x0)
lstat("/usr",{ mode=drwxr-xr-x ,inode=2,size=512,blksize=16384 }) = 0 (0x0)
lstat("/usr/local",{ mode=drwxr-xr-x ,inode=2,size=1024,blksize=16384 }) = 0 (0x0)
lstat("/usr/local/etc",{ mode=drwxr-xr-x ,inode=471040,size=2560,blksize=16384 }) = 0 (0x0)
lstat("/usr/local/etc/libmap.d",0xbfbfc4f0)      ERR#2 'No such file or directory'
munmap(0x28068000,109)                           = 0 (0x0)
open("/var/run/ld-elf.so.hints",O_CLOEXEC,00)    = 3 (0x3)

まず気が付くのは出力の違い。truss の方では関数の戻り値が呼び出しと同じ行に書かれるのに対し、trace では別の行になっている。

以下のものは、ktrace のもの。同じファイル名が同じ順で出てくるのが見て取れる。


  1368 echo     CALL  lstat(0x28065000,0xbfbfd138)
  1368 echo     NAMI  "/etc"
  1368 echo     STRU  struct stat {dev=99, ino=22, mode=040755, nlink=...
  1368 echo     RET   lstat 0
  1368 echo     CALL  lstat(0x28065000,0xbfbfd138)
  1368 echo     NAMI  "/etc/libmap.conf"
  1368 echo     STRU  struct stat {dev=99, ino=908, mode=0100644, nlink=...
  1368 echo     RET   lstat 0
  1368 echo     CALL  open(0x28065000,0x100000,0xbfbfddf4)
  1368 echo     NAMI  "/etc/libmap.conf"
  1368 echo     RET   open 3
  1368 echo     CALL  fstat(0x3,0xbfbfddd8)
  1368 echo     STRU  struct stat {dev=99, ino=908, mode=0100644, nlink=...

若干の違いはあるにしても、どちらも同じことを出来る。関数や引数によって見やすい方が違うので、適材適所で使い分けるのが賢明に思われる。