linux socket详解(1)
谨以此文纪念过往的岁月
一.前言
网络文件系统是一个很大的模块,其复杂程度不下于cdev,对于其学习需要很大的毅力,并且其并没有cdev那样的形象,其更加的抽象。
二.socket的创建
对于很多应用程序员而言,socket并不陌生,但是其在内核中具体的实现并不是很了解,而对于驱动工程师而言,这却是必须的,那就从socket的创建开始。以sys_socket开始,对于常用source insight而言,当我们去搜sys_socket时就不会找到该函数的定义。其具体的实现其实在SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol);我们剔除其对type以及flags的检测,其就包含两个函数sock_create
和sock_map_fd。闲话不说,咱们还是来看源码吧。你如果比照源码会发现下面的函数怎么缩水不少,不过核心还就是这东西。
static 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;
sock = sock_alloc(); --分配socket
sock->type = type;
pf = net_families[family];
pf->create(net, sock, protocol);
*res = sock;
return 0;
}
那来看socket结构体。也许单独去理解该结构体比较麻烦,不过如果添加在源码中会好理解很多。
struct socket {
socket_state state; --socket 状态如:SS_CONNECTED
short type; --socket 类型如: SOCK_STREAM
unsigned long flags; --socket 旗标如: SOCK_ASYNC_NOSPACE
const struct proto_ops *ops; --协议特定的套接字操作。
struct fasync_struct *fasync_list; --异步唤醒list
struct file *file; --文件指针
struct sock *sk; --网络层对应的socket示例
wait_queue_head_t wait; --等待队列
};
分配一个socket,其实最最主要的是创建一个inode,在文件系统中,file和inode比较难理解。
引用http://hi.baidu.com/lxsbupt/blog/item/14409e10b38f41f7c2ce7910.html中对节点的理解。
1)进程每打开一个文件,就会有一个file结构与之对应。同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述被打开文件的属性,如文件的当前偏移量等信息。
2)两个不同的file结构可以对应同一个dentry结构。进程多次打开同一个文件时,对应的只有一个dentry结构。Dentry结构存储目录项和对应文件(inode)的信息。
3)在存储介质中,每个文件对应唯一的inode结点,但是每个文件又可以有多个文件名。即可以通过不同 的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentry和inode的关系。
4)Inode中不存储文件的名字,它只存储节点号;而dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode。
5)不同的dentry则是同个文件链接(ln命令)来实现的。
再引用http://blog.csdn.net/yalizhi123/archive/2010/08/16/5815661.aspx中对节点的描述。
inode 译成中文就是索引节点。每个存储设备或存储设备的分区(存储设备是硬盘、软盘、U盘 ... ... )被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode呢,就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快的找到相对应的文件。
做个比喻,比如一本书,存储设备或分区就相当于这本书,Block相当于书中的每一页,inode 就相当于这本书前面的目录,一本书有很多的内容,如果想查找某部份的内容,我们可以先查目录,通过目录能最快的找到我们想要看的内容。
inode其实是对存储介质的每一个文件的描述。
static struct socket *sock_alloc(void)
{
struct inode *inode;
struct socket *sock;
inode = new_inode(sock_mnt->mnt_sb); --创建一个新的节点,其参数为supper blk
if (!inode)
return NULL;
sock = SOCKET_I(inode); --根据inode查找socket,就是根据contain_of找到其所属
的socket_alloc,然后再指向sock
inode->i_mode = S_IFSOCK | S_IRWXUGO; --设置节点的属性
inode->i_uid = current->fsuid; --其使用者id为当前进程的uid
inode->i_gid = current->fsgid;
get_cpu_var(sockets_in_use)++; --增加sockets_in_use计数。
put_cpu_var(sockets_in_use);
return sock;
}
在下面的函数中会涉及两个重要的结构体inode和address_space,对于inode的说明网上很多,而address_space并不多。
struct address_space {
struct inode *host; --所属者
struct radix_tree_root page_tree; --所有页的基树
spinlock_t tree_lock; --上面树的保护锁
unsigned int i_mmap_writable; --计数VM_SHARED的映射数
struct prio_tree_root i_mmap; --私有和共享的映射的树
struct list_head i_mmap_nonlinear;--VM_NONLINEAR映射链表
spinlock_t i_mmap_lock; --树的保护锁
unsigned int truncate_count;
unsigned long nrpages;
pgoff_t writeback_index;
const struct address_space_operations *a_ops;
unsigned long flags;
struct backing_dev_info *backing_dev_info;
spinlock_t private_lock;
struct list_head private_list;
struct address_space *assoc_mapping;
}
下面的函数主要是创建一个inode,并对其中的部分成员初始化。
static struct inode *alloc_inode(struct super_block *sb)
{
static const struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static const struct file_operations empty_fops;
struct inode *inode;
if (sb->s_op->alloc_inode)
inode = sb->s_op->alloc_inode(sb); --对于socket而言,其会调用sock_alloc_inode
else
inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);
if (inode) {
struct address_space * const mapping = &inode->i_data;
inode->i_sb = sb; --节点supper blk
inode->i_blkbits = sb->s_blocksize_bits; --以位为单位的块大小
inode->i_flags = 0;
atomic_set(&inode->i_count, 1); --引用计数
inode->i_op = &empty_iops; --索引节点操作表
inode->i_fop = &empty_fops; --默认的索引节点操作
inode->i_nlink = 1; --硬链接数
atomic_set(&inode->i_writecount, 0); --写计数
inode->i_size = 0; --以字节为单位的文件大小
inode->i_blocks = 0; --文件的块数
inode->i_bytes = 0; --使用的字节数
inode->i_generation = 0; --索引节点版本号
inode->i_pipe = NULL; --管道信息
inode->i_bdev = NULL; --块设备驱动
inode->i_cdev = NULL; --cdev驱动
inode->i_rdev = 0; --即dev_t
inode->dirtied_when = 0; --首次修改时间
spin_lock_init(&inode->i_lock);
lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
mutex_init(&inode->i_mutex);
lockdep_set_class(&inode->i_mutex, &sb->s_type->i_mutex_key);
init_rwsem(&inode->i_alloc_sem);
lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->i_alloc_sem_key);
mapping->a_ops = &empty_aops; --映射的操作表
mapping->host = inode; --映射的所有者
mapping->flags = 0; --错误bits和gfp的标示
mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
mapping->assoc_mapping = NULL;
mapping->backing_dev_info = &default_backing_dev_info;
mapping->writeback_index = 0;
inode->i_private = NULL;
inode->i_mapping = mapping;
}
return inode;
}
struct socket_alloc {
struct socket socket;
struct inode vfs_inode;
};
下面的函数很好理解,咱就不多费口舌了。
static struct inode *sock_alloc_inode(struct super_block *sb)
{
struct socket_alloc *ei;
ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
init_waitqueue_head(&ei->socket.wait);
ei->socket.fasync_list = NULL; --异步通知链表
ei->socket.state = SS_UNCONNECTED; --socket状态为未连接
ei->socket.flags = 0;
ei->socket.ops = NULL;
ei->socket.sk = NULL;
ei->socket.file = NULL;
return &ei->vfs_inode;
}
再次回到alloc_socket中,我们创建了一个inode,就是完成了inode = new_inode(sock_mnt->mnt_sb)。
到此我们就完成了sock = sock_alloc();那下面就是根据不同的协议类型填充不同的socket。其实现代码如下:
pf = rcu_dereference(net_families[family]);
pf->create(net, sock, protocol);
我们传入的family为AF_INET,那这里面的create函数会指向inet_create.关于其具体的实现下个星期继续。
阅读(3405) | 评论(0) | 转发(1) |