ソケットを read したらどうなるの?
ファイルは read/write するもの、ソケットは recv/send するものというのが
普通ですよね。
でも select() にはファイルもソケットもつっこめます…ということは、両者にそんなに違いはない!?
実は socket() が返すファイル番号は、ファイルディスクリプタとしても使えます。
だから、ソケットを read したり write したりもできます。
その仕組みをちょっと紹介してみます。
sys_socket()
asmlinkage long sys_socket(int family, int type, int protocol)
{
int retval;
struct socket *sock;
int flags;
// 各種フラグの設定
...
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;
// ここで、ファイルディスクリプタとしての準備をする
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release;
out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval;
out_release:
sock_release(sock);
return retval;
}
sock_map_fd : net/socket.c
int sock_map_fd(struct socket *sock, int flags)
{
struct file *newfile;
int fd = sock_alloc_fd(&newfile, flags);
if (likely(fd >= 0)) {
// ここで、fd の機能を追加する
int err = sock_attach_fd(sock, newfile, flags);
if (unlikely(err < 0)) {
put_filp(newfile);
put_unused_fd(fd);
return err;
}
fd_install(fd, newfile);
}
return fd;
}
static int sock_attach_fd(struct socket *sock, struct file *file, int flags)
{
struct dentry *dentry;
struct qstr name = { .name = "" };
dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name);
if (unlikely(!dentry))
return -ENOMEM;
dentry->d_op = &sockfs_dentry_operations;
/*
* We dont want to push this dentry into global dentry hash table.
* We pretend dentry is already hashed, by unsetting DCACHE_UNHASHED
* This permits a working /proc/$pid/fd/XXX on sockets
*/
dentry->d_flags &= ~DCACHE_UNHASHED;
d_instantiate(dentry, SOCK_INODE(sock));
sock->file = file;
init_file(file, sock_mnt, dentry, FMODE_READ | FMODE_WRITE,
&socket_file_ops);
// ここがポイント。
// read とか write とか出来るように、file 構造体としての体裁を整える。
SOCK_INODE(sock)->i_fop = &socket_file_ops;
file->f_flags = O_RDWR | (flags & O_NONBLOCK);
file->f_pos = 0;
file->private_data = sock;
return 0;
}
ここで、socjet_file_ops の定義は以下のように成っている。
static const struct file_operations socket_file_ops = {
.owner = THIS_MODULE, // ソケットのオーナー
.llseek = no_llseek, // seek はできない
.aio_read = sock_aio_read, // 非同期 read の定義
.aio_write = sock_aio_write, // 非同期 write の定義
.poll = sock_poll, //
.unlocked_ioctl = sock_ioctl, // ioctl
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_sock_ioctl,
#endif
.mmap = sock_mmap,
.open = sock_no_open, /* special open code to disallow open via /proc */
// open はできない
.release = sock_close,
.fasync = sock_fasync,
.sendpage = sock_sendpage,
.splice_write = generic_splice_sendpage,
.splice_read = sock_splice_read,
};
一方、file 構造体オリジナルの定義はこちら。
(from include/linux/fs.h)
システムコール表: syscall_table_32.S
システムコールのはじまりはいつもここ。
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */
.long sys_exit
.long sys_fork
.long sys_read
read はシステムコール番号 3 番で、実体は sys_read。
sys_read(): fs/read_write.c
ファイルディスクリプタ番号 (fd) から、file 構造体を探し、
これを指定して vfs_read を呼びます。
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
// ファイル構造体の取得
file = fget_light(fd, &fput_needed);
if (file) {
// ファイルポインタの位置を取得
loff_t pos = file_pos_read(file);
// ファイルの読み込み
ret = vfs_read(file, buf, count, &pos);
// 現在のファイルポインタの位置を書き込み
// (次回 read したときにちゃんと次から読めるように)
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}