注:本分类下文章大多整理自《深入分析linux内核源代码》一书,另有参考其他一些资料如《linux内核完全剖析》、《linux c 编程一站式学习》等,只是为了更好地理清系统编程和网络编程中的一些概念性问题,并没有深入地阅读分析源码,我也是草草翻过这本书,请有兴趣的朋友自己参考相关资料。此书出版较早,分析的版本为2.4.16,故出现的一些概念可能跟最新版本内核不同。

此书已经开源,阅读地址 http://www.kerneltravel.net

一、套接字socket


(一)、套接字在网络中的地位和作用




socket 在网络系统中的作用如下。
(1)socket 位于网络协议之上,屏蔽了不同网络协议之间的差异。
(2)socket 是网络编程的入口,它提供了大量的系统调用,构成了网络程序的主体。
(3)在Linux 系统中,socket 属于文件系统的一部分,网络通信可以被看作是对文件的
读取,使得我们对网络的控制和对文件的控制一样方便。

(二)、套接字接口的种类

Linux 支持多种套接字种类,不同的套接字种类称为“地址族”,这是因为每种套接字
种类拥有自己的通信寻址方法。Linux 所支持的套接字地址族见表12.3。
Linux 将上述套接字地址族抽象为统一的 BSD 套接字接口,应用程序关心的只是 BSD 套
接字接口,而 BSD 套接字由各地址族专有的软件支持。一般而言,BSD 套接字可支持多种套
接字类型,不同的套接字类型提供的服务不同,Linux 所支持的部分 BSD 套接字类型见表
12.4,但表12.3 中的套接字地址族并不一定全部支持表12.4 中的这些套接字类型。




(三)、套接字的工作原理

INET 套接字就是支持 Internet 地址族的套接字,它位于TCP 之上,BSD 套接字之下,
如图12.8 所示,这里也体现了Linux 网络模块分层的设计思想。



INET 和 BSD 套接字之间的接口通过 Internet 地址族套接字操作集实现,这些操作集
实际是一组协议的操作例程,在include/linux/net.h 中定义为struct proto_ops:


 C++ Code 
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

 
struct proto_ops

{

    
int family;

    
int (*release) (
struct socket *sock);

    
int (*bind) (
struct socket *sock, 
struct sockaddr *umyaddr,

                 
int sockaddr_len);

    
int (*connect) (
struct socket *sock, 
struct sockaddr *uservaddr,

                    
int sockaddr_len, 
int flags);

    
int (*socketpair) (
struct socket *sock1, 
struct socket *sock2);

    
int (*accept) (
struct socket *sock, 
struct socket *newsock,

                   
int flags);

    
int (*getname) (
struct socket *sock, 
struct sockaddr *uaddr,

                    
int *usockaddr_len, 
int peer);

    
unsigned 
int (*poll) (
struct file *file, 
struct socket *sock, 
struct poll_table_struct

                          *wait);

    
int (*ioctl) (
struct socket *sock, 
unsigned 
int cmd,

                  
unsigned 
long arg);

    
int (*listen) (
struct socket *sock, 
int len);

    
int (*shutdown) (
struct socket *sock, 
int flags);

    
int (*setsockopt) (
struct socket *sock, 
int level, 
int optname,

                       
char *optval, 
int optlen);

    
int (*getsockopt) (
struct socket *sock, 
int level, 
int optname,

                       
char *optval, 
int *optlen);

    
int (*sendmsg) (
struct socket *sock, 
struct msghdr *m, 
int total_len, 
struct

                    scm_cookie *scm);

    
int (*recvmsg) (
struct socket *sock, 
struct msghdr *m, 
int total_len, 
int flags,

                    
struct scm_cookie *scm);

    
int (*mmap) (
struct file *file, 
struct socket *sock, 
struct vm_area_struct *vma);

    ssize_t (*sendpage) (
struct socket *sock, 
struct page *page, 
int offset, size_t size, 
int flags);

};


这个操作集类似于文件系统中的file_operations 结构。BSD 套接字层通过调用
proto_ops 结构中的相应函数执行任务。BSD 套接字层向 INET 套接字层传递 socket 数据
结构来代表一个 BSD 套接字,socket 结构在include/linux/net.h 中定义如下:


 C++ Code 
1

2

3

4

5

6

7

8

9

10

11

12

13

 
struct socket

{

    socket_state state;

    
unsigned 
long flags;

    
struct proto_ops *ops;

    
struct inode *inode;

    
struct fasync_struct *fasync_list; 
/* Asynchronous wake up list */

    
struct file *file; 
/* File back pointer for gc */

    
struct sock *sk;

    wait_queue_head_t wait;

    
short type;

    
unsigned 
char passcred;

};


但在 INET 套接字层中,它利用自己的 sock 数据结构来代表该套接字,因此,这两个
结构之间存在着链接关系,sock 结构定义于include/net/sock.h
。在 BSD 的 socket 数据结构中存在一个指向sock 的指针sk,而在sock 中又
有一个指向socket 的指针,这两个指针将 BSD socket 数据结构和sock 数据结构链接了起
来。通过这种链接关系,套接字调用就可以方便地检索到 sock 数据结构。实际上,sock 数
据结构可适用于不同的协议,它也定义有自己的协议操作集proto_ops。在建立套接字时,sock
数据结构的协议操作集指针指向所请求的协议操作集。如果请求 TCP,则 sock 数据结构的
协议操作集指针将指向 TCP 的协议操作集。

BSD 套接字上的详细操作与具体的底层地址族有关,底层地址族的不同实际意味着寻址
方式、采用的协议等的不同。Linux 利用 BSD 套接字层抽象了不同的套接字接口。在内核的
初始化阶段,内建于内核的不同地址族分别以 BSD 套接字接口在内核中注册。然后,随着应
用程序创建并使用 BSD 套接字。
内核负责在 BSD 套接字和底层的地址族之间建立联系。这种联系通过交叉链接数据结
构以及地址族专有的支持例程表建立。
在内核中, 地址族和协议信息保存在inet_protos 向量中, 其定义于
include/net/protocol.h:


 C++ Code 
1

2

3

4

5

6

7

8

9

10

11

12

 
struct inet_protocol *inet_protos[MAX_INET_PROTOS];


/* This is used to register protocols. */


struct inet_protocol

{

    
int (*handler)(
struct sk_buff *skb);
//The Linux kernel uses an sk_buff data structure to describe each packet.

    
void (*err_handler)(
struct sk_buff *skb, u32 info);

    
struct inet_protocol *next;

    
unsigned 
char protocol;

    
unsigned 
char copy: 
;

    
void *data;

    
const 
char *name;

};


每个地址族由其名称以及相应的初始化例程地址代表。在引导阶段初始化套接字接口
时,内核调用每个地址族的初始化例程,这时,每个地址族注册自己的协议操作集。协议操
作集实际是一个例程集合,其中每个例程执行一个特定的操作。


(四)、套接字的创建过程

Linux 在利用socket()系统调用建立新的套接字时,需要传递套接字的地址族标识符、
套接字类型以及协议,其函数定义于net/socket.c 中:


 C++ Code 
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

 
asmlinkage 
long sys_socket(
int family, 
int type, 
int protocol)

{

    
int retval;

    
struct socket *sock;

    retval = sock_create(family, type, protocol, &sock);

    
if (retval < 
)

        
goto out;

    retval = sock_map_fd(sock);

    
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;

}


实际上,套接字对于用户程序而言就是特殊的已打开的文件。内核中为套接字定义了一
种特殊的文件类型,形成一种特殊的文件系统sockfs,其定义于net/socket.c:

 C++ Code 
1

2

3

 
static 
struct vfsmount *sock_mnt;


static DECLARE_FSTYPE(sock_fs_type, 
"sockfs", sockfs_read_super, FS_NOMOUNT);


在系统初始化时,要通过kern_mount()安装这个文件系统。安装时有个作为连接件的
vfsmount 数据结构,这个结构的地址就保存在一个全局的指针sock_mnt 中。所谓创建一个
套接字,就是在
sockfs 
文件系统中创建一个特殊文件,或者说一个节点,并建立起为实现套
接字功能所需的一整套数据结构。所以,函数sock_create()首先是建立一个socket 数据结
构,然后将其“映射”到一个已打开的文件中,进行socket 结构和sock 结构的分配和初始
化。

新创建的 BSD socket 数据结构包含有指向地址族专有的套接字例程的指针,这一指针
实际就是 proto_ops 数据结构的地址。

BSD 套接字的套接字类型设置为所请求的 SOCK_STREAM 或 SOCK_DGRAM 等。然后,内
核利用 proto_ops 数据结构中的信息调用地址族专有的创建例程。
之后,内核从当前进程的 fd 向量中分配空闲的文件描述符,该描述符指向的 file 数
据结构被初始化。初始化过程包括将文件操作集指针指向由 BSD 套接字接口支持的 BSD 文
件操作集。所有随后的套接字(文件)操作都将定向到该套接字接口,而套接字接口则会进
一步调用地址族的操作例程,从而将操作传递到底层地址族,如图12.10 所示。

实际上,socket 结构与sock 结构是同一事物的两个方面。如果说socket 结构是面向进
程和系统调用界面的,那么sock 结构就是面向底层驱动程序的。可是,为什么不把这两个数
据结构合并成一个呢?
我们说套接字是一种特殊的文件系统,因此,inode 结构内部的union 的一个成分就用
作socket 结构,其定义如下:


 C++ Code 
1

2

3

4

5

6

7

8

 
struct inode {

        .......

        
union

    {

        ........

        
struct socket socket_i;

    }

}




由于套接字操作的特殊性,这个结构中需要大量的结构成分。可是,如果把这些结构成
分全都放在socket 结构中,则inode 结构中的这个union 就会变得很大,从而inode 结构也
会变得很大,而对于其他文件系统,这个union 成分并不需要那么庞大。因此,就把套接字
所需的这些结构成分拆成两部分,把与文件系统关系比较密切的那一部分放在socket 结构
中,把与通信关系比较密切的那一部分则单独组成一个数据结构,即sock 结构。由于这两部
分数据在逻辑上本来就是一体的,所以要通过指针互相指向对方,形成一对一的关系。


套接字socket 的地址族和类型、工作原理、创建过程的更多相关文章

  1. Java网络编程--套接字Socket

    一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...

  2. 套接字编程,创建套接字socket

    1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...

  3. 网络编程(二)--TCP协议、基于tcp协议的套接字socket

    一.TCP协议(Transmission Control Protocol 传输控制协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会 ...

  4. 网络编程(二)——TCP协议、基于tcp协议的套接字socket

    TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...

  5. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

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

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

  7. [置顶] Java套接字Socket编程

    1)概念 网络编程基本模型就客户端到服务器的模型,也就是我们常见的C/S模型.简单的说就是两个进程间相互通信的过程.即通信双方一方作为服务器等待客户端提出请求并给以回应,另一方作为客户端向服务器提出请 ...

  8. 什么是套接字(Socket)

    应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题.多个TCP连接或多个应用程序进程可能需要 通过同一个TCP协议端口传输数据.为了区别不同的应用程序进程和连 ...

  9. Java套接字Socket编程--TCP参数

    在Java的Socket中,主要包含了以下可设置的TCP参数. 属性 说明 默认值 SO_TIMEOUT 对ServerSocket来说表示等待连接的最长空等待时间; 对Socket来说表示读数据最长 ...

随机推荐

  1. eclipse 快捷键汇总

    1几个最重要的快捷键 代码助手:Ctrl+Space(简体中文操作系统是Alt+/)快速修正:Ctrl+1单词补全:Alt+/打开外部Java文档:Shift+F2 显示搜索对话框:Ctrl+H快速O ...

  2. python3.5之输出HTML实体字符

    出  关①   徐兰 凭山俯海古边州, 旆②影风翻见戍楼. 马后桃花马前雪,出关争得不回头? [注]关,指居庸关.②旆(pèi),旌旗. 刚刚学习用python写爬虫,实战一下. 抓取出一个网页的内容 ...

  3. 从源码看Android中sqlite是怎么通过cursorwindow读DB的

    更多内容在这里查看 https://ahangchen.gitbooks.io/windy-afternoon/content/ 执行query 执行SQLiteDatabase类中query系列函数 ...

  4. GMTED2010 高程数据下载

    http://topotools.cr.usgs.gov/GMTED_viewer/viewer.htm

  5. Mirror–使用证书配置镜像模板

    –==================================================================–该文档主要用于内部配置模板–场景:–主服务器:192.168.3 ...

  6. 低功耗之战!ANT VS Bluetooth LE

    利用近距离无线通信技术将手机及可穿戴式传感器终端等与智能电话连接起来,实现新的功能.最近,以此为目标的行动正在展开.其中备受关注的近距离无线方式是“ANT”和“Bluetooth LE”.为了在各种便 ...

  7. 11417 - GCD

    Problem A GCD Input: Standard Input Output: Standard Output Given the value of N, you will have to f ...

  8. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  9. 清风注解-Swift程序设计语言:Point11~15

    目录索引 清风注解-Swift程序设计语言 Point 11. 数值型字面量 代码事例: let decimalInteger = // 十进制的17 let binaryInteger = 0b10 ...

  10. codeigniter使用mongodb/redis

    ci2.x版本,使用mongodb,需要安装pecl-php-mongo扩展(github上很多扩展已不可用,找到个可用版本纪录于此),添加到php.ini中 使用如下 public function ...