对于用户态编程的第一个函数是

fd=socket(AF_INET,SOCK_STREAM,0);

该函数会通过系统调用进入内核,内核的实现如下:

kernel\net\Socket.c

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{int retval;struct socket *sock;int flags;...基本类型:由低8位设置共8种类型特殊类型:由其他bit位设置下面2句的意思就是判断socket创建的类型只能是基本类型(SOCK_STREAM,SOCK_DGRAM等)或者(SOCK_CLOEXEC,SOCK_NONBLOCK))如果还包含其他的高位地址的类型,则返回错误的参数flags = type & ~SOCK_TYPE_MASK;if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))return -EINVAL;type &= SOCK_TYPE_MASK;对于类似alpha或者mips架构有可能会将SOCK_NONBLOCK设置为不等于O_NONBLOCK,因此针对我们的ARM架构可以忽略这条if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;下面就是创建一个socket文件,注意这里的sock指针作为传入参数在sock_create完成赋值retval = sock_create(family, type, protocol, &sock);if (retval < 0)goto out;通过nlmsg_multicast将socket创建的消息发送出去if (retval == 0)sockev_notify(SOCKEV_SOCKET, sock);将这个新建的socket文件关联到一个文件中这个文件的fops为socket_file_ops,并返回这个关联的文件描述符retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));if (retval < 0)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_create(current->nsproxy->net_ns,...)          这里的第一个参数代表的是当前进程的网络命名空间

struct socket *sock;
const struct net_proto_family *pf;
...
sock = sock_alloc();
sock->type = type;    ----SOCK_STREAM/SOCK_DGRAM
//针对我们当前的sock而言,socket的family是AF_INET,同时AF_INET是PF_INET的别称,因此返回的pf就是inet_family_ops
pf = rcu_dereference(net_families[family]);
...
//调用的自然就是inet_create
err = pf->create(net, sock, protocol, kern);//针对当前的调用情况,这个值kern目前为0
*res = sock;  //返回新建的sock文件,这里的*res就是sock_create传入的最后一个参数

这里要说明的是net_families这是一个指针数组定义如下:

static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;

数组中的每一个元素都是一个指向结构体net_proto_family的指针,那么问题来了这些指针是怎么被放进去的呢,在sock.c文件中提供了一个函数sock_register用于填充这个数组。如下:

kernel\net\ipv4\Af_inet.c

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

inet_init

(void)sock_register(&inet_family_ops);
因此针对socket的创建最终就回来到inet_create这个函数

inet_create

这里inet_ehash_secret是一个4字节的整型数代表的是一个加密字串,如果这个字串还没有设置,同时报文既不是SOCK_RAW也不是SOCK_DGRAM,那么我们先设置加密字串,回到我们应用程序中,我们设置的类型是SOCK_STREAM,因此这里是需要设置这个加密字串的,但是只会设置一次
if (unlikely(!inet_ehash_secret))if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)build_ehash_secret();
设置当前socket状态为未连接
sock->state = SS_UNCONNECTED;
lookup_protocol:
这里的inetsw是一个静态链表类型的数组,保存的是像tcp或者udp协议对应的链表元素,因此下面这句的意思就是通过sock->type,具体到我当前的应用而言就是SOCK_STREAM,通过这个下标找到tcp对应的inet_protosw类型的answer指针
list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {....
}
更新相关字段,这里面sock->ops这个字段很关键
sock->ops = answer->ops;
answer_prot = answer->prot;
answer_no_check = answer->no_check;
answer_flags = answer->flags;
创建sock文件,这里的sk是struct sock *sk;
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
sk->sk_no_check = answer_no_check;
因为inet内嵌的第一个元素就是sock结构体,因此这里将sk强制转换为inet
inet = inet_sk(sk);
对于TCP中inet->is_icsk这个字段为true,其他协议为false
inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;
下面是是否使能MTU的路径发现机制,可以通过"/proc/sys/net/ipv4/ip_no_pmtu_disc"去设置
所谓的MTU的路径发现机制:我们知道如果应用层的数据报文过大,IP会分片,如果使能了MTU路径发现机制,那么应用层会逐步增大报文的长度尝试向对端发送报文,一旦路径中的主机发现报文超过了它设置的MTU值,就会丢弃报文,同时向报文的源端发送“报文过大”的ICMP消息。从而源端就可以知道在不分片的情况下链路最大可以设置的MTU
if (ipv4_config.no_pmtu_disc)inet->pmtudisc = IP_PMTUDISC_DONT;
elseinet->pmtudisc = IP_PMTUDISC_WANT;
inet->inet_id = 0;
利用struct socket进一步初始化struct sock结构体,这里也会设置sock的发送和接收的缓冲区的大小
sock_init_data(sock, sk);
...
if (sk->sk_prot->init) {err = sk->sk_prot->init(sk);if (err)sk_common_release(sk);}
这里的sk->sk_prot->init对应是TCP协议的tcp_prot中的init函数tcp_v4_init_sock

这里详细说明下inetsw中的元素通过inet_register_protosw的方式将下面协议逐个加进去的

static struct inet_protosw inetsw_array[] =
{{.type =       SOCK_STREAM,.protocol =   IPPROTO_TCP,.prot =       &tcp_prot,.ops =        &inet_stream_ops,.no_check =   0,.flags =      INET_PROTOSW_PERMANENT |INET_PROTOSW_ICSK,},{.type =       SOCK_DGRAM,.protocol =   IPPROTO_UDP,.prot =       &udp_prot,.ops =        &inet_dgram_ops,.no_check =   UDP_CSUM_DEFAULT,.flags =      INET_PROTOSW_PERMANENT,},{.type =       SOCK_DGRAM,.protocol =   IPPROTO_ICMP,.prot =       &ping_prot,.ops =        &inet_dgram_ops,.no_check =   UDP_CSUM_DEFAULT,.flags =      INET_PROTOSW_REUSE,},{.type =       SOCK_RAW,.protocol =   IPPROTO_IP,  /* wild card */.prot =       &raw_prot,.ops =        &inet_sockraw_ops,.no_check =   UDP_CSUM_DEFAULT,.flags =      INET_PROTOSW_REUSE,}
};

再说下最后一个函数,之前我们有提到的tcp_v4_init_sock

kernel\net\ipv4\Tcp_ipv4.c

static int tcp_v4_init_sock(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);tcp_init_sock(sk);icsk->icsk_af_ops = &ipv4_specific;#ifdef CONFIG_TCP_MD5SIGtcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific;
#endifreturn 0;
}

kernel\net\ipv4\Tcp.c

void tcp_init_sock(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);struct tcp_sock *tp = tcp_sk(sk);skb_queue_head_init(&tp->out_of_order_queue);tcp_init_xmit_timers(sk);tcp_prequeue_init(tp);INIT_LIST_HEAD(&tp->tsq_node);icsk->icsk_rto = TCP_TIMEOUT_INIT;tp->mdev = TCP_TIMEOUT_INIT;/* So many TCP implementations out there (incorrectly) count the* initial SYN frame in their delayed-ACK and congestion control* algorithms that we must have the following bandaid to talk* efficiently to them.  -DaveM*/tp->snd_cwnd = TCP_INIT_CWND;/* See draft-stevens-tcpca-spec-01 for discussion of the* initialization of these values.*/tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;tp->snd_cwnd_clamp = ~0;tp->mss_cache = TCP_MSS_DEFAULT;tp->reordering = sysctl_tcp_reordering;tcp_enable_early_retrans(tp);icsk->icsk_ca_ops = &tcp_init_congestion_ops;tp->tsoffset = 0;sk->sk_state = TCP_CLOSE;sk->sk_write_space = sk_stream_write_space;sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);icsk->icsk_sync_mss = tcp_sync_mss;/* Presumed zeroed, in order of appearance:*    cookie_in_always, cookie_out_never,*    s_data_constant, s_data_in, s_data_out*/这里会再次更新发送和接收缓冲区的大小,这个值可以通过下面的方式查看和修改:"/sys/kernel/ipv4/tcp_wmem_def"sk->sk_sndbuf = sysctl_tcp_wmem[1];sk->sk_rcvbuf = sysctl_tcp_rmem[1];local_bh_disable();sock_update_memcg(sk);sk_sockets_allocated_inc(sk);local_bh_enable();
}

Linux网络编程之socket创建相关推荐

  1. linux网络编程之Socket编程

    (1)socket套接字 1)在linux环境下,socket用于表示进程间网络通信的特殊文件类型,其本质是内核借助缓冲区形成的伪文件(不占磁盘空间,除此之外还有二进制文件,管道,字符文件). 2)伪 ...

  2. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...

  3. Linux网络编程之Socket套接字

    一.Socket到底是什么 socket 这个英文单词的原意是"插口""插槽", 在网络编程中,它的意思是可以通过插口接入的方式,快速完成网络连接和数据收发.你 ...

  4. Linux网络编程之socket文件传输示例

    本文所述示例程序是基于Linux平台的socket网络编程,实现文件传输功能.该示例是基于TCP流协议实现的socket网络文件传输程序.采用C语言编写.最终能够实现传输任何格式文件的文件传输程序. ...

  5. linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时

    一.使用alarm 函数设置超时 C++ Code  1 2 3 4 5 6 7 8 9 10 11 12 13   void handler( int sig) { } signal(SIGALRM ...

  6. linux多网卡网络编程,Linux网络编程之Socket初探

    Socket由来 Socket 的英文原意就是"孔"或"插座",现在,作为 BSD UNIX 的进程通讯机制,取其后一种意义.一起看下网络编程里说的socket ...

  7. linux网络编程之socket:使用fork并发处理多个client的请求

    在回射客户/服务器程序中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现.网络服务器通常用for ...

  8. linux网络编程之socket(十):shutdown 与 close 函数 的区别

    假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...

  9. Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解

    Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解 (1)sockaddr struct sockaddr { unsigned  short  sa_ ...

  10. 网络编程之socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

最新文章

  1. 肠·道 | 朱元方:产检消毒恐误伤菌脉,6大举措则促菌脉相承
  2. JAVA Web.xml 加载顺序
  3. 利用微信登录掘金网站的HTTP请求分析
  4. Lotus 下部门间用户的移动操作
  5. 【算法竞赛学习】资金流入流出预测-挑战Baseline_数据探索与分析1
  6. 云计算管理工具:根植热土
  7. c语言程序机试题及答案,C语言程序设计试题及答案解析(二)
  8. 基于simulink的Passive anti-islanding-UVP/OVP and UFP/OFP被动反孤岛模型仿真
  9. java bitmap 保存 jpg_Glide加载图片并保存到本地返回file,bitmap
  10. 涂鸦Zigbee SDK开发系列教程——2.环境搭建
  11. 门控时钟:Clock fating
  12. Java 小练习(简单)—合集
  13. 微服务下的链路追踪(Sleuth+Zipkin)
  14. Beauty diary
  15. C#调用TSC条码打印机打印条码(转)
  16. Win10安装使用Hadoop3.0.0
  17. PCA(主成分分析)、超弦/M理论、意识
  18. Fluent动网格【11】:弹簧光顺
  19. VMware Pro v14.1.1 官方版本及激活密钥
  20. Au 音频效果参考:延迟与回声

热门文章

  1. 韦东山驱动视频笔记——6.输入子系统之编写驱动程序
  2. 【转】勤奋小孩的遗言:没有理由不坚持。
  3. 为ashx文件启用session管理
  4. dataframe groupby_python pandas获取groupby之后的数据
  5. 程序反编译找pdf打开密码_即时密码保护PDF文件的免费工具
  6. Java多线程总结之线程安全队列Queue
  7. js 正则表达式总结
  8. 2015年度夏季假期学习内容
  9. 六、openstack安装之Horizon篇
  10. FLEX 字符串处理函数