ア | イ | ウ | エ | オ |
カ | キ | ク | ケ | コ |
サ | シ | ス | セ | ソ |
タ | チ | ツ | テ | ト |
ナ | ニ | ヌ | ネ | ノ |
ハ | ヒ | フ | ヘ | ホ |
マ | ミ | ム | メ | モ |
ヤ | ユ | ヨ | ||
ラ | リ | ル | レ | ロ |
ワ | ヰ | ヴ | ヱ | ヲ |
ン |
A | B | C | D | E |
F | G | H | I | J |
K | L | M | N | O |
P | Q | R | S | T |
U | V | W | X | Y |
Z | 数字 | 記号 |
オペレーティングシステム(OS)のカーネルが稼働しているアドレス空間のこと。
オペレーティングシステムにも様々なものがあるが、一般的なものは二つの「層」に分けることができる。
そのうち下位の層にあり、よりハードウェアに近い処理、機能を担うのがカーネルで、このカーネルが動作する層をカーネル空間と呼ぶ。対するはユーザー空間である。
アドレス空間をどのように分けて扱うかは、OSごとに様々である。パーソナルコンピューター用OSと、組み込み機器用OSとでは、CPUの違いなどもあり、かなり構造が異なる。
組み込み用の例として、SuperH用のNetBSDの実装を説明する。
SuperHでは、アドレス空間4Giバイトを二分割し、上位2Giバイトをユーザー空間、下位2Giバイトをカーネル空間とする。
SuperHの場合、0x00000000〜0x1fffffffの512Miバイト程度をよく用い、この中に様々なものが割り付けられる。この空間は、MMUの有無、キャッシュの有無等の各条件で、0x80000000〜0xdfffffffの範囲にもマッピングされている。
NetBSDの実装はこの特徴を用いており、下位の領域のうちストアキュー領域以外は特権モード以外では利用できないように設定し、ここをカーネル空間としている。
P3領域であれば、通常のP0/U0領域からのアクセスと何ら変わらない環境となり、またフラッシュメモリーの書き換え処理のようにキャッシュがあっては邪魔な場合はP2領域にアクセスすれば良いことになる。
なお、物理的なメモリーはユーザー領域、カーネル領域とも同じものであるので、この管理もCPUとOSがすることになる。
パーソナルコンピューター用のLinux/i386やFreeBSD/i386の場合、仮想アドレス空間4Giバイトを二分割し、次のようにしている。
実際の物理メモリーはこれよりも少ないことから、OSが、仮想アドレスを適当な物理アドレスへとマッピングすることになる。従って、仮想空間上は有効なページであっても、そのページに物理メモリーが割り当てられているとは限らない。
ディスクドライブのファイルブロックやSWAPを指していたり、あるいは実際にアクセスされたときに0クリアされたメモリーを割り当てる指定になっていることもある。
カーネル空間からユーザー空間への通知は、OSごとに異なり、またOSのカーネルバージョンごとに異なる可能性もあるが、何らかのシステムコールは存在する。
Linuxの場合、カーネルからユーザー空間のプロセスに対して任意のシグナルを送ることができる。プロセスを識別するためにユーザー空間でのPID(プロセスID)を用いるため、送信に際して予め宛先のPIDを知る必要があり、何らかの方法で情報を伝える手段を用意する必要がある。
一般的には/dev以下、あるいはsysfsやdebugfsなど手頃な場所にデバイスを用意し、ユーザー空間からPIDをioctlやwriteしてカーネルに伝える手法がよく取られている。
以下は実際の送信処理のみを記載するが、このほかに、デバイスを作り、PIDをユーザーランドから得る処理が必要である。
#include <asm/siginfo.h> // siginfo #include <linux/rcupdate.h> // rcu_read_lock #include <linux/sched.h> // find_task_by_pid_type #define SIG_NO (SIGRTMIN+10) // 他と衝突しないものを使う(SIGRTMAXを超えないこと) static pid_t userland_pid; static int sendsig_sample(void) { struct siginfo si; struct task_struct *t; memset(&si, 0, sizeof(struct siginfo)); si.si_signo = SIG_NO; si.si_code = SI_QUEUE; si.si_int = 0; /* 任意の32ビット値を一つだけ通知可能 */ rcu_read_lock(); t = find_task_by_vpid(userland_pid); /* 送信先のPIDが格納されているものとする */ rcu_read_unlock(); if (t == NULL) return -ENODEV; return send_sig_info(SIG_NO, &si, t); }
より新しいLinuxカーネルでは、find_task_by_vpid ではなく find_task_by_pid_type など違う関数になっている可能性がある。
ユーザーランドでは、シグナルハンドラーを定義するだけである。シグナル番号はカーネルと一致させる。
#define SIG_NO (SIGRTMIN+10) // カーネルと同じものを使う struct sigaction sa; sa.sa_sigaction = signal_handler; sa.sa_flags = SA_SIGINFO; sigaction(SIG_NO, &sa, NULL);
シグナルハンドラーは以下の形式である。
void signal_handler(int n, siginfo_t *si, void *dummy) { /* ここに処理を追加 */ }
このほかに、デバイスに対してPIDをioctlまたはwriteするための処理が別途必要になる。
ユーザー空間から、カーネル空間に直接アクセスする術は無いのが一般的である。カーネル空間にアクセスすれば、通常はアドレスエラーが発生する。
そこで、カーネル空間にデバイスドライバー等を用意し、このデバイスを経由してカーネル空間にアクセスすることになる。
カーネル空間から、ユーザー空間に直接アクセスできるかどうかは実装による。カーネルそのものであれば特権があるためアクセスは自在であろうが、カーネル空間で動くデバイスドライバーとなると、若干権限が落とされており、出来ないことが多いようである。
そこで、ユーザー空間の領域にアクセスするための関数が用意される。この仕様はOSごとに様々であり、互換性はない。
NetBSDでは、copy関数群、fetch関数群、store関数群という関数が用意されている。これらは、FreeBSDでも同様と思われる。
fetch/storeは、1バイト〜1ワードまでの単位でのアクセス手段を提供し、copy関数群はデータ列、文字列単位でのアクセス手段を提供する。
ユーザー空間とカーネル空間をまたぐ、memcpyやstrncpy相当の関数群である。
#include <sys/systm.h>
int copyin(const void *uaddr, void *kaddr, size_t len);
int copyout(const void *kaddr, void *uaddr, size_t len);
int copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done);
int copystr(const void *kfaddr, void *kdaddr, size_t len, size_t *done);
具体的な機能は次の通り。
ユーザー空間からカーネル空間にデータを取り込む関数群である。
#include <sys/types.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
int fubyte(const void *base);
int fusword(void *base);
int fuswintr(void *base);
int fuword(const void *base);
具体的な機能は次の通り。
カーネル空間からユーザー空間にデータを格納する関数群である。
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
int subyte(void *base, int byte);
int susword(void *base, int word);
int suswintr(void *base, int word);
int suword(void *base, int word);
具体的な機能は次の通り。
Linuxでは、単純なアクセス関数、ブロック関数、文字列関数などが用意されている。
Linux 2.1.x以降で概ね共通。Linux 2.0.xでは仕様が異なる。現在ではLinux 2.6、ときにLinux 2.4程度まで古いものに対応すれば充分なので、古いものは無視しても問題にならないだろう。
ユーザー空間とカーネル空間の間で整数値(1、2、4バイト)をfetch/storeする機能である。
#include <asm/uaccess.h>
int get_user(var, ptr);
int put_user(var, ptr);
おそらくマクロとして定義されており、sizeof(*(ptr))の大きさ(1、2、4)に合わせて動作することになる。get時は結果はvarに得られる。put時はvarが書き込まれる。
ユーザー空間とカーネル空間をまたぐ、memcpy相当の関数群である。
#include <asm/uaccess.h>
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
long strncpy_from_user(char *dst, const char __user *src, long count);
具体的な機能は次の通り。
コメントなどを投稿するフォームは、日本語対応時のみ表示されます