#詳細Linuxカーネル 5章 同期処理を読む
詳細Linuxカーネル 5章 同期処理を読む
Linuxではマルチプロセッサでの割込み・システムコール・例外発生時に、
同じ変数・メモリへのアクセスを適切に同期(順序付け)が必要になる。
5章ではそのためにの同期処理方法を説明している。
今回は、カーネルが使用している同期技法のスピンロックを読む。
スピンロック
スピンロックによる同期の実現は以下の
カーネルは共有しているデータへのアクセス時に資源確保しロックする。
アクセスが終わった時点でロックを解除する。
すでにロックされた状態だと、そのカーネルはロックが解除するまでループして待つ。
スピンロックでは、ロック開放の待ち時間はCPUを消費し続けるので、
無駄に見えるがカーネル資源の多くはロック時間が短いため問題ない。
スピンロックによるデットロックの対策は何かされているのか?
スピンロックの構造体 spinlock_t
スピンロックはspinlock_t
構造体で実現。
構造体の中身はraw_spinlock
構造体とデバッグ用の何か。
slock
:スピンロック状態を示す。1がロック開放状態。break_lock
:ロックによって待っているプロセスがいることを示す。
スピンロックの確保 spin_lock()
スピンロックを確保するための関数 spin_lock()
は、
spinlock_t
構造体のアドレスを引数にとる。
Spin_lock()
は raw_spin_lock()
->_raw_spin_lock()
ー>__raw_spin_lock()
-の順にコール/マクロ化され、実体は__raw_spin_lock
になる。
static inline void __raw_spin_lock(raw_spinlock_t *lock) { preempt_disable(); spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); }
preempt_disable()
をコール
カーネル内のプリエンプションを禁止(5.1章に書いているから後で読む)spin_acuore()
をコール
デバック用の関数っぽい。あとでみるLOCK_CONTENDED
のマクロを実行do_raw_spin_trylock
でスピンロックが取れているか確認。
実体はarch_spin_trylock
。ここでなにかしてるっぽい。
ここまでにするのか・・・
#define LOCK_CONTENDED(_lock, try, lock) \ do { \ if (!try(_lock)) { \ lock_contended(&(_lock)->dep_map, _RET_IP_); \ lock(_lock); \ } \ lock_acquired(&(_lock)->dep_map, _RET_IP_); \ } while (0)
12章 VFS 1/9アップデート
詳細Linuxカーネル 12章 VFSを読む
LinuxはVFSを導入することで、 いろんなファイルシステムを扱うことを可能にしている。
共通ファイルモデルの役割
VFSは共通ファイルモデルに従っている。
共通ファイルモデルで
Linuxのカーネルは各ファイルシステム固有の関数を呼び出し、
ソフト・アプリ側に意識をさせない役割がある。
VFSが取り扱うシステムコール
12.6.1参照
ファイルコピーのプログラムcp()
を通してVFSシステムコールを確認する。
cpが以下のコードで実現できるらしい。(正常系のみ)
nf = open("/floppy/TEST", O_RDONLY, 0);
outf = open("/tmp/test", O_WRONLY | O_CREAT | O_TRUNC, 0600);
do {
len = read(inf, buf, 4096);
write(outf, buf, len);
} while (len);
close(outf);
close(inf);
open() システムコール
open()
の実体はdo_sys_open()
になる。
引数は以下の4つ。
dfd
:filename
:ファイルのパス名flags
:アクセスモードフラグmode
:パーミッションビット
ソースを追うと
builud_open_flags
でflags
とmode
でアクセスモード、フラグをopen_flag
に設定getname
でファイルのパス名を読み取りカーネルがユーザ空間のファイル名を読み取りを実施する。
具体的にはgetname_flags
で__getname
をコールしてkmemからファイルを展開する領域確保する。 そして__getname
で確保した領域に、stryncpy_from_user()
でユーザ空間のファイル名をカーネル空間にコピー。 もしも、ファイル名がEMBEDDED_NAME_MAX
より大きいと、PATH_MAX
で再度コピー。get_unused_fd_flags()
でfd(ファイルディスクリプタ)の空きを確保する。do_filp_opne()
でfile構造体を取得する。以下の順序にて設定
set_nameidata()
でdfd
とファイル名からnameidata
構造体を生成 ー>これを何に使うかはわからなん。path_openat()
がfile構造体を取得するメインの関数。get_empty_filp()
でからのfile構造体を取得。フラグ
open_flag
でfile構造体のファイルフラグを初期化。nameidata
構造体(nd)の初期化とファイル名からPathの検索を実施。 絶対パスの場合、dfd=AT_FDCWD
の場合を考えてる。 ここをもう少し読みたいが、まずはnameidata
構造体を理解すべきか。
fd_install
でflie構造体のポインタをfdに格納する。
12章 VFS
詳細Linuxカーネル 12章 VFSを読む
LinuxはVFSを導入することで、 いろんなファイルシステムを扱うことを可能にしている。
共通ファイルモデルの役割
VFSは共通ファイルモデルに従っている。
共通ファイルモデルで
Linuxのカーネルは各ファイルシステム固有の関数を呼び出し、
ソフト・アプリ側に意識をさせない役割がある。
VFSが取り扱うシステムコール
ファイルコピーのプログラムcp()
を通してVFSシステムコールを確認する。
cpが以下のコードで実現できる。(正常系のみ)
nf = open("/floppy/TEST", O_RDONLY, 0);
outf = open("/tmp/test", O_WRONLY | O_CREAT | O_TRUNC, 0600);
do {
len = read(inf, buf, 4096);
write(outf, buf, len);
} while (len);
close(outf);
close(inf);
open() システムコール
open()
の実体はdo_sys_open()
になる。
+getname
でファイルのパス名を読み取り
+get_unused_fd_flags
で空きを作成
力尽きた・・・
9章プロセスアドレス空間 20151121Linux Kernel 勉強会 #13
詳細Linuxカーネルの9章のプロセスアドレス空間を読んだメモ。
9章 プロセスアドレス空間
ー>カーネルがプロセスから依頼されてメモリ確保の規則
・ユーザモードプロセスへの動的メモリ割り当ては遅延させる
・ユーザモードのプロセスはアドレスバグがあるため、
アドレッシングエラーを補足する
9.1 プロセスのアドレス空間
=プロセスが使用できるすべてのリニアアドレス
カーネルはメモリリージョンをプロセスに付与
+先頭リニアアドレス 4kB単位
+大きさ・サイズ 4KB単位
+アクセス権
カーネルはページフォルト要因を2つに識別
・プログラミングエラーにより発生した
・ページが存在しないために発生した
(カーネルがプロセスに渡したリニアアドレスのページ割当をしていなかった場合?)
9.2 メモリディスクリプタ
=プロセスアドレス空間に関する情報
(持ち主はプロセス?)
メモリディスクリプタの構造体:mm_struct
(プロセスディスクリプタ mmから参照する)
mm_users : mm_struct データを共有する軽量プロセス数
mm_count : メモリディスクリプタ参照数
*カーネルがプロセスのメモリディスクリプタを参照するときには、いずれかのメンバ1加える
9.3 メモリリージョン
vm_area_struct構造体(メモリリ0ジョンディスクリプタ)でメモリリージョンを管理
カーネル側がメモリリージョンをどうやって管理しているか?
9.4 ページフォルト例外ハンドラ
割り込みのハンドラはページフォルト例外発生のアドレスが、
プロセス空間かつメモリリージョンに割当済みであれば、ページ割当を実施。
それ以外はプロセス/カーネル異常とする。
ページフォルト例外ハンドラの”do_page_falut()”を以下で確認していく。
・エラー発生時のリニアアドレスを取得する。
Intelのx86の場合はCR2に格納されている。
unsigned long address = read_cr2(); /* Get the faulting address */
・__do_page_falut()をコールする
割り込み処理中であることを宣言してコール
引数は、pt_regs 構造体のアドレス regs/例外発生時のErrorCode/リニアアドレス(address)
・以下ページフォルトの実体 __do_page_falut
if (unlikely(fault_in_kernel_space(address))) { /* リニアアドレスがカーネル空間でではないことを確認*/ /* address > 0xc0000000 */ if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) { /* ErrorCodeにbit0,2,3にエラーがない場合)*/ if (vmalloc_fault(address) >= 0) /*アドレスがvmalloc空間の場合はメモリ確保? return; if (kmemcheck_fault(regs, address, error_code)) /*?????? */ return; } /* ErrorCodeにbit1にエラーがある場合 */ /* Can handle a stale RO->RW TLB: */ if (spurious_fault(error_code, address)) /* 擬似的なページフォルト*/ return; /* Write時/プレフィチェ時のページレベル違反以外はページ割当を実施*/ /* kprobes don't want to hook the spurious faults: */ if (kprobes_fault(regs)) return; /* * Don't take the mm semaphore here. If we fixup a prefetch * fault we could otherwise deadlock: */ bad_area_nosemaphore(regs, error_code, address); return; }