socketpairシステムコール


socketpairはソケットオペレーションコールバックの.socketpairで、AF_UNIXでのみ実装され、引数に応じたの2つのソケットを作成し、そのソケットペアーに互いのソケット設定する事で、相互のやり取りを実現します。
sock1->->sk_pair= sock2
sock2->->sk_pair= sock1

newfile1->private_data = sock1
newfile2->private_data = sock2;

current->file[fd1] = newfile1
current->file[fd2] = newfile2;

usockvec[0] = fd1
usockvec[1] = fd2
送受信はDGRAM/STREAMに応じたコールバックがコールされるだけで、互いのソケットに相手のソケット有してるのでサーバ(bind)/クライアント(connect)に掛かる実装は必要なく双方の送受が可能となります。

protocol引数は、ソケット作成のstruct net_proto_family *net_families[NPROTO]コールバックで、NPROTOに応じたcreatコールバックに依存します。PF_UNIXのcreatコールバックは0/1(PF_UNIX)でないとエラーとなるだけで、掛かる引数に依存する実装はありません。
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;
}

int sock_create(int family, int type, int protocol, struct socket **res)
{
       return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}

int __sock_create(struct net *net, int family, int type, int protocol,
                        struct socket **res, int kern)
{
       int err;
       struct socket *sock;
       const struct net_proto_family *pf;

       if (family < 0 || family >= NPROTO)
               return -EAFNOSUPPORT;
       if (type < 0 || type >= SOCK_MAX)
               return -EINVAL;

       if (family == PF_INET && type == SOCK_PACKET) {
               static int warned;
               if (!warned) {
                       warned = 1;
                       printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
                              current->comm);
               }
               family = PF_PACKET;
       }

       err = security_socket_create(family, type, protocol, kern);
       if (err)
               return err;

       sock = sock_alloc();
       if (!sock) {
               if (net_ratelimit())
                       printk(KERN_WARNING "socket: no more sockets\n");
               return -ENFILE; /* Not exactly a match, but its the
                                  closest posix thing */
       }

       sock->type = type;

#ifdef CONFIG_MODULES
       if (rcu_access_pointer(net_families[family]) == NULL)
               request_module("net-pf-%d", family);
#endif

       rcu_read_lock();
       pf = rcu_dereference(net_families[family]);
       err = -EAFNOSUPPORT;
       if (!pf)
               goto out_release;

       if (!try_module_get(pf->owner))
               goto out_release;

       rcu_read_unlock();

       err = pf->create(net, sock, protocol, kern);
       if (err < 0)
               goto out_module_put;

       if (!try_module_get(sock->ops->owner))
               goto out_module_busy;

       module_put(pf->owner);
       err = security_socket_post_create(sock, family, type, protocol, kern);
       if (err)
               goto out_sock_release;
       *res = sock;

       return 0;

out_module_busy:
       err = -EAFNOSUPPORT;
out_module_put:
       sock->ops = NULL;
       module_put(pf->owner);
out_sock_release:
       sock_release(sock);
       return err;

out_release:
       rcu_read_unlock();
       goto out_sock_release;
}

static int unix_create(struct net *net, struct socket *sock, int protocol,
                      int kern)
{
       if (protocol && protocol != PF_UNIX)
               return -EPROTONOSUPPORT;

       sock->state = SS_UNCONNECTED;

       switch (sock->type) {
       case SOCK_STREAM:
               sock->ops = &unix_stream_ops;
               break;
       case SOCK_RAW:
               sock->type = SOCK_DGRAM;
       case SOCK_DGRAM:
               sock->ops = &unix_dgram_ops;
               break;
       case SOCK_SEQPACKET:
               sock->ops = &unix_seqpacket_ops;
               break;
       default:
               return -ESOCKTNOSUPPORT;
       }

       return unix_create1(net, sock) ? 0 : -ENOMEM;
}
unix_stream_ops/unix_dgram_ops/unix_seqpacket_opsのコールバックで、socka->sk->sk_pair=sockb->sk sockb->sk->sk_pair=sockc->skとします。socka->sk->sk_state=TCP_ESTABLISHED socka->state=SS_CONNECTEDは、stream/seqpacketでは掛かる状態でないと送信できないからです。
#define unix_peer(sk) ((sk)->sk_pair)

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;
       ska->sk_peercred.pid = skb->sk_peercred.pid = current->tgid;
       ska->sk_peercred.uid = skb->sk_peercred.uid = current->euid;
       ska->sk_peercred.gid = skb->sk_peercred.gid = current->egid;

       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;
}
検証サンプル
[root@localhost c]# cat socketpair.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

void main() {
    char  buff0[16], buff1[16];
    int   s_sockfd[2], d_sockfd[2];
    const char *msg0 = "msg0";
    const char *msg1 = "msg1";

    socketpair(AF_UNIX, SOCK_STREAM, 0, s_sockfd);
    socketpair(AF_UNIX, SOCK_DGRAM,  0, d_sockfd);
 
    write(s_sockfd[0], msg0, strlen( msg0));
    write(s_sockfd[1], msg1, strlen( msg1));
    write(d_sockfd[0], msg0, strlen( msg0));
    write(d_sockfd[1], msg1, strlen( msg1));

    memset(buff0, 0, 16);
    memset(buff1, 0, 16);
    read(s_sockfd[0], buff0, sizeof( buff0));
    read(s_sockfd[1], buff1, sizeof( buff1));
    printf("SOCK_STREAM\n");
    printf("write sock0:%s\n", msg0);
    printf("write sock1:%s\n", msg1);
    printf("read  sock0:%s\n", buff0);
    printf("read  sock1:%s\n", buff1);

    memset(buff0, 0, 16);
    memset(buff1, 0, 16);
    read(d_sockfd[0], buff0, sizeof( buff0));
    read(d_sockfd[1], buff1, sizeof( buff1));
    printf("SOCK_DGRAM\n");
    printf("write sock0:%s\n", msg0);
    printf("write sock1:%s\n", msg1);
    printf("read  sock0:%s\n", buff0);
    printf("read  sock1:%s\n", buff1);
}
結果
[root@localhost c]# ./socketpair.o
SOCK_STREAM
write sock0:msg0
write sock1:msg1
read  sock0:msg1
read  sock1:msg0
SOCK_DGRAM
write sock0:msg0
write sock1:msg1
read  sock0:msg1
read  sock1:msg0

補足

DGRAM送信はキャッシュバイトサイズまでの1回限りの送信となり、STREAM送信はキャッシュバイトサイズ単位で送信サイズまで複数回に渡って全サイズのデータが送信されます。



最終更新 2016/09/24 22:57:35 - north
(2015/12/27 20:07:32 作成)


検索

アクセス数
3689353
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。