- 论坛徽章:
- 0
|
刚开始读linux内核的代码,觉得无从入手,在开始的时候看的是起动部分的代码,因为对硬件、汇编等知识的贫瘠所以放弃了。好在有2年多的Linux应用开发经验,所以决定从网络部分开始,尽管如此,仍然发现网络部分和其它部分比如文件系统、内存管理等有着很多的联系。不过没有灰心,相信总会有一天能够对各个部分都有一个详细的理解,大家也一样不要被代码吓倒,一步步来,多参考一下其它的文章,一定会成功!
以下代码全部摘自linux-2.4.20-8(Redhat9)
[/code]
第一章 套接字的初始化
在讨论套接字的建立、读写前,先介绍一下内核中关于套接字的初始化的代码,这样才能更好的理解后面的代码。
我们先来看看sock_init(net/socket.c)函数,该函数是在系统初始化的时候执行的,源程序如下:
void __init sock_init(void)
{
int i;
printk(KERN_INFO "Linux NET4.0 for Linux 2.4\n" ;
printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039\n" ;
/*
* Initialize all address (protocol) families.
*/
指针数组,每种family对应一个数组元素,第二章
有详细说明
for (i = 0; i < NPROTO; i++)
net_families = NULL;
/*
* Initialize sock SLAB cache.
*/
通过slab分配sock缓存,关于slab请参考
Linux内存管理部分
sk_init();
#ifdef SLAB_SKB
/*
* Initialize skbuff SLAB cache
*/
分配skbuff,Linux网络层采用统一的缓冲区结构skbuff,被组织
成双向链表,数据通过它在各层传递
skb_init();
#endif
/*
* Wan router layer.
*/
路由模块的初始化
#ifdef CONFIG_WAN_ROUTER
wanrouter_init();
#endif
/*
* Initialize the protocols module.
*/
注册网络文件系统并mount,Linux的虚拟文件系统使得网络、
管道等等都可以是一个独立的文件系统,对于核心的开发和应用
的开发都可以用一种统一的方式。可以cat /proc/filesystems查看
当前的系统中安装了哪些文件系统。
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type);
/* The real protocol initialization is performed when
* do_initcalls is run.
*/
/*
* The netlink device handler may be needed early.
*/
#ifdef CONFIG_NET
rtnetlink_init();
#endif
#ifdef CONFIG_NETLINK_DEV
init_netlink();
#endif
#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
#ifdef CONFIG_BLUEZ
bluez_init();
#endif
}
第二章 建立套接字
在发送接收数据之前,我们要通过系统调用(socket)建立套接字。关于用户进程通过系统调用申请内核服务请参考其它文章。
系统调用socket通过0x80中断调用内核中相应的函数sys_socket(net/socket.c),源程序如下:
asmlinkage long sys_socket(int family, int type, int protocol)
{
int retval;
定义struct socket结构指针sock
struct socket *sock;
调用sock_create函数分配空间初始化一系列的结构成员等。
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
前几天在CU上看到了有人问内核中goto语句的问题
当时大鹰给出了答案,在次说明一遍。
goto语句在内核中很大的原因在于效率的问题,还有
一部分的原因是使得错误处理更加简洁,还有极小部分
的原因是为了代码风格的一致。
goto out;
为套接字分配一个可利用的文件描述字,
使得在应用层可以向读写文件那样来读写套接字
retval = sock_map_fd(sock);
if (retval < 0)
goto out_release;
out:
/* It may be already another descriptor Not kernel problem. */
return retval;
out_release:
sock_release(sock);
return retval;
}
sock_create(net/socket.c)函数源程序如下:
int sock_create(int family, int type, int protocol, struct socket **res)
{
int i;
struct socket *sock;
/*
* Check protocol is in range
*/
对family和type进行有效性的检查
if (family < 0 || family >;= NPROTO)
return -EAFNOSUPPORT;
if (type < 0 || type >;= SOCK_MAX)
return -EINVAL;
/* Compatibility.
This uglymoron is moved from INET layer to here to avoid
deadlock in module load.
*/
对参数family是PF_INET而type是SOCK_PACKET(这种类型不是标准的BSD套接字类型,它是Linux自身对套接字系统的扩展,它允许进程直接对设备层的数据包进行操作)的调用做一下兼容性的转换,对family重新赋值PF_PACKET
if (family == PF_INET && type == SOCK_PACKET) {
本人觉得warned赋初始值更为妥当,该小段代码是打印出
警告信息,current是指向当前task的全局指针。
static int warned;
if (!warned) {
warned = 1;
printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->;comm);
}
family = PF_PACKET;
}
如果在编译内核的时候把网络部分编译成模块
#if defined(CONFIG_KMOD) && defined(CONFIG_NET)
/* Attempt to load a protocol module if the find failed.
*
* 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
* requested real, full-featured networking support upon configuration.
* Otherwise module support will break!
*/
指针数组,其中create成员(函数指针)保存了每种协议的create函数地址
对于PF_INET协议来说,这个数组是在inet_init函数(net/ipv4/af_inet.c)
中通过调用sock_register(net/socket.c)来赋值的,并把create指向了
inet_create函数。
以下程序说明如果该钟协议对应的数组元素为空,那么就加载相应的模块。
if (net_families[family]==NULL)
{
char module_name[30];
sprintf(module_name,"net-pf-%d",family);
request_module(module_name);
}
#endif
对于这里为什么要加锁我不能给出详细的答案,因为我也不知道,
但是想想,分配sock和创建sock的时候应该会访问到很多全局
的东西,并且net_families所指向的内存应该是系统的共享内存区域
所以上锁也是可以理解的。大家看看sock_register函数(net/socket.c)
可能会更好的理解,因为这个函数是对net_families做赋值的,在
这个函数中用到了net_family_write_lock函数。
net_family_read_lock();
if (net_families[family] == NULL) {
i = -EAFNOSUPPORT;
goto out;
}
/*
* Allocate the socket and allow the family to set things up. if
* the protocol is 0, the family is instructed to select an appropriate
* default.
*/
分配socket,该函数实际上是在网络文件系统上生成新的inode
然后做一些相应的设置工作。
if (!(sock = sock_alloc()))
{
printk(KERN_WARNING "socket: no more sockets\n" ;
i = -ENFILE; /* Not exactly a match, but its the
closest posix thing */
goto out;
}
sock->;type = type;
创建socket,其实调用的是inet_create,因为net_families[PF_INET]
在inet_init的时候被初始化,create函数指针指向的是inet_create函数
if ((i = net_families[family]->;create(sock, protocol)) < 0)
{
sock_release(sock);
goto out;
}
*res = sock;
out:
net_family_read_unlock();
return i;
}
[code][/code] |
|