关于套接字创建的执行流程,前文已有描述,其最终会进入我们的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是否大于零即可。
没有评论:
发表评论