システムコールでファイル記述子を使用したいとします(fd番号はパラメータで指定されます)。ユーザー空間プログラムがこのシステムコールを使用した場合はどうなりますか? OSはこの特定のfdをどこで見つけますか?現在のプロセスのファイル記述子にありますか?それとも別の場所にありますか?
以下では、この点について説明します。
+--------------+ +----++--------------+
| Kernel space | | fd || User space |
| | |list|| |
| handler <---------------- syscall(fd) |
| | | || |
+--------------+ +----++--------------+
答え1
ファイル記述子は、特定のプロセスで開かれたすべてのファイルの1つのファイルを参照する整数です。通常、これはファイル記述子をテーブルへのインデックスとして扱うことによってカーネルによって行われます。
私の答えの残りの部分はLinuxに適用されます。
Linuxでは、各有効なファイル記述子はに関連付けられていますstruct file
。この構造には、inode(ファイルのデータとメタデータ)へのポインタ、ファイル内のプロセスの現在の場所、タスクリスト(実際にはファイルを含むファイルへのポインタ)が含まれています。ファイルが配置される)システムによって実装された機能へのポインタ)など
file
ファイル記述子から構造を取得するには、Linuxカーネルは次のように進みます。ここではシステムコールを例に挙げますread
。
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_read(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
fdput_pos(f);
}
return ret;
}
最初の操作は、fdget_pos
ユーザー・スペースの呼び出し元からファイル記述子をパラメーターとして取り出し、次のように定義されたfile
ファイルを返します。struct fd
struct fd {
struct file *file;
unsigned int flags;
};
これは、基本的にstruct file
構造を再配置するときに必要な作業を覚えているいくつかのフラグがあるものです。
さて、fdget_pos
それがどのように機能するか。実際には奇妙な方法で複雑ですが、2つの基本的なタスクにまとめられています(単純化のためにここではそれ以上のチェックは表示されません)。
1つ目は、プロセスのファイルテーブルをインポートすることです。テーブルは、呼び出し側のプロセス構造内のポインタを介して使用できます(次からアクセス可能)current
。
struct files_struct *files = current->files;
次のタスクには、ファイル記述子を検証するタスクが含まれます。
if (fd < files->fdt->max_fds) // first of all, if the file descriptor is too big, then it cannot be valid
return files->fdt->fd[fd]; // otherwise, we return the pointer stored in the table of file descriptors (may be NULL)
return NULL;
このポインタは、関数が返される前に削除できます(たとえば、プロセスのあるスレッドがread
同じファイル記述子で同時に別のスレッドを実行している場合など)。close
カーネルはこの問題を処理する役割を担います。
struct file
返されたポインタがあるfdget_pos
場合は、システムコールNULL
に渡されたファイル記述子が無効であることを意味します。この場合、システムコールはエラーコードEBADF
(「無効なファイル記述子」)を返します。
要約すると、ファイルディスクリプタは各プロセスのファイルディスクリプタテーブルのインデックスにすぎません。しかし、単に逆参照するだけでは十分ではありませんNULL
。なぜなら、ファイルテーブルのエントリは 。