Chinaunix首页 | 论坛 | 博客
  • 博客访问: 643971
  • 博文数量: 155
  • 博客积分: 5688
  • 博客等级: 大校
  • 技术积分: 2134
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-15 15:12
文章分类

全部博文(155)

文章存档

2011年(58)

2010年(97)

分类: 系统运维

2010-08-26 21:13:33

声明:本文为原创
#####请转贴时保留以下内容######
作者GTT
本文档归属http://oldtown.cublog.cn/.转载请注明出处!
请提出宝贵意见Mail:mtloveft@hotmail.com
Linux Version:2.6.33
提示本文是关于linux 如何实现socket 通信的介绍
 
做过网络通信的基本都用过socket,有没有想过它怎么实现的,把我对linux socket的实现简单的记录下来。
希望对大家能有所帮助。不过写作水平有限,不一定说得那么明白。
 
user space 基本都是先调用clib提供的函数,然后发起系统调用,就从sys call 入手。

/*
 * System call vectors.
 * Argument checking cleaned up. Saved 20% in size.
 * This function doesn't need to set the kernel lock because * it is set by the callees.
 */

SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
    unsigned long a[6];
    unsigned long a0, a1;
    int err;
    unsigned int len;

    if (call < 1 || call > SYS_RECVMMSG) return -EINVAL;

    len = nargs[call];
    if (len > sizeof(a)) return -EINVAL;

    /* copy_from_user should be SMP safe. */
    if (copy_from_user(a, args, len)) return -EFAULT;

    audit_socketcall(nargs[call] / sizeof(unsigned long), a);

    a0 = a[0];
    a1 = a[1];

    switch (call) {
    case SYS_SOCKET:
        err = sys_socket(a0, a1, a[2]);
        break;
    case SYS_BIND:
        err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
        break;
    case SYS_CONNECT:
        err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
        break;
    case SYS_LISTEN:
        err = sys_listen(a0, a1);
        break;
    case SYS_ACCEPT:
        err = sys_accept4(a0, (struct sockaddr __user *)a1, (int __user *)a[2], 0);
        break;
    case SYS_GETSOCKNAME:
        err = sys_getsockname(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
        break;
    case SYS_GETPEERNAME:
        err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
        break;
    case SYS_SOCKETPAIR:
        err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
        break;
    case SYS_SEND:
        err = sys_send(a0, (void __user *)a1, a[2], a[3]);
        break;
    case SYS_SENDTO:
        err = sys_sendto(a0, (void __user *)a1, a[2], a[3], (struct sockaddr __user *)a[4], a[5]);
        break;
    case SYS_RECV:
        err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
        break;
    case SYS_RECVFROM:
        err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3], (struct sockaddr __user *)a[4], (int __user *)a[5]);
        break;
    case SYS_SHUTDOWN:
        err = sys_shutdown(a0, a1);
        break;
    case SYS_SETSOCKOPT:
        err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
        break;
    case SYS_GETSOCKOPT:
        err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
        break;
    case SYS_SENDMSG:
        err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
        break;
    case SYS_RECVMSG:
        err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);
        break;
    case SYS_RECVMMSG:
        err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], (struct timespec __user *)a[4]);
        break;
    case SYS_ACCEPT4:
        err = sys_accept4(a0, (struct sockaddr __user *)a1, (int __user *)a[2], a[3]);
        break;
    default:
        err = -EINVAL;
        break;
    }
    return err;
}

 
socketcall 带两个参数,第一个参数call为具体syscall的选择码,第二个参数args是从用户空间传过来的配置参数。
 
首先计算从user space 传过来得参数长度,即nargs[call]
nargs的定义如下

/* Argument list sizes for sys_socketcall */
#define AL(x) ((x) * sizeof(unsigned long))
static const unsigned char nargs[20] = {
    AL(0), AL(3), AL(3), AL(3), AL(2),
    AL(3), AL(3), AL(3), AL(4), AL(4),
    AL(4), AL(6), AL(6), AL(2), AL(5),
    AL(5), AL(3), AL(3), AL(4), AL(5)
};

 
所以不同的系统调用,参数的长度是不同的。
 
copy_from_user 是从user space 把用户态参数拷贝到kernel space 的数组a里。
 

根据call 值,学则具体的syscall
call 值的定义如下

#define SYS_SOCKET      1 /* sys_socket(2) */
#define SYS_BIND        2 /* sys_bind(2) */
#define SYS_CONNECT     3 /* sys_connect(2) */
#define SYS_LISTEN      4 /* sys_listen(2) */
#define SYS_ACCEPT      5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR  8 /* sys_socketpair(2) */
#define SYS_SEND        9 /* sys_send(2) */
#define SYS_RECV       10 /* sys_recv(2) */
#define SYS_SENDTO     11 /* sys_sendto(2) */
#define SYS_RECVFROM   12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN   13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG    16 /* sys_sendmsg(2) */
#define SYS_RECVMSG    17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4    18 /* sys_accept4(2) */
#define SYS_RECVMMSG   19 /* sys_recvmmsg(2) */

有些东西是不长用的,先忽略。看看经常使用的。
SYS_SOCKET      :是创建socket,
SYS_SHUTDOWN    :是关闭一个socket,
SYS_BIND        :是把一个socket和一个socket address绑定

下面是关于有连接的sys call
SYS_LISTEN      :server 端监听client端的连接请求
SYS_CONNECT     :client  端向server 端发送连接请求
SYS_ACCEPT      :server 端接受client端的连接请求

下面几个参数是关于发送/接收数据报文的
SYS_SEND/SYS_RECVSYS_SENDTO/SYS_RECVFROM,  SYS_SENDMSG/SYS_RECVMSGSYS_RECVMMSG

其他是一些辅助syscall
SYS_GETSOCKNAME, SYS_SETSOCKOPT, SYS_GETSOCKOPT

 

阅读(1491) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~