socketpairシステムコール
Rev.1を表示中。最新版はこちら。
UNIXドメインソケットは、異なるプロセス間のソケットを接続するため、元のソケットをbindでソケットスペシャルファイルを作成し、そのinode番号をインデックスとするソケットを作成します。それを接続するタスクのconnectで、接続するタスクソケットをpeerとする接続元タスクが使用するソケットを作成、接続元タスクはacceptでそのソケットを取得して、両タスクでの通信を実現します。しかし、親子プロセスのように、ファイルIDを共有するタスク間では、このような実装は不要です。socketpairシステムコールは、互いのソケットのpeerを互いのソケットする2つのソケットを取得し、connect/acceptなく、ソケット間でやり取りを行うためのシステムコールです。
sock1->peer = sock2/sock2->peer = sock1のソケットを作成し、 file->private_data = sockとする
2つのfileを取得します。
SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol, int __user *, usockvec) { struct socket *sock1, *sock2; int fd1, fd2, err; struct file *newfile1, *newfile2; int flags; flags = type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; err = sock_create(family, type, protocol, &sock1); if (err < 0) goto out; err = sock_create(family, type, protocol, &sock2); if (err < 0) goto out_release_1; err = sock1->ops->socketpair(sock1, sock2); if (err < 0) goto out_release_both; fd1 = sock_alloc_file(sock1, &newfile1, flags); if (unlikely(fd1 < 0)) { err = fd1; goto out_release_both; } fd2 = sock_alloc_file(sock2, &newfile2, flags); if (unlikely(fd2 < 0)) { err = fd2; fput(newfile1); put_unused_fd(fd1); sock_release(sock2); goto out; } audit_fd_pair(fd1, fd2); fd_install(fd1, newfile1); fd_install(fd2, newfile2); err = put_user(fd1, &usockvec[0]); if (!err) err = put_user(fd2, &usockvec[1]); if (!err) return 0; sys_close(fd2); sys_close(fd1); return err; out_release_both: sock_release(sock2); out_release_1: sock_release(sock1); out: return err; } #define unix_peer(sk) (unix_sk(sk)->peer) static int unix_socketpair(struct socket *socka, struct socket *sockb) { struct sock *ska = socka->sk, *skb = sockb->sk; sock_hold(ska); sock_hold(skb); unix_peer(ska) = skb; unix_peer(skb) = ska; init_peercred(ska); init_peercred(skb); if (ska->sk_type != SOCK_DGRAM) { ska->sk_state = TCP_ESTABLISHED; skb->sk_state = TCP_ESTABLISHED; socka->state = SS_CONNECTED; sockb->state = SS_CONNECTED; } return 0; } static int sock_alloc_file(struct socket *sock, struct file **f, int flags) { struct qstr name = { .name = "" }; struct path path; struct file *file; int fd; fd = get_unused_fd_flags(flags); if (unlikely(fd < 0)) return fd; path.dentry = d_alloc_pseudo(sock_mnt->mnt_sb, &name); if (unlikely(!path.dentry)) { put_unused_fd(fd); return -ENOMEM; } path.mnt = mntget(sock_mnt); d_instantiate(path.dentry, SOCK_INODE(sock)); SOCK_INODE(sock)->i_fop = &socket_file_ops; file = alloc_file(&path, FMODE_READ | FMODE_WRITE, &socket_file_ops); if (unlikely(!file)) { ihold(path.dentry->d_inode); path_put(&path); put_unused_fd(fd); return -ENFILE; } sock->file = file; file->f_flags = O_RDWR | (flags & O_NONBLOCK); file->f_pos = 0; file->private_data = sock; *f = file; return fd; }
備考
DGRAMのstruct proto_ops unix_dgram_opsも.socketpair = unix_socketpairとなっており、DGRAMでのsocketpairは、機能的にseqpacketと同じ物ととなります。ざくっと走査したところ、UNIXドメインソケット以外、socketpairを実装しているソケットファミリは無いようです。