socket


sock_create()でfamily/type/protocolに応じたstruct socketを取得し、sock_map_fd()でcurrent->filesのファイルIDに、file->private_data = sock file->f_op = socket_file_opsとするsocketファイルを設定します。sockはファイルIDそのもので、そのread/writeコールバックでsock_create()で作成されたソケットコールバックがコールされるという事です。

socket_file_opsのread/writeのsock_aio_read()/sock_aio_write()は、 sock->ops->recvmsg()/sock->ops->sendmsg()をコールし、実装はsockファミリ依存となります。なお、UNIXドメインソケットでは、スレッド間ではbindすることなく、直接socketで参照可能です。(たぶん)
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
       int retval;
       struct socket *sock;
       int flags;

       BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
       BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
       BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
       BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

       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;

       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:
       return retval;

out_release:
       sock_release(sock);
       return retval;
}
sock_create()から__sock_create()をされます。net_families[family]はネットファミリに応じたstruct net_proto_familyが登録されていて、ネットファミリに応じたcreate()コールバックで、UNIXドメインソケットはunix_create()がコールされ、ネットファミリに応じたsockeコールバックが設定されたstruct socketを作成します。
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;


       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;
}
sock_map_fd()はsock_alloc_file()で作成した、file->op=socket_file_ops/file->private_data = sock、inode->i_fop=socket_file_opsするファイル名=""のファイルをsockfsに作成し、返値のfdはカレントプロセスの空きファイルIDで、そのファイルIDにfileを設定することで、ファイルとしての参照が可能となります。
int sock_map_fd(struct socket *sock, int flags)
{
       struct file *newfile;
       int fd = sock_alloc_file(sock, &newfile, flags);

       if (likely(fd >= 0))
               fd_install(fd, newfile);

       return fd;
}
sockefsからdentryを取得し、SOCK_INODE(sock)->i_fop = &socket_file_ops/file->f_op = &socket_file_opsとし、sock->file = fileで、fileコールバックからsocket依存のコールバックをコールする事が可能となります。
static const struct file_operations socket_file_ops = {
       .owner =        THIS_MODULE,
       .llseek =       no_llseek,
       .aio_read =     sock_aio_read,
       .aio_write =    sock_aio_write,
       .poll =         sock_poll,
       .unlocked_ioctl = sock_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 */
       .release =      sock_close,
       .fasync =       sock_fasync,
       .sendpage =     sock_sendpage,
       .splice_write = generic_splice_sendpage,
       .splice_read =  sock_splice_read,
};

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)) {
               /* drop dentry, keep inode */
               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;
}

static struct socket *sock_alloc(void)
{
       struct inode *inode;
       struct socket *sock;

       inode = new_inode_pseudo(sock_mnt->mnt_sb);
       if (!inode)
               return NULL;

       sock = SOCKET_I(inode);

       kmemcheck_annotate_bitfield(sock, type);
       inode->i_ino = get_next_ino();
       inode->i_mode = S_IFSOCK | S_IRWXUGO;
       inode->i_uid = current_fsuid();
       inode->i_gid = current_fsgid();

       percpu_add(sockets_in_use, 1);
       return sock;
}

static struct inode *sock_alloc_inode(struct super_block *sb)
{
       struct socket_alloc *ei;
       struct socket_wq *wq;

       ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
       if (!ei)
               return NULL;
       wq = kmalloc(sizeof(*wq), GFP_KERNEL);
       if (!wq) {
               kmem_cache_free(sock_inode_cachep, ei);
               return NULL;
       }
       init_waitqueue_head(&wq->wait);
       wq->fasync_list = NULL;
       RCU_INIT_POINTER(ei->socket.wq, wq);

       ei->socket.state = SS_UNCONNECTED;
       ei->socket.flags = 0;
       ei->socket.ops = NULL;
       ei->socket.sk = NULL;
       ei->socket.file = NULL;

       return &ei->vfs_inode;
}

補足

ソケットファミリはsock_register()でstruct net_proto_familyとする属性をファミリをインデックスとしてnet_families[]に登録されます。
static int __init af_unix_init(void)
{
       int rc = -1;
       struct sk_buff *dummy_skb;

       BUILD_BUG_ON(sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb));

       rc = proto_register(&unix_proto, 1);
       if (rc != 0) {
               printk(KERN_CRIT "%s: Cannot create unix_sock SLAB cache!\n",
                      __func__);
               goto out;
       }

       sock_register(&unix_family_ops);
       register_pernet_subsys(&unix_net_ops);
out:
       return rc;
}

int sock_register(const struct net_proto_family *ops)
{
       int err;

       if (ops->family >= NPROTO) {
               printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family,
                      NPROTO);
               return -ENOBUFS;
       }

       spin_lock(&net_family_lock);
       if (rcu_dereference_protected(net_families[ops->family],
                                     lockdep_is_held(&net_family_lock)))
               err = -EEXIST;
       else {
               rcu_assign_pointer(net_families[ops->family], ops);
               err = 0;
       }
       spin_unlock(&net_family_lock);

       printk(KERN_INFO "NET: Registered protocol family %d\n", ops->family);
       return err;
}
EXPORT_SYMBOL(sock_register);
UNIXドメインソケット及びストリームプロトコルのコールバック群で、unix_family_opsがsock_register()でnet_families[]に登録され、その.createで、unix_stream_opsが実コールバック関数としてsockに設定されます。
static const struct net_proto_family unix_family_ops = {
       .family = PF_UNIX,
       .create = unix_create,
       .owner  = THIS_MODULE,
};

static const struct proto_ops unix_stream_ops = {
       .family =       PF_UNIX,
       .owner =        THIS_MODULE,
       .release =      unix_release,
       .bind =         unix_bind,
       .connect =      unix_stream_connect,
       .socketpair =   unix_socketpair,
       .accept =       unix_accept,
       .getname =      unix_getname,
       .poll =         unix_poll,
       .ioctl =        unix_ioctl,
       .listen =       unix_listen,
       .shutdown =     unix_shutdown,
       .setsockopt =   sock_no_setsockopt,
       .getsockopt =   sock_no_getsockopt,
       .sendmsg =      unix_stream_sendmsg,
       .recvmsg =      unix_stream_recvmsg,
       .mmap =         sock_no_mmap,
       .sendpage =     sock_no_sendpage,
};

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;
}
サポートされているソケットファミリ
#define AF_UNSPEC       0
#define AF_UNIX         1       /* Unix domain sockets          */
#define AF_LOCAL        1       /* POSIX name for AF_UNIX       */
#define AF_INET         2       /* Internet IP Protocol         */
#define AF_AX25         3       /* Amateur Radio AX.25          */
#define AF_IPX          4       /* Novell IPX                   */
#define AF_APPLETALK    5       /* AppleTalk DDP                */
#define AF_NETROM       6       /* Amateur Radio NET/ROM        */
#define AF_BRIDGE       7       /* Multiprotocol bridge         */
#define AF_ATMPVC       8       /* ATM PVCs                     */
#define AF_X25          9       /* Reserved for X.25 project    */
#define AF_INET6        10      /* IP version 6                 */
#define AF_ROSE         11      /* Amateur Radio X.25 PLP       */
#define AF_DECnet       12      /* Reserved for DECnet project  */
#define AF_NETBEUI      13      /* Reserved for 802.2LLC project*/
#define AF_SECURITY     14      /* Security callback pseudo AF */
#define AF_KEY          15      /* PF_KEY key management API */
#define AF_NETLINK      16
#define AF_ROUTE        AF_NETLINK /* Alias to emulate 4.4BSD */
#define AF_PACKET       17      /* Packet family                */
#define AF_ASH          18      /* Ash                          */
#define AF_ECONET       19      /* Acorn Econet                 */
#define AF_ATMSVC       20      /* ATM SVCs                     */
#define AF_RDS          21      /* RDS sockets                  */
#define AF_SNA          22      /* Linux SNA Project (nutters!) */
#define AF_IRDA         23      /* IRDA sockets                 */
#define AF_PPPOX        24      /* PPPoX sockets                */
#define AF_WANPIPE      25      /* Wanpipe API Sockets */
#define AF_LLC          26      /* Linux LLC                    */
#define AF_CAN          29      /* Controller Area Network      */
#define AF_TIPC         30      /* TIPC sockets                 */
#define AF_BLUETOOTH    31      /* Bluetooth sockets            */
#define AF_IUCV         32      /* IUCV sockets                 */
#define AF_RXRPC        33      /* RxRPC sockets                */
#define AF_ISDN         34      /* mISDN sockets                */
#define AF_PHONET       35      /* Phonet sockets               */
#define AF_IEEE802154   36      /* IEEE802154 sockets           */
#define AF_CAIF         37      /* CAIF sockets                 */
#define AF_ALG          38      /* Algorithm sockets            */
#define AF_NFC          39      /* NFC sockets                  */
#define AF_MAX          40      /* For now.. */

追記

UNIX ドメインソケットではunix_bindコールバックでソケットスペシャルファイルが作成されます。UNIX ドメインソケットはメインプロセスのファイル配下にsockeが登録されるだけでなく、ソケット単位で管理もされています。この管理がファイルinode番号をインデックスとする物で、ソケットスペシャルファイルはプロセス間ソケット共有のためで、read/writeは言うまでもなくopenさえもできません。またcopy/他ファイルシステムへのmv等、inode番号が変更するような事はしてはいけません。


最終更新 2015/11/20 18:06:22 - north
(2014/11/15 16:47:11 作成)


検索

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