也许学过从事过网络编程的人都知道socket是什么,表示什么?socket的英文原义是“孔”或“插座。但我们用网络术语将它称为“套接字”(见Linux网络编程),但是我习惯叫“套接口”,可能是受Unix网络编程的影响。里面是这样解释的:首先Socket作为网络API之一,跟XTI一样,是应用层或其他协议层访问接口,其次具体使用的套接口是与Unix管道某端口类似的机制,应用程序和内核(实际是内核实现的网络堆栈)可一通过它来通信,也即进程间的套接字通信;这里我们不讨论怎么称呼他,我们在乎的是如何很好的使用它。
为什我们称为网络堆栈?我想这得益于他的层次结构来的吧,我们知道ISO把他分为7层:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。但是实际应用中我们一般只会用会用到四层也即典型的TCP/IP协议(Transmission
这里我就不讲解每次的用处,懂网络编程的朋友估计也不需要我去赘述。下面我将分析有应用层至内核这些机制进行互联互通的。我就成为:Linux网络编程深究
Linux网络编程深究
上图我们还可以细分:
1、有了原始套接口,进程可以读写ICMPv4,IGMPv4和ICMPv6等分组。举例来说:ping就是使用原始套接口发送ICMP回射请求并接收ICMP回射应答。多播路由守护程序mrouted也使用原始套接口发送和接收IGMPv4分组。这个能力还使得使用ICMP或IGMP构造的应用程序能够完全作为用户进程处理,而不必往内核中添加额外代码。
2、有了原始套接口,进程可以读写内核不处理其协议字段的IPv4数据报。大多数内核仅仅处理该字段值为1(ICMP),2(IGMP),6(TCP)和17(UDP)的数据报。然而为协议字段定义的值还有不少。
3、有了原始套接口,进程还可以使用IP_HDRINCL套接口选项执行构造IPv4头部。这个能力可用于构造TCP或UDP分组等。
我们可以通过给套接口Socket函数的第二个参数设定为SOCK_RAW既要告知内核返回一个原是套接口结构。这不是我们的话重点,也就是将到哪提到那我就随便提下。
1
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8
9
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19 #include
#define
#define
#define
#define
这里就讲下
SERVPORT
BACKLOG
1、未完成等待队列:每个SYN分节对应其中一项,已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三次握手过程,这些套接口处于SYN_RCVD状态;
2、已完成连接队列:每个完成TCP三次握手过程的客户对应其中某一项。
所以这个BACKLOG
我们把头文件声明好了,接下来就讲解Socket系统调用函数的怎么创建返回一个sockfd给应用程序的吧:
int
Family:表示协议类型
Type
Protocol:看字面意思就知道是要创建的套接口遵守的协议了。但是他表示前面family中的 一种,通常family里只有一中的,所以它通常为0
因为前面我们讲过,我们一般的网络通讯都是用TCP/IP的,所以通常该函数几个参数我们可以规定了,就当是默认的吧,当初我就是这样理解该函数的。
Int listenfd
其实该函数是系统调用函数,它类似于open、read,write等函数通过系统调用函数:sys_open,sys_read,sys_write实现相应的操作。但是在Linux中socket是通过sys_socketcall来逐步完成的:
asmlinkage
{
Int
switch
{
case
er
If
return
return
break;
case
er
If
return
return
case
er
If
return
return
....
}
}
Sys_socketcall()函数有两个参数,第一个参数表示被调用的应用层接口函数,第二个指向被调用函数所需的参数,用户程序进行系统调用时传入的参数将原封不动的传递给内核网络堆栈相应底层函数,socket为sock_socket();那具体过程是如何传递的呢?我们以accept为例来讲述
一、
*/inux/accept.S
1.#define
2.#deifine
3.#define
4.#include
二、socket.S
15.#include
16.#...........
17.#define
18.#define
19..text
26.#ifndef
27.Ifndef
28.#define
29.#else
30.#define
31.#enfif
32.#endif
33..globl
34.ENTRY
36.movl
37.movl
38.Movl
39.Lea
40.Int
41.Movl
42.Cmpl
....
其中
#ifdef
#define
SYS_##syscall_name
#else
#define
#endif
所以经过预处理后socket.S中37行就很好理解了:(等价于)
Movl
三、
那socket.S中的SYS_socketcall从哪来的呢?我们通过查找/usr/include/bits/syscall.h可以看到:
286
287
288
289
即把SYS_socketcall定义为__NR_socketcall
#define
#define
#define
#define
#define
换句话说就是把socket.S中37行的目的就是为了将sys_socketcall对应的系统调用号(102)被赋值予eax中,从而使得套接字系统调用进入到相应的入口函数中,那么调用系统函数的参数是怎么样传递的呢?因为系统调用中参数从用户态向内核态的传递是通过寄存器完成的,eax存放的是当前的系统调用号,ebx存放的是改掉用好对应的第一个参数,ecx存放的是该调用号对应的第二个,Ecx为第三个...在系统调用过程中编译器仅从堆栈中获取数据,而不会从寄存器中获取任何数据,所以在进入system_call前,用户程序会将参数放置相应寄存器中system_call函数执行时就会将这些寄存器中的数据全部压栈,因此系统调用服务例程便可从被system_call函数压入的堆栈中方便的获取数据,对数据的修改也在堆栈中进行,所以当系统调用结束后用户程序便可直接从堆栈中获取(被修改过的)数据。那么SOCKOP_socket代表什么呢?
10
11
12
13
14
15
16
17
18
19
20
但是sys_socketcall(int
对比net.h和socketcall.h我们可以得出:二者中个函数调用的名称(标识)虽不同,但是有一点我们可以发现,就是他们的值想同:(即)
所以:
asmlinkage
10
11
12
13
14
15
16
17
18
19
20
21
22
23
中的switch也就找到相应的case了。接下来就是sock_socket的工作了...这就是socket内陷内核的过程,交给内核后,内核会根据family输的的参数决定域做函数集用于socket结构中的ops字段赋值,如inet_proto_ops,unix_proto_ops。之后系统调用sock->ops->create生成一个下层sock(内核socket结构)结构并将其绑定在当前进程上。最后调用get_fd()分配一个文件描述符及其对应的FILE结构返回给应用层,另一部分会与文件描述符绑定并还回给应用层,该描述符就被赋值到listenfd了。其他函数我想大家也就一目了然了。。