免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1077 | 回复: 0
打印 上一主题 下一主题

深入内核socket编程(4)(zz) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-08-29 14:02 |只看该作者 |倒序浏览
以下内容来自
http://hi.baidu.com/linux%5Fkernel
关于unix网络编程的一个不错的blog,作者应该是搞协议栈,对于写应用程序的人来说,看这些东西有利于分析问题;推荐看看.

创建一个套接字
2006年08月11日 星期五 下午 02:22   
我们已经完成了MY_PF_INET域的初始化,虽然留了很多空,但我们至少已经具备了:TCP, UDP, RAW三种协议;TCP, UDP, ICMP, IGMP四种基本协议;inetsw数组。有了这些,我们
可以尝试着创建一个套接字试试。
    关于套接字创建的执行流程,前文已有描述,其最终会进入我们的family中的创建函数:
            static int myinet_create(struct socket *sock, int protocol);
套接字类型已经包含在sock结构中。MY_PF_INET域中有效的类型是SOCK_STREAM, SOCK_DGRAM和SOCK_RAW。据此,我们定位到inetsw数组的某一项(一个链表的链表头),然后在这
个链表中匹配protocol。
    MY_PF_INET域中的常用的protocol是:IPPROTO_IP, IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_TCP, IPPROTO_UDP。其中IPPROTO_IP比较特殊,是一个通配符。链表中的
protocol匹配可以是严格匹配,也可以是通配符匹配,但最终 protocol必须有一个确定的值,而不能是IPPROTO_IP。因为MY_PF_INET域中inetsw数组只有三项(SOCK_STREAM,
IPPROTO_TCP), (SOCK_DGRAM, IPPROTO_UDP), (SOCK_RAW, IPPROTO_IP),所有这个匹配比较简单。
    完成匹配后,我们要把匹配到的inet_protosw结构中的相关的值赋给sock。同时,创建sock的成员结构struct sock *sk,另外还涉及到一个结构体struct inet_sock的初始化
,具体内容不在这里详述了。
    最后,调用具体协议(TCP, UDP, RAW)的初始化函数,完成最后的创建工作。
    到这里为止,我们的MY_PF_INET模块已经能够执行socket系统调用,并作出相应的动作,返回正确的值了。我们在三种协议的初始化函数中分别加入调试输出语句,并观察其
行为(udp没有提供init函数)。
    基于前面的积累,我们再来仔细分析socket这个系统调用:
        #include
        #include
        int socket(int domain, int type, int protocol);
    三个参数中,domain指定了一个套接字域,它会影响到内核具体选择使用哪一个模块。比如,在我们的测试程序中,可以使用MY_PF_INET(30) 使内核使用我们的my_inet.ko模
块,建立因特网协议域。而type表示域中的套接字类型,对应inetsw数组的一项,在我们的 MY_PF_INET域中,其有效的取值是SOCK_STREAM, SOCK_DGRAM和SOCK_RAW。最后一个参
数protocol,指定具体的协议类型,它可以使用通配符IPPROTO_IP匹配对应类型的链表中的第一项。下面的测试代码:
        int fd0 = socket( MY_PF_INET, SOCK_RAW, MY_IPPROTO_ICMP );
        int fd1 = socket( MY_PF_INET, SOCK_RAW, MY_IPPROTO_IGMP );
        int fd2 = socket( MY_PF_INET, SOCK_RAW, IPPROTO_IP );
        int fd3 = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
        int fd4 = socket( MY_PF_INET, SOCK_DGRAM, IPPROTO_IP );
        int fd5 = socket( MY_PF_INET, SOCK_STREAM, MY_IPPROTO_TCP );
        int fd6 = socket( MY_PF_INET, SOCK_STREAM, IPPROTO_IP );
        printf("fd: %d, %d, %d, %d, %d, %d, %d\n", fd0, fd1, fd2, fd3, fd4, fd5, fd6);
显然,SOCK_RAW是没有办法通配的。结果就很明显:
        fd: 3, 4, -1, 5, 6, 7, 8
    我们再来关注一下socket系统调用的一些主要出错情况。
    第一种:所选的域不支持。domain在内核中的最大取值是31。共有32个值可选(包括四个空值,和一个保留值)。这个错误在__sock_create中就会被检测出。
    第二种:套接字类型与协议类型不匹配。比如SOCK_DGRAM搭配IPPROTO_TCP,肯定会失败。
    其余的出错类型,一般为系统错误,不必关注,实际编程中,只要判断返回的fd是否大于零即可。
销毁一个套接字(一)2006年08月11日 星期五 下午 02:22   
要使我们的工作得以顺利进行,我们必须把建立与销毁,注册与注销等配对的工作放在一起完成,才能使模块始终处于一个可使用的状态。所以,完成了套接字的创建,我们下一
步紧接着面临的工作就是套接字的销毁。
    在系统调用层,套接字也是一个文件描述符来表示,所以,关闭套接字跟关闭打开着的文件并没有区别,都是使用close(fd),但同样一个操作,在内核中却发生着很不一样的
操作。
    我们还需要从socket系统调用创建套接字讲起,当代表套接字的一个struct socket结构被完整创建出来以后,它被映射到一个文件描述符,并且系统把这个文件描述符返回给
用户。现在,我们就需要简单了解这个映射过程是怎么样的。
    因为我们的目标是重新建立一个INET域的代码,所以不想在关于文件系统的方面走太远,所以只进行简单介绍。我们首先要从系统获取一个未使用的文件描述符,并创建一个
struct file结构。同时,我们初始化这个struct file结构,整个初始化过程我们只需要关注其中两步:
            sock->file = file;
            file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;
    在这个file结构的f_op成员中,我们记录了能对该文件(因为,此时,socket提供一组跟普通文件完全一致的操作 socket_file_ops,所以,能够被看成是一个文件)进行的
所有操作。其中,包括一个release操作,这在我们销毁一个套接字时非常有用。
    最后,我们把文件描述符fd和结构file安装到进程的描述符中。进程的描述符中有一个file_struct结构成员,它是一个进程所拥有的打开的文件的列表。其中有一个成员
struct file **fd,我们使fd[fd] = file。这样,就完成了文件描述符到socket的映射,即我们通过current->files->fd[fd]能找到file,通过 sock->file也能找到file。其实
,它们之间还有其它关系。
    有了这层关系,我们在close(fd)时,便能通过fd轻松调用到socket_file_ops.release(...)函数,最终完成销毁套接字的工作,有关具体的销毁流程,下回分解。

销毁一个套接字(二)
2006年08月11日 星期五 下午 02:23   
我们先来简单看一下系统调用close(int fd)的流程。该系统调用会调用到内核中的函数:
        asmlinkage long sys_close(unsigned int fd)
参数fd给我们一个很好的线索,我们可以很方便地找到相应的struct file结构:file = current->files->fd[fd]。取出了这个至关重要的数据结构后,我们归还fd给系统,同时
,设current-> file->fd[fd]=NULL。使我们创建的socket完全跟系统和进程分离。最后再销毁struct file结构。
    销毁struct file的很多细节我们不关注,但在某一步,一个叫__fput(struct file *file)的函数中,有这样一个调用:
        file->f_op->release(inode, file);
它实际调用到了sock_close函数,该函数又会调用到sock_release函数。sock_release函数又调用我们my_inet模块提供的myinet_release函数完成实际的socket销毁工作,同时,
释放inode。
    上面讲述的是一个大致的流程,我们重点关注的还是如何在我们的my_inet模块中实现套接字的销毁。下面先看一下myinet_release函数的实现:
        int myinet_release(struct socket *sock)
        {
            struct sock *sk = sock->sk;
            if (sk) {
                long timeout = 0;
                myip_mc_drop_socket(sk);
                if (sock_flag(sk, SOCK_LINGER) &&
                                !(current->flags & PF_EXITING))
                    timeout = sk->sk_lingertime;
                sock->sk = NULL;
                sk->sk_prot->close(sk, timeout);
            }
            return 0;
        }
    可见,在我们的模块中,我们关注的其实是sock->sk的销毁,它是网络层一个套接字表示。myip_mc_drop_socket是离开组播组(如果曾加入过组播组的话),我们目前不关注
。关于timeout我们也暂时不关心。
    这里调用到了我们对应的协议的close函数,暂时,我们不去关注很多细节,只要记得在这个函数中应该调用sk_common_release最后完成对sock->sk的资源释放即可。

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/47252/showart_370259.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP