下面通过学习linux 1.2.13源码进一步理解socket通信机制。对该版本源码的学习主要参考《Linux内核网络栈源代码情景分析》(曹桂平 编著)。

  要理解socket的本质,就要理解当调用socket函数时,该函数到底创建了什么?返回了什么?

int  socket(int family, int type, int protocol);

  socket 函数为用户层函数,该函数对应的内核函数为sock_socket(socket.c文件),源码如下:

static int sock_socket(int family, int type, int protocol)
{
int i, fd;
struct socket *sock;
struct proto_ops *ops; /* Locate the correct protocol family. */
for (i = ; i < NPROTO; ++i)
{
if (pops[i] == NULL) continue;
if (pops[i]->family == family)
break;
} if (i == NPROTO)
{
return -EINVAL;
} ops = pops[i]; /*
* Check that this is a type that we know how to manipulate and
* the protocol makes sense here. The family can still reject the
* protocol later.
*/ if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
type != SOCK_SEQPACKET && type != SOCK_RAW &&
type != SOCK_PACKET) || protocol < )
return(-EINVAL); /*
* 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.
*/ if (!(sock = sock_alloc()))
{
printk("NET: sock_socket: no more sockets\n");
return(-ENOSR); /* Was: EAGAIN, but we are out of
system resources! */
} sock->type = type;
sock->ops = ops;
if ((i = sock->ops->create(sock, protocol)) < )
{
sock_release(sock);
return(i);
} if ((fd = get_fd(SOCK_INODE(sock))) < )
{
sock_release(sock);
return(-EINVAL);
} return(fd);
}

   sock_socket 函数完成如下工作:

(1)分配socket、sock结构,这两个结构在网络栈的不同层次表示一个套接字连接。

(2)分配inode、file结构用于普通文件操作。

(3)分配一个文件描述符并返回给应用程序作为以后的操作句柄。

  sock_alloc 函数用于分配一个inode节点,并返回该节点的socket指针

struct socket *sock_alloc(void)
{
struct inode * inode;
struct socket * sock; inode = get_empty_inode();
if (!inode)
return NULL; inode->i_mode = S_IFSOCK;
inode->i_sock = ;
inode->i_uid = current->uid;
inode->i_gid = current->gid; sock = &inode->u.socket_i;
sock->state = SS_UNCONNECTED;
sock->flags = ;
sock->ops = NULL;
sock->data = NULL;
sock->conn = NULL;
sock->iconn = NULL;
sock->next = NULL;
sock->wait = &inode->i_wait;
sock->inode = inode; /* "backlink": we could use pointer arithmetic instead */
sock->fasync_list = NULL;
sockets_in_use++;
return sock;
}

inode的定义如下

/* include/fs.h */
struct inode {
dev_t i_dev;
unsigned long i_ino;
umode_t i_mode;
nlink_t i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
off_t i_size;
time_t i_atime;
time_t i_mtime;
time_t i_ctime;
unsigned long i_blksize;
unsigned long i_blocks;
unsigned long i_version;
struct semaphore i_sem;
struct inode_operations * i_op;
struct super_block * i_sb;
struct wait_queue * i_wait;
struct file_lock * i_flock;
struct vm_area_struct * i_mmap;
struct inode * i_next, * i_prev;
struct inode * i_hash_next, * i_hash_prev;
struct inode * i_bound_to, * i_bound_by;
struct inode * i_mount;
unsigned short i_count;
unsigned short i_wcount;
unsigned short i_flags;
unsigned char i_lock;
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_sock;
unsigned char i_seek;
unsigned char i_update;
union {
struct pipe_inode_info pipe_i;
struct minix_inode_info minix_i;
struct ext_inode_info ext_i;
struct ext2_inode_info ext2_i;
struct hpfs_inode_info hpfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
struct xiafs_inode_info xiafs_i;
struct sysv_inode_info sysv_i;
struct socket socket_i;
void * generic_ip;
} u;
};

  inode 结构是文件系统的一个结构体,该结构体中的成员变量u指明了该inode结构具体的文件类型,当inode是用于socket通信时,u的值就为socket_i。sock_alloc 的作用就是创建inode结构体,然后返回socket_i的地址。至于具体如何分配inode涉及到文件系统方面的知识,这里暂不讨论。

  当协议族为AF_INET时,ops->create 将调用inet_create(struct socket*sock, int protocol)函数。该函数将创建一个sock结构体并使得socket的data指针指向该sock结构体。

static int inet_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct proto *prot;
int err; sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
if (sk == NULL)
return(-ENOBUFS);
sk->num = ;
sk->reuse = ;
switch(sock->type)
{
case SOCK_STREAM:
case SOCK_SEQPACKET:
if (protocol && protocol != IPPROTO_TCP)
{
kfree_s((void *)sk, sizeof(*sk));
return(-EPROTONOSUPPORT);
}
protocol = IPPROTO_TCP;
sk->no_check = TCP_NO_CHECK;
prot = &tcp_prot;
break; case SOCK_DGRAM:
if (protocol && protocol != IPPROTO_UDP)
{
kfree_s((void *)sk, sizeof(*sk));
return(-EPROTONOSUPPORT);
}
protocol = IPPROTO_UDP;
sk->no_check = UDP_NO_CHECK;
prot=&udp_prot;
break; case SOCK_RAW:
if (!suser())
{
kfree_s((void *)sk, sizeof(*sk));
return(-EPERM);
}
if (!protocol)
{
kfree_s((void *)sk, sizeof(*sk));
return(-EPROTONOSUPPORT);
}
prot = &raw_prot;
sk->reuse = ;
sk->no_check = ; /*
* Doesn't matter no checksum is
* performed anyway.
*/
sk->num = protocol;
break; case SOCK_PACKET:
if (!suser())
{
kfree_s((void *)sk, sizeof(*sk));
return(-EPERM);
}
if (!protocol)
{
kfree_s((void *)sk, sizeof(*sk));
return(-EPROTONOSUPPORT);
}
prot = &packet_prot;
sk->reuse = ;
sk->no_check = ; /* Doesn't matter no checksum is
* performed anyway.
*/
sk->num = protocol;
break; default:
kfree_s((void *)sk, sizeof(*sk));
return(-ESOCKTNOSUPPORT);
}
sk->socket = sock;
#ifdef CONFIG_TCP_NAGLE_OFF
sk->nonagle = ;
#else
sk->nonagle = ;
#endif
sk->type = sock->type;
sk->stamp.tv_sec=;
sk->protocol = protocol;
......
sk->timer.function = &net_timer;
skb_queue_head_init(&sk->back_log);
sk->blog = ;
sock->data =(void *) sk; //socket 指向 sock
sk->dummy_th.doff = sizeof(sk->dummy_th)/;
......
if (sk->prot->init)
{
err = sk->prot->init(sk);
if (err != )
{
destroy_sock(sk);
return(err);
}
}
return();
}

  最后调用get_fd 返回一个文件描述符给上层应用。

/* socket.c */
static int get_fd(struct inode *inode)
{
int fd;
struct file *file; /*
* Find a file descriptor suitable for return to the user.
*/ file = get_empty_filp(); // 获取一个闲置的file结构
if (!file)
return(-); for (fd = ; fd < NR_OPEN; ++fd)
if (!current->files->fd[fd])
break;
if (fd == NR_OPEN)
{
file->f_count = ;
return(-);
} FD_CLR(fd, &current->files->close_on_exec);
current->files->fd[fd] = file;
file->f_op = &socket_file_ops; // socket 文件操作
file->f_mode = ;
file->f_flags = O_RDWR;
file->f_count = ;
file->f_inode = inode;
if (inode)
inode->i_count++;
file->f_pos = ;
return(fd);
}

  get_fd 用于为网络套接字分配一个文件描述符,分配描述符的同时需要一个file结构,每个file结构都需要一个inode结构对应。内核维护一个file结构数据,get_empty_filp 函数即通过检查该数组,获取一个闲置的成员。f_op 字段的赋值实现了网络操作的普通文件接口。如果调用write、read函数进行操作就会调用相应的sock_read 和 sock_write 函数。

  如何根据文件描述如fd找到相应的sock?

网络协议栈学习(二)创建 socket的更多相关文章

  1. 网络协议栈学习(一)socket通信实例

    网络协议栈学习(一)socket通信实例 该实例摘自<linux网络编程>(宋敬彬,孙海滨等著). 例子分为服务器端和客户端,客户端连接服务器后从标准输入读取输入的字符串,发送给服务器:服 ...

  2. 网络编程学习笔记-浅析socket

    一.问题的引入——socket的引入是为了解决不同计算机间进程间通信的问题 .socket与进程的关系 ).socket与进程间的关系:socket 用来让一个进程和其他的进程互通信息(IPC),而S ...

  3. 网络编程学习笔记:Socket编程

    文的主要内容如下: 1.网络中进程之间如何通信? 2.Socket是什么? 3.socket的基本操作 3.1.socket()函数 3.2.bind()函数 3.3.listen().connect ...

  4. 网络编程学习笔记--1.socket可读可写条件

    转至 :http://blog.csdn.net/majianfei1023/article/details/45788591 socket可读可写条件,经常做为面试题被问,因为它考察被面试者对网络编 ...

  5. Linux网络编程学习(二) ----- 进程控制(第三章)

    1.进程和程序 程序是一个可执行文件,而一个进程是一个执行中的程序实例.一个进程对应于一个程序的执行,进程是动态的,程序是静态的,多个进程可以并发执行同一个程序.比如几个用户可以同时运行一个编辑程序, ...

  6. 网络编程学习二(IP与端口)

    InetAddress类 封装计算机的ip地址,没有端口 // 使用getLocalHost方法创建InetAddress对象 InetAddress addr = InetAddress.getLo ...

  7. pipelinewise 学习二 创建一个简单的pipeline

    pipelinewise 提供了方便的创建简单pipeline的命令,可以简化pipeline 的创建,同时也可以帮我们学习 生成demo pipeline pipelinewise init --n ...

  8. sublime text 2学习(二):创建可复用的代码片段

    对于前端工程师来讲,写一个html页面的基本结构是体力活,每次去拷贝一个也麻烦,sublime text 2 提供了一个很好的复用代码片段.下面介绍一下创建一个html5的代码片段的过程. 在菜单上点 ...

  9. micronaut 学习 二 创建一个简单的服务

    micronaut 提供的cli 很方便,我们可以快速创建具有所需特性的应用,以下是一个简单的web server app 创建命令 mn create-app hello-world 效果 mn c ...

随机推荐

  1. P4396 [AHOI2013]作业

    题目链接 luogu4396 思路 唯有水题暖人心 咕了4天,今天跟着std对拍才做出来不得不说题解真的水的一批 先离散化一下 第一问差分询问,权值树状数组套一套就好了 \(nlog_{n}\) 第二 ...

  2. HDU 1358 Period(KMP+最小循环节)题解

    思路: 这里只要注意一点,就是失配值和前后缀匹配值的区别,不懂的可以看看这里,这题因为对子串也要判定,所以用前后缀匹配值,其他的按照最小循环节做 代码: #include<iostream> ...

  3. Ubuntu 16.04设置IP、网关、DNS

    说明:在网上给的教程上面通常会有这样的一个误导思路,按照配置文件设置后会不生效的问题,甚至没有一点效果,经过排查发现Linux下设置IP这个话题的入口线索应该分为两种:1为Server版,2为Desk ...

  4. 【第三十八章】 springboot+docker(maven)

    回顾上一章的整个部署过程: 使用"mvn install"进行打包jar 将jar移动到与Dockerfile文件相同的文件夹下 编写Dockerfile文件 使用"do ...

  5. js文字转移效果

    这个例子算是有点样子的. 思路: 字符串操作.左框里面先是预设的.点击按钮时截取左框中的字符串的前一个字符到右框里的字符串后面,以此循环.点击按钮时按钮变为灰色,在循环完成后恢复.计数的总数(右边)是 ...

  6. Cocos2d-x学习笔记(四) HelloWorld场景类

    类定义原型如下: #ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" ...

  7. shell wc命令 统计行数

    users文件内容 hello world 我们要统计 users 文件的行数,执行以下命令: $ wc -l users users 也可以将输入重定向到 users 文件: $ wc -l < ...

  8. MongoDB(课时19 数据删除)

    3.4.4 删除数据 在MongoDB里面删除数据使用“remove()”.但是这个函数有两个可选项: 删除条件:满足条件的数据被删除. 只删除一个数据:设置为true或者是1表示只删除一个. 范例: ...

  9. MongoDB(课时6 关系查询)

    支持关系查询操作:大于($gt),小于($lt),大于等于($gte),小于等于($lte),不等于($ne ),等于(key:value 或 $eq).想让这些操作正常使用,需要准备一个数据集合. ...

  10. Qt5_pro_01

    1. QT += core gui \ sql \ #ZC: 这个对应 #include <SQL/???> (如<QtSql/QSqlDatabase><QtSql/Q ...