scoket系统调用主要完成socket的创建,必要字段的初始化,关联传输控制块,绑定文件等任务,完成返回socket绑定的文件描述符;

 /**
* socket函数调用关系
* sys_socket
* |-->sock_create
* | |-->__sock_create
* | |-->inet_create
* |-->sock_map_fd
*/
 SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
int retval;
struct socket *sock;
int flags; /* Check the SOCK_* constants for consistency. */
BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK); /* 取得标志 */
flags = type & ~SOCK_TYPE_MASK; /* 除此标记之外还有标记,错误 */
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL; /* 取得类型 */
type &= SOCK_TYPE_MASK; /* 标记以O_NONBLOCK为准 */
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; /* 创建socket */
retval = sock_create(family, type, protocol, &sock);
if (retval < )
goto out; /* 创建socket文件并绑定描述符 */
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < )
goto out_release; out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval; out_release:
sock_release(sock);
return retval;
}

因为sock_create调用层次较多,放到下面来分析,此处先看下sock_map_fd函数,该函数负责分配文件,并与socket进行绑定;

 /* 套接口与文件描述符绑定 */
static int sock_map_fd(struct socket *sock, int flags)
{
struct file *newfile;
/* 获取未使用的文件描述符 */
int fd = get_unused_fd_flags(flags);
if (unlikely(fd < ))
return fd; /* 分配socket文件 */
newfile = sock_alloc_file(sock, flags, NULL);
if (likely(!IS_ERR(newfile))) {
/* fd和文件进行绑定 */
fd_install(fd, newfile);
return fd;
} /* 释放fd */
put_unused_fd(fd);
return PTR_ERR(newfile);
}

下面来分析sock_create流程,其主要工作为创建socket,并进行必要的初始化;

 int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, );
}

__socket_create函数负责必要的检查项,创建socket,必要的初始化,之后调用对应协议族的pf->create函数来创建传输控制块,并且与socket进行关联;

 /* 创建socket */
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; /*
* Check protocol is in range
*/
/* 检查协议族 */
if (family < || family >= NPROTO)
return -EAFNOSUPPORT; /* 检查类型 */
if (type < || type >= SOCK_MAX)
return -EINVAL; /* Compatibility. This uglymoron is moved from INET layer to here to avoid
deadlock in module load.
*/
/* ipv4协议族的packet已经废除,检测到,则替换成packet协议族 */
if (family == PF_INET && type == SOCK_PACKET) {
pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
current->comm);
family = PF_PACKET;
} /* 安全模块检查套接口 */
err = security_socket_create(family, type, protocol, kern);
if (err)
return err; /*
* 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已经绑定 */
sock = sock_alloc();
if (!sock) {
net_warn_ratelimited("socket: no more sockets\n");
return -ENFILE; /* Not exactly a match, but its the
closest posix thing */
} /* 设定类型 */
sock->type = type; #ifdef CONFIG_MODULES
/* 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!
*/
if (rcu_access_pointer(net_families[family]) == NULL)
request_module("net-pf-%d", family);
#endif rcu_read_lock();
/* 找到协议族 */
pf = rcu_dereference(net_families[family]);
err = -EAFNOSUPPORT;
if (!pf)
goto out_release; /*
* We will call the ->create function, that possibly is in a loadable
* module, so we have to bump that loadable module refcnt first.
*/
/* 增加模块的引用计数 */
if (!try_module_get(pf->owner))
goto out_release; /* Now protected by module ref count */
rcu_read_unlock(); /* 调用协议族的创建函数 */
err = pf->create(net, sock, protocol, kern);
if (err < )
goto out_module_put; /*
* Now to bump the refcnt of the [loadable] module that owns this
* socket at sock_release time we decrement its refcnt.
*/
if (!try_module_get(sock->ops->owner))
goto out_module_busy; /*
* Now that we're done with the ->create function, the [loadable]
* module can have its refcnt decremented
*/
module_put(pf->owner);
err = security_socket_post_create(sock, family, type, protocol, kern);
if (err)
goto out_sock_release;
*res = sock; return ; out_module_busy:
err = -EAFNOSUPPORT;
out_module_put:
sock->ops = NULL;
module_put(pf->owner);
out_sock_release:
sock_release(sock);
return err; out_release:
rcu_read_unlock();
goto out_sock_release;
}
EXPORT_SYMBOL(__sock_create);

对于PF_INET协议族来讲,上述的pf->create函数将调用inet_create函数;

 static const struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};

inet_create函数负责创建传输控制块,并且将socket与传输控制块进行关联;

 /*
* Create an inet socket.
*/ /* 创建与该接口对应的传输控制块并关联 */
static int inet_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
struct inet_protosw *answer;
struct inet_sock *inet;
struct proto *answer_prot;
unsigned char answer_flags;
int try_loading_module = ;
int err; /* 检查协议 */
if (protocol < || protocol >= IPPROTO_MAX)
return -EINVAL; /* 设置接口的状态为未连接 */
sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */
lookup_protocol:
err = -ESOCKTNOSUPPORT;
rcu_read_lock();
list_for_each_entry_rcu(answer, &inetsw[sock->type], list) { err = ;
/* Check the non-wild match. */
/* 匹配协议成功 */
if (protocol == answer->protocol) {
/* 传入为某指定协议,成功*/
if (protocol != IPPROTO_IP)
break; /* 未指定协议,继续查找 */ }
/* 未指定协议或者未匹配成功的分支 */
else {
/* Check for the two wild cases. */
/* 如果传入为未指定协议 */
if (IPPROTO_IP == protocol) {
/* 则指定为当前协议,成功 */
protocol = answer->protocol;
break;
} /* 指定了传入协议,但是均未匹配成功 */ /* 当前正在匹配的协议通用协议,则使用之 */
if (IPPROTO_IP == answer->protocol)
break;
} /* 循环查找结束了,还未找到 */
/* 传入了某指定协议,未找到匹配,并且没有通用协议 */
err = -EPROTONOSUPPORT;
} /*
未找到对应inet_protosw实例
加载对应的协议模块,重新查找
*/
if (unlikely(err)) {
/* 尝试加载的模块不超过2次 */
if (try_loading_module < ) {
rcu_read_unlock();
/*
* Be more specific, e.g. net-pf-2-proto-132-type-1
* (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
*/
/* 第一次,加载指定协议和类型的模块 */
if (++try_loading_module == )
request_module("net-pf-%d-proto-%d-type-%d",
PF_INET, protocol, sock->type);
/*
* Fall back to generic, e.g. net-pf-2-proto-132
* (net-pf-PF_INET-proto-IPPROTO_SCTP)
*/
/* 第二次,加载只指定协议的模块 */
else
request_module("net-pf-%d-proto-%d",
PF_INET, protocol);
goto lookup_protocol;
}
/* 超过2次,则查找失败 */
else
goto out_rcu_unlock;
} err = -EPERM; /* 判断是否允许创建sock-raw套接口 */
if (sock->type == SOCK_RAW && !kern &&
!ns_capable(net->user_ns, CAP_NET_RAW))
goto out_rcu_unlock; /* 设置套接口操作 */
sock->ops = answer->ops;
/* 临时存储协议的操作和标志 */
answer_prot = answer->prot;
answer_flags = answer->flags;
rcu_read_unlock(); WARN_ON(!answer_prot->slab); err = -ENOBUFS;
/* 分配传输控制块 */
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);
if (!sk)
goto out; err = ;
/* 设置重用地址和端口标记 */
if (INET_PROTOSW_REUSE & answer_flags)
sk->sk_reuse = SK_CAN_REUSE; inet = inet_sk(sk); /* 设置是否为面向连接的控制块 */
inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != ; inet->nodefrag = ; /* 如果类型是原始套接字 */
if (SOCK_RAW == sock->type) {
/* 设置本地端口为协议号 */
inet->inet_num = protocol; /* 协议为ipproto_raw */
if (IPPROTO_RAW == protocol)
/* 标记需要自己构建ip首部 */
inet->hdrincl = ;
} /* 设置是否支持pmtu */
if (net->ipv4.sysctl_ip_no_pmtu_disc)
inet->pmtudisc = IP_PMTUDISC_DONT;
else
inet->pmtudisc = IP_PMTUDISC_WANT; /* 出事连接控制块 */
inet->inet_id = ; /* 连接控制块的初始化 */
sock_init_data(sock, sk); sk->sk_destruct = inet_sock_destruct;
sk->sk_protocol = protocol;
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; inet->uc_ttl = -;
inet->mc_loop = ;
inet->mc_ttl = ;
inet->mc_all = ;
inet->mc_index = ;
inet->mc_list = NULL;
inet->rcv_tos = ; sk_refcnt_debug_inc(sk); /* 设置了本地端口 */
if (inet->inet_num) {
/* It assumes that any protocol which allows
* the user to assign a number at socket
* creation time automatically
* shares.
*/ /* 设置网络序的源端口 */
inet->inet_sport = htons(inet->inet_num);
/* Add to protocol hash chains. */
/* 加入到hash */
err = sk->sk_prot->hash(sk);
if (err) {
sk_common_release(sk);
goto out;
}
} /* 如果有init则调用init初始化 */
if (sk->sk_prot->init) {
err = sk->sk_prot->init(sk);
if (err) {
sk_common_release(sk);
goto out;
}
} if (!kern) {
err = BPF_CGROUP_RUN_PROG_INET_SOCK(sk);
if (err) {
sk_common_release(sk);
goto out;
}
}
out:
return err;
out_rcu_unlock:
rcu_read_unlock();
goto out;
}

socket系统调用的更多相关文章

  1. 从零开始—Socket系统调用和多态封装

    1 重新搭建实验环境 前面都是用实验楼环境做的实验,偷的懒总是要还的,这一次重装环境前后花了十几个小时,踩了无数的坑. 1.1 Ubuntu和LINUX内核的区别 Ubuntu是基于LINUX内核编写 ...

  2. socket相关系统调用的调用流程

    最近一直在读内核网络协议栈源码,这里以ipv4/tcp为例对socket相关系统调用的流程做一个简要整理,这些相关系统调用的内部细节虽然各有不同,但其调用流程则基本一致: 调用流程: (1)系统调用 ...

  3. linux socket编程系统调用栈

    目录 一.网络协议参考模型简介 二.SOCKET概述 三.SOCKET基本数据结构 1.TCP通信编程 2.服务器端实例代码 3.客户端实例代码 4.头文件socketwrapper.h 5.程序实现 ...

  4. Socket 套接字的系统调用

    socket 结构 /** * struct socket - general BSD socket * @state: socket state (%SS_CONNECTED, etc) * @ty ...

  5. [转]C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

  6. 【转】网络编程socket基本API详解

    转自:http://www.cnblogs.com/luxiaoxun/archive/2012/10/16/2725760.html socket socket是在应用层和传输层之间的一个抽象层,它 ...

  7. C++ 系列:socket 资料收集

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  8. Socket

    Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 以J2SDK-1.3为例,Socket和ServerSocket类库位于 ...

  9. Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

随机推荐

  1. NEERC 15 (10/12)

    NEERC=Not Easy European Regional Contest 据说NEERC天天被搬,赶紧做了好了.在Claris和Google的帮助下做了10题,感谢cls. http://co ...

  2. P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold 解题报告

    P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold 题意 给一个字符串,每次可以从两边中的一边取一个字符,要求取出的字符串字典序最小 可以Hash+二分 也可以S ...

  3. 为smokeping添加日志开启debug

    用包管理工具安装smokeping没有自带日志输出,为了定位问题开启日志就成为第一需求. 1.修改smokeping的配置 # vim /etc/smokeping/config.d/General ...

  4. 数据库之Oracle的介绍与使用20180620

    /*******************************************************************************************/ 一.orac ...

  5. struts2的MVC模式

    MVC是一种架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而得到更好的开发和维护效率. 在MVC模式中,应用程序被划分成了模型(Model).视 ...

  6. [case]filesystem problem

    e2fsck -Nov-) fsck.ext4: Superblock invalid, trying backup blocks... fsck.ext4: Bad magic number in ...

  7. bzoj 5015 [Snoi2017]礼物 矩阵乘法

    5015: [Snoi2017]礼物 Time Limit: 15 Sec  Memory Limit: 512 MBSubmit: 163  Solved: 115[Submit][Status][ ...

  8. Java入门:注册模块的实现

    1.主活动图 用户选择注册选项,进入注册界面,开始输入注册信息,到最后完成注册.具体的活动图如下: 以上活动图中,矩形框里的操作不是在一个类里面实现的,而是通过Form类和UserService类来实 ...

  9. mysql 设置默认编码为 utf8

    vi /etc/mysql/mysql.conf.d/mysqld.cnf [client] default-character-set=utf8 [mysql] default-character- ...

  10. 「Vue」实用组件

    一.时间格式 1.安装Moment模块 npm i moment -S2.main.js中设置全局过滤器 import moment from 'moment' Vue.filter('ctime', ...