unixドメインソケット(STREAM/DATAGRAM)
STREAM型通信はconnectでサーバのsock->sk_receive_queueにクライアントソケットをpeerとするソケットを登録し、acceptで、このクライアントソケットの新規ソケットを作成したソケットをサーバ側のソケットとしてお互いの通信を行います。
DATAGRAM型通信のconnectは、サーバのsock->sk_receive_queueに登録されず、クライアントソケットのsock->peerにサーバソケットがセットされます。従ってサーバからクライアントに送信できません。また全てのクライアントはサーバ元ソケットにデータが送信されます。
ソケット送信は、送信先のsock->sk_receive_queueに送信データをリストし、受信は自ソケットのsock->sk_receive_queueからデータを取得します。
acceptシステムコールです。ソケット依存のsock->ops->accept()がコールされ、DATAGRAM型ではエラーです。sock->ops->accept()で自ソケットのsock->sk_receive_queueからクライアントのconnectで作成したstruct sk_buff *skbを作成し、sock_graft(tsk, newsock)でnewsock->sk = skb->skとする新規にsockを作成します。newsock->sk->peerはクライアントソケットが設定されおり、クライアントソケットのsock->sk->peerにはnewsocが設定され、クライアント/サーバ間でのやり取りが可能となります。送信は自ソケットのsock->peerソケットのsock->sk_receive_queueに送信データをリストする事で実装されます。
このソケットファイルを、fd_install()でcurrent->files->fd[newfd] = newfileとし、newfdファイルIDのソケットファイルを作成します。
DATAGRAMはクライアントソケットpeerをサーバのソケットにするだけです。クライアントはこのpeerのソケットへ送信するのみです。
DATAGRAM型通信のconnectは、サーバのsock->sk_receive_queueに登録されず、クライアントソケットのsock->peerにサーバソケットがセットされます。従ってサーバからクライアントに送信できません。また全てのクライアントはサーバ元ソケットにデータが送信されます。
ソケット送信は、送信先のsock->sk_receive_queueに送信データをリストし、受信は自ソケットのsock->sk_receive_queueからデータを取得します。
acceptシステムコールです。ソケット依存のsock->ops->accept()がコールされ、DATAGRAM型ではエラーです。sock->ops->accept()で自ソケットのsock->sk_receive_queueからクライアントのconnectで作成したstruct sk_buff *skbを作成し、sock_graft(tsk, newsock)でnewsock->sk = skb->skとする新規にsockを作成します。newsock->sk->peerはクライアントソケットが設定されおり、クライアントソケットのsock->sk->peerにはnewsocが設定され、クライアント/サーバ間でのやり取りが可能となります。送信は自ソケットのsock->peerソケットのsock->sk_receive_queueに送信データをリストする事で実装されます。
このソケットファイルを、fd_install()でcurrent->files->fd[newfd] = newfileとし、newfdファイルIDのソケットファイルを作成します。
SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
int __user *, upeer_addrlen, int, flags)
{
struct socket *sock, *newsock;
struct file *newfile;
int err, len, newfd, fput_needed;
struct sockaddr_storage address;
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL;
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
err = -ENFILE;
newsock = sock_alloc();
if (!newsock)
goto out_put;
newsock->type = sock->type;
newsock->ops = sock->ops;
__module_get(newsock->ops->owner);
newfd = sock_alloc_file(newsock, &newfile, flags);
if (unlikely(newfd < 0)) {
err = newfd;
sock_release(newsock);
goto out_put;
}
err = security_socket_accept(sock, newsock);
if (err)
goto out_fd;
err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err < 0)
goto out_fd;
if (upeer_sockaddr) {
if (newsock->ops->getname(newsock, (struct sockaddr *)&address,
&len, 2) < 0) {
err = -ECONNABORTED;
goto out_fd;
}
err = move_addr_to_user((struct sockaddr *)&address,
len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_fd;
}
fd_install(newfd, newfile);
err = newfd;
out_put:
fput_light(sock->file, fput_needed);
out:
return err;
out_fd:
fput(newfile);
put_unused_fd(newfd);
goto out_put;
}
STREAM型のconnectです。unix_create1()でサーバのacceptで取得するソケットを作成し、unix_peer(newsk) = sk / unix_peer(sk) = newsk;として互いに連動し合うソケットを__skb_queue_tail(&other->sk_receive_queue,)で接続先ソケットにリストします。
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk), *newu, *otheru;
struct sock *newsk = NULL;
struct sock *other = NULL;
struct sk_buff *skb = NULL;
unsigned hash;
int st;
int err;
long timeo;
err = unix_mkname(sunaddr, addr_len, &hash);
if (err < 0)
goto out;
addr_len = err;
if (sock->passcred && !u->addr && (err = unix_autobind(sock)) != 0)
goto out;
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
err = -ENOMEM;
newsk = unix_create1(NULL);
if (newsk == NULL)
goto out;
skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
if (skb == NULL)
goto out;
restart:
other = unix_find_other(sunaddr, addr_len, sk->sk_type, hash, &err);
if (!other)
goto out;
unix_state_rlock(other);
if (sock_flag(other, SOCK_DEAD)) {
unix_state_runlock(other);
sock_put(other);
goto restart;
}
err = -ECONNREFUSED;
if (other->sk_state != TCP_LISTEN)
goto out_unlock;
if (skb_queue_len(&other->sk_receive_queue) >
other->sk_max_ack_backlog) {
err = -EAGAIN;
if (!timeo)
goto out_unlock;
timeo = unix_wait_for_peer(other, timeo);
err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
sock_put(other);
goto restart;
}
st = sk->sk_state;
switch (st) {
case TCP_CLOSE:
break;
case TCP_ESTABLISHED:
err = -EISCONN;
goto out_unlock;
default:
err = -EINVAL;
goto out_unlock;
}
unix_state_wlock(sk);
if (sk->sk_state != st) {
unix_state_wunlock(sk);
unix_state_runlock(other);
sock_put(other);
goto restart;
}
err = security_unix_stream_connect(sock, other->sk_socket, newsk);
if (err) {
unix_state_wunlock(sk);
goto out_unlock;
}
sock_hold(sk);
unix_peer(newsk) = sk;
newsk->sk_state = TCP_ESTABLISHED;
newsk->sk_type = SOCK_STREAM;
newsk->sk_peercred.pid = current->tgid;
newsk->sk_peercred.uid = current->euid;
newsk->sk_peercred.gid = current->egid;
newu = unix_sk(newsk);
newsk->sk_sleep = &newu->peer_wait;
otheru = unix_sk(other);
if (otheru->addr) {
atomic_inc(&otheru->addr->refcnt);
newu->addr = otheru->addr;
}
if (otheru->dentry) {
newu->dentry = dget(otheru->dentry);
newu->mnt = mntget(otheru->mnt);
}
sk->sk_peercred = other->sk_peercred;
sock_hold(newsk);
unix_peer(sk) = newsk;
sock->state = SS_CONNECTED;
sk->sk_state = TCP_ESTABLISHED;
unix_state_wunlock(sk);
spin_lock(&other->sk_receive_queue.lock);
__skb_queue_tail(&other->sk_receive_queue, skb);
atomic_inc(&newu->inflight);
spin_unlock(&other->sk_receive_queue.lock);
unix_state_runlock(other);
other->sk_data_ready(other, 0);
sock_put(other);
return 0;
out_unlock:
if (other)
unix_state_runlock(other);
out:
if (skb)
kfree_skb(skb);
if (newsk)
unix_release_sock(newsk, 0);
if (other)
sock_put(other);
return err;
}
DATAGRAM型のconnectで、unix_find_other()で送信先ソケットを検索し、自身ソケットのsk->sk_pair=otherとするだけです。このソケットでの送信は、other->sk_receive_queueにリストされます。
static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
int alen, int flags)
{
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr;
struct sock *other;
unsigned hash;
int err;
if (addr->sa_family != AF_UNSPEC) {
err = unix_mkname(sunaddr, alen, &hash);
if (err < 0)
goto out;
alen = err;
if (test_bit(SOCK_PASSCRED, &sock->flags) &&
!unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0)
goto out;
restart:
other = unix_find_other(net, sunaddr, alen, sock->type, hash, &err);
if (!other)
goto out;
unix_state_double_lock(sk, other);
if (sock_flag(other, SOCK_DEAD)) {
unix_state_double_unlock(sk, other);
sock_put(other);
goto restart;
}
err = -EPERM;
if (!unix_may_send(sk, other))
goto out_unlock;
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
if (err)
goto out_unlock;
} else {
other = NULL;
unix_state_double_lock(sk, other);
}
if (unix_peer(sk)) {
struct sock *old_peer = unix_peer(sk);
unix_peer(sk) = other;
unix_state_double_unlock(sk, other);
if (other != old_peer)
unix_dgram_disconnected(sk, old_peer);
sock_put(old_peer);
} else {
unix_peer(sk) = other;
unix_state_double_unlock(sk, other);
}
return 0;
out_unlock:
unix_state_double_unlock(sk, other);
sock_put(other);
out:
return err;
}
要約
STREAMはconnectはソケットのpeerをクライアントソケットとするサーバで個別に使用する事になるソケットを作成し、サーバのソケットにリストします。そしてクライアントソケットのpeerにはこのサーバが使用するソケットを設定することで、サーバがacceptでこのソケットを取得すれば、相互のやり取りが可能となります。DATAGRAMはクライアントソケットpeerをサーバのソケットにするだけです。クライアントはこのpeerのソケットへ送信するのみです。





