MEMFD_CREATE(2) | Linux Programmer's Manual | MEMFD_CREATE(2) |
memfd_create - 無名ファイル (anonymous file) を作成する
#include <sys/memfd.h>
int memfd_create(const char *name, unsigned int flags);
memfd_create() は、 無名ファイル (anonymous file) を作成し、 そのファイルを参照するファイルディスクリプターを返す。 このファイルは通常のファイルと同様に振る舞い、 変更、切り詰め (truncate)、 メモリーマップなどを行うことができる。 しかし、 通常のファイルとは違い、 このファイルは RAM 上に置かれ、 格納されるストレージは揮発性である。 このファイルへの参照がすべてなくなると、 ファイルは自動的に解放される。 このファイルが置かれるページには無名メモリー (anonymous memory) が使用される。 したがって、 memfd_create() で作成されたフィアルは、 他の無名メモリーの割り当て (MAP_ANONYMOUS フラグ付きの mmap(2) を使って割り当てられた無名メモリーなど) と同じ動作をする。
ファイルの初期サイズは 0 に設定される。 呼び出しの後に、 ftruncate(2) を使ってファイルサイズを設定すべきである (代わりに、 write(2) や同様の関数を呼び出してファイルにデータを書き込むこともできる)。
name に指定された名前はファイル名として使用され、 ディレクトリ /proc/self/fd/ で対応するシンボリックリンクのリンク先として表示される。 表示される名前の前には常に memfd: が付き、 この名前はデバッグ用途としてのみ機能する。 名前はファイルディスクリプターの動作には影響せず、 複数のファイルが同じ名前を持っても副作用はない。
以下の値をビット論理和で flags に指定して、 memfd_create() の動作を変更できる。
flags の未使用のビットは 0 でなければならない。
返り値として memfd_create() は、 作成したファイルを参照するのに使用できる新しいファイルディスクリプターを返す。 このファイルディスクリプターは読み書き両用 (O_RDWR) でオープンされ、 O_LARGEFILE がこのファイルディスクリプターにセットされる。
fork(2) と execve(2) に関しては、 memfd_create() で作成したファイルディスクリプターについても通常の動作が適用される。 ファイルディスクリプターのコピーは fork(2) で生成される子プロセスに継承され、 同じファイルを参照する。 close-on-exec フラグがセットされていない限り、 execve(2) の前後でファイルディスクリプターは保持される。
成功の場合、 memfd_create() は新しいファイルディスクリプターを返す。 エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。
memfd_create() システムコールは Linux 3.17 で初めて登場した。 GNU C ライブラリでのサポートは検討中である。
memfd_create() システムコールは Linux 固有である。
memfd_create() システムコールは、 手動で tmpfs ファイルシステムをマウントして、 そのファイルシステムにファイルをオープンするという操作の、 簡単な代替手段を提供している。 memfd_create() の主な目的は、 fcntl(2) が提供する file-sealing API で使用できる、 ファイルとそれに関連付けられるファイルディスクリプターを作成することである。
memfd_create() システムコールは、 file sealing なしでも用途がある (これが明示的に MFD_ALLOW_SEALING フラグが要求されない限り、 file-sealing が無効になる理由である)。 特に、 ファイルシステムに実際にファイルを残す意図がない場合、 tmp にファイルを作成したり open(2) O_TMPFILE を使ったりする際の代替手段として使用できる。
file sealing がない場合、 共有メモリー経由で通信するプロセスは、 互いに信頼するか、 信頼していない相手が共有メモリー領域を問題がある方法で操作する可能性に対処するための対策を講じなければならない。 例えば、 信頼していない相手は、 いつでも共有メモリーの内容を変更したり、 共有メモリー領域を縮小したりする可能性がある。 前者の場合は、 ローカルプロセスでは、 データの確認時点と使用時点の競合条件の問題が起こり得る (通常はこの問題への対処は共有メモリー領域からデータをこぴーしてからデータを確認、使用することである)。 後者の場合は、 ローカルプロセスでは、 共有メモリー領域の存在しなくなった場所にアクセスしようとした際にシグナル SIGBUS が発生する可能性がある (この可能性に対処するにはシグナル SIGBUS に対してハンドラーを使用する必要がある)。
信頼していない相手への対処により、 共有メモリーを利用するコードに余計な複雑性が増すことになる。 メモリー sealing により余計な複雑性をなくすことができる。 相手が望まない方法で共有メモリーを変更できないことを知っていることで、 プロセスは安全に動作できるようになる。
sealing 機構の使い方の例は以下のとおりである。
以下では memfd_create() と file sealing API の使用例を示すサンプルプログラムを 2 つとりあげる。
最初のプログラム t_memfd_create.c は、 memfd_create() を使って tmpfs ファイルを作成し、 そのファイルのサイズを設定し、 メモリーにマッピングし、 要求された場合にはそのファイルに seal を設定する。 このプログラムは最大で 3 つのコマンドライン引き数を取り、 最初の 2 つは必須である。 最初の引き数はファイルに関連付けられる名前で、 2 番目の引き数はファイルに設定されるサイズである。 省略可能な 3 番目の引き数は、 このファイルに設定する seal を指定する文字列である。
2 つめのプログラム t_get_seals.c を使うと、 memfd_create() を使って作成された既存のファイルをオープンし、 そのファイルに適用されている seal の集合を調査できる。
以下のシェルのセッションはこれらのプログラムの使用例を示したものである。 まず tmpfs ファイルを作成し、そのファイルに seal をいくつか設定している。
$ ./t_memfd_create my_memfd_file 4096 sw & [1] 11775 PID: 11775; fd: 3; /proc/11775/fd/3
この時点では、 t_memfd_create プログラムはバックグラウンドで動作し続ける。 もう一つのプログラムから、 memfd_create() がオープンしたディスクリプターに対応する /proc/PID/fd ファイルをオープンすることで、 memfd_create() で作成されたファイルのファイルディスクリプターを取得できる。 そのパス名を使って、 /proc/PID/fd シンボリックリンクの内容を調査し、 t_get_seals プログラムを使ってそのファイルに設定されている seal を見ることができる。
$ readlink /proc/11775/fd/3 /memfd:my_memfd_file (deleted) $ ./t_get_seals /proc/11775/fd/3 Existing seals: WRITE SHRINK
#include <sys/memfd.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0) int main(int argc, char *argv[]) {
int fd;
unsigned int seals;
char *addr;
char *name, *seals_arg;
ssize_t len;
if (argc < 3) {
fprintf(stderr, "%s name size [seals]\n", argv[0]);
fprintf(stderr, "\t'seals' can contain any of the "
"following characters:\n");
fprintf(stderr, "\t\tg - F_SEAL_GROW\n");
fprintf(stderr, "\t\ts - F_SEAL_SHRINK\n");
fprintf(stderr, "\t\tw - F_SEAL_WRITE\n");
fprintf(stderr, "\t\tS - F_SEAL_SEAL\n");
exit(EXIT_FAILURE);
}
name = argv[1];
len = atoi(argv[2]);
seals_arg = argv[3];
/* Create an anonymous file in tmpfs; allow seals to be
placed on the file */
fd = memfd_create(name, MFD_ALLOW_SEALING);
if (fd == -1)
errExit("memfd_create");
/* Size the file as specified on the command line */
if (ftruncate(fd, len) == -1)
errExit("truncate");
printf("PID: %ld; fd: %d; /proc/%ld/fd/%d\n",
(long) getpid(), fd, (long) getpid(), fd);
/* Code to map the file and populate the mapping with data
omitted */
/* If a 'seals' command-line argument was supplied, set some
seals on the file */
if (seals_arg != NULL) {
seals = 0;
if (strchr(seals_arg, 'g') != NULL)
seals |= F_SEAL_GROW;
if (strchr(seals_arg, 's') != NULL)
seals |= F_SEAL_SHRINK;
if (strchr(seals_arg, 'w') != NULL)
seals |= F_SEAL_WRITE;
if (strchr(seals_arg, 'S') != NULL)
seals |= F_SEAL_SEAL;
if (fcntl(fd, F_ADD_SEALS, seals) == -1)
errExit("fcntl");
}
/* Keep running, so that the file created by memfd_create()
continues to exist */
pause();
exit(EXIT_SUCCESS); }
#include <sys/memfd.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0) int main(int argc, char *argv[]) {
int fd;
unsigned int seals;
if (argc != 2) {
fprintf(stderr, "%s /proc/PID/fd/FD\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDWR);
if (fd == -1)
errExit("open");
seals = fcntl(fd, F_GET_SEALS);
if (seals == -1)
errExit("fcntl");
printf("Existing seals:");
if (seals & F_SEAL_SEAL)
printf(" SEAL");
if (seals & F_SEAL_GROW)
printf(" GROW");
if (seals & F_SEAL_WRITE)
printf(" WRITE");
if (seals & F_SEAL_SHRINK)
printf(" SHRINK");
printf("\n");
/* Code to map the file and access the contents of the
resulting mapping omitted */
exit(EXIT_SUCCESS); }
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2015-01-22 | Linux |