linux socket bind 内核详解,Socket与系统调用深度分析(示例代码)

1、 什么是系统调用

操作系统通过系统调用为运行于其上的进程提供服务。当用户态进程发起一个系统调用, CPU 将切换到 内核态 并开始执行一个 内核函数 。 内核函数负责响应应用程序的要求,例如操作文件、进行网络通讯或者申请内存资源等。在Linux中系统调用是有Linux内核提供的各种功能服务,为了便于调用Linux提供了一个底层C语言库libc(glibc是GUN版本的libc,其他类似库还有uclibc、klibc),目前glibc是linux标准函数库,这些都对系统系统接口打包成了标准C函数,这些函数一般就成为系统调用。系统调用可以通过syscall()函数发起,或者调用每个对应的一个C函数,这些函数定义在 或者 头文件中。Linux系统中通过软中断0x80调用实现控制权转移给内核,内容执行完成后返回结果。所有系统调用在linux内核的源文件目录" arch/x86/kernel"中的各种文件中定义。

内核实现了很多的系统调用函数, 这些函数会有自己的名字, 以及编号. 用户要调用系统调用, 首先需要使用  int 0x80 触发软中断. 这个指令会在0x80代表十进制的128, 所以这个指令会找终端向量表的128项, 找到以后, 跳转到相应的函数, 这个处理函数就是system_call. 这个中断向量表的设置, 是在操作系统初始化的时候, 通过trap_init()函数设置的. 在进入中断处理函数system_call以后, 首先要进行一般的中断处理流程, 即保护现场. 这个体现在指令SAVE_ALL(494行)上. 然后有一个重要的函数调用 call *sys_call_table(,%eax,4). 这个表示查找系统调用函数表(), 然后调用相应的系统调用函数. 对于32位的系统, 函数位置存了4个Bytes, eax中是我们传入的系统调用号, 所以4*eax,就可以找到对应的系统调用函数, 执行函数. 之后还需要进行返回值的保存等工作.

2、 socket相关系统调用的内核处理函数深入分析

1)        因为上一次实验是在shiyanlou环境下完成,所以此次实验需要重新下载下载linux-5.0.1的内核并编译内核,并制作根文件系统。

20191219155311140619.png

20191219155311540008.png

2)

解Linux内核中socket接口层的代码,找出112号系统调用socketcall的内核处理函数sys_socketcall,理解socket接口函数编号和对应的socket接口内核处理函数 通过前面构建MenuOS实验环境使得我们有方法跟踪socket接口通过系统调用进入内核代码,在我们的环境中socket接口通过112号系统调用socketcall进入内核的

System call vectors.

Argument checking cleaned up. Saved 20% in size.

This function doesn‘t need to set the kernel lock because

it is set by the callees.

2490 */

2491

2492SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)

2493{

...

2517 switch (call) {

2518 case SYS_SOCKET:

2519       err = sys_socket(a0, a1, a[2]);

2520       break;

2521 case SYS_BIND:

2522       err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);

2523       break;

2524 case SYS_CONNECT:

2525       err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);

2526       break;

2527 case SYS_LISTEN:

2528       err = sys_listen(a0, a1);

2529       break;

2530 case SYS_ACCEPT:

2531       err = sys_accept4(a0, (struct sockaddr __user *)a1,

2532                       (int __user *)a[2], 0);

2533       break;

2534 case SYS_GETSOCKNAME:

2535       err =

2536           sys_getsockname(a0, (struct sockaddr __user *)a1,

2537                         (int __user *)a[2]);

2538       break;

2539 case SYS_GETPEERNAME:

2540       err =

2541           sys_getpeername(a0, (struct sockaddr __user *)a1,

2542                         (int __user *)a[2]);

2543       break;

2544 case SYS_SOCKETPAIR:

2545       err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);

2546       break;

2547 case SYS_SEND:

2548       err = sys_send(a0, (void __user *)a1, a[2], a[3]);

2549       break;

2550 case SYS_SENDTO:

2551       err = sys_sendto(a0, (void __user *)a1, a[2], a[3],

2552                     (struct sockaddr __user *)a[4], a[5]);

2553       break;

2554 case SYS_RECV:

2555       err = sys_recv(a0, (void __user *)a1, a[2], a[3]);

2556       break;

2557 case SYS_RECVFROM:

2558       err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],

2559                        (struct sockaddr __user *)a[4],

2560                        (int __user *)a[5]);

2561       break;

2562 case SYS_SHUTDOWN:

2563       err = sys_shutdown(a0, a1);

2564       break;

2565 case SYS_SETSOCKOPT:

2566       err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);

2567       break;

2568 case SYS_GETSOCKOPT:

2569       err =

2570           sys_getsockopt(a0, a1, a[2], (char __user *)a[3],

2571                        (int __user *)a[4]);

2572       break;

2573 case SYS_SENDMSG:

2574       err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);

2575       break;

2576 case SYS_SENDMMSG:

2577       err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);

2578       break;

2579 case SYS_RECVMSG:

2580       err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);

2581       break;

2582 case SYS_RECVMMSG:

2583       err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],

2584                        (struct timespec __user *)a[4]);

2585       break;

2586 case SYS_ACCEPT4:

2587       err = sys_accept4(a0, (struct sockaddr __user *)a1,

2588                       (int __user *)a[2], a[3]);

2589       break;

2590 default:

2591       err = -EINVAL;

2592       break;

2593 }

2594 return err;

2595}

2596

在我们的实验环境中,socket接口的调用是通过给socket接口函数编号的方式通过112号系统调用来处理的。这些socket接口函数编号的宏定义见/linux-3.18.6/include/uapi/linux/net.h#26

26#define SYS_SOCKET 1            /* sys_socket(2)           */

27#define SYS_BIND     2            /* sys_bind(2)              */

28#define SYS_CONNECT    3            /* sys_connect(2)         */

29#define SYS_LISTEN  4            /* sys_listen(2)             */

30#define SYS_ACCEPT 5            /* sys_accept(2)           */

31#define SYS_GETSOCKNAME  6            /* sys_getsockname(2)        */

32#define SYS_GETPEERNAME   7            /* sys_getpeername(2)        */

33#define SYS_SOCKETPAIR 8            /* sys_socketpair(2)             */

34#define SYS_SEND    9            /* sys_send(2)              */

35#define SYS_RECV    10          /* sys_recv(2)               */

36#define SYS_SENDTO      11          /* sys_sendto(2)           */

37#define SYS_RECVFROM  12          /* sys_recvfrom(2)        */

38#define SYS_SHUTDOWN 13          /* sys_shutdown(2)             */

39#define SYS_SETSOCKOPT      14          /* sys_setsockopt(2)            */

40#define SYS_GETSOCKOPT     15          /* sys_getsockopt(2)           */

41#define SYS_SENDMSG    16          /* sys_sendmsg(2)        */

42#define SYS_RECVMSG    17          /* sys_recvmsg(2)         */

43#define SYS_ACCEPT4     18          /* sys_accept4(2)         */

44#define SYS_RECVMMSG 19          /* sys_recvmmsg(2)             */

45#define SYS_SENDMMSG 20          /* sys_sendmmsg(2)            */

接下来我们根据TCP server程序调用socket接口的顺序依次看一下socket、bind、listen、accept等socket接口的内核处理函数。

socket接口函数的内核处理函数sys_socket

1377SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

1378{

1379 int retval;

1380 struct socket *sock;

...

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

...

socket接口函数主要作用是建立socket套接字描述符,Unix-like系统非常成功的设计是将一切都抽象为文件,socket套接字也是一种特殊的文件,sock_create内部就是使用文件系统中的数据结构inode为socket套接字分配了文件描述符。socket套接字与普通的文件在内部存储结构上是一致的,甚至文件描述符和套接字描述符是通用的,但是套接字和文件还是特殊之处,因此定义了结构体struct socket,struct socket的结构体定义见/linux-3.18.6/include/linux/net.h#105,具体代码摘录如下:

95/**

96 *  struct socket - general BSD socket

97 *  @state: socket state (%SS_CONNECTED, etc)

98 *  @type: socket type (%SOCK_STREAM, etc)

99 *  @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)

100 *  @ops: protocol specific socket operations

101 *  @file: File back pointer for gc

102 *  @sk: internal networking protocol agnostic socket representation

103 *  @wq: wait queue for several uses

104 */

105struct socket {

106  socket_state         state;

107

108  kmemcheck_bitfield_begin(type);

109  short                    type;

110  kmemcheck_bitfield_end(type);

111

112  unsigned long             flags;

113

114  struct socket_wq __rcu *wq;

115

116  struct file       *file;

117  struct sock            *sk;

118  const struct proto_ops *ops;

119};

sock_create内部还根据指定的网络协议族family和protocol初始化了相关协议的处理接口到结构体struct socket中,结构体struct socket在后续的分析和理解中还会用到,这里简单略过用到时再具体研究。

bind接口函数的内核处理函数sys_bind

1519/*

1520 *     Bind a name to a socket. Nothing much to do here since it‘s

1521 *     the protocol‘s responsibility to handle the local address.

1522 *

1523 *     We move the socket address to kernel space before we call

1524 *     the protocol layer (having also checked the address is ok).

1525 */

1526

1527SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)

1528{

1529 struct socket *sock;

1530 struct sockaddr_storage address;

1531 int err, fput_needed;

1532

1533 sock = sockfd_lookup_light(fd, &err, &fput_needed);

1534 if (sock) {

1535       err = move_addr_to_kernel(umyaddr, addrlen, &address);

1536       if (err >= 0) {

1537              err = security_socket_bind(sock,

1538                                      (struct sockaddr *)&address,

1539                                      addrlen);

1540              if (!err)

1541                     err = sock->ops->bind(sock,

1542                                         (struct sockaddr *)

1543                                         &address, addrlen);

1544       }

1545       fput_light(sock->file, fput_needed);

1546 }

1547 return err;

1548}

如上代码可以看到,move_addr_to_kernel将用户态的struct sockaddr结构体数据拷贝到内核里的结构体变量struct sockaddr_storage address,然后使用sock->ops->bind将该网络地址绑定到之前创建的套接字。这里用到了通过套接字描述符fd找到之前分配的套接字struct socket *sock,利用该套接字中的成员const struct proto_ops *ops找到对应网络协议的bind函数指针即sock->ops->bind。这里即是一个socket接口层通往具体协议处理的接口。

listen接口函数的内核处理函数sys_listen

1550/*

1551 *     Perform a listen. Basically, we allow the protocol to do anything

1552 *     necessary for a listen, and if that works, we mark the socket as

1553 *     ready for listening.

1554 */

1555

1556SYSCALL_DEFINE2(listen, int, fd, int, backlog)

1557{

1558 struct socket *sock;

1559 int err, fput_needed;

1560 int somaxconn;

1561

1562 sock = sockfd_lookup_light(fd, &err, &fput_needed);

1563 if (sock) {

1564       somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;

1565       if ((unsigned int)backlog > somaxconn)

1566              backlog = somaxconn;

1567

1568       err = security_socket_listen(sock, backlog);

1569       if (!err)

1570              err = sock->ops->listen(sock, backlog);

1571

1572       fput_light(sock->file, fput_needed);

1573 }

1574 return err;

1575}

listen接口的主要作用是通知网络底层开始监听套接字并接收网络连接请求,listen接口正常处理完TCP服务就已经启动了,只是这时网络连接请求都会暂存在缓冲区,等调用accept建立连接,listen接口函数的参数backlog就是用来配置支持的连接数。

我们发现实际处理的工作是由sock->ops->listen完成的,这也是一个socket接口层通往具体协议处理的接口。

accept接口函数的内核处理函数sys_accept

内核处理函数sys_accept的主要功能是调用sys_accept4完成的,sys_accept4见/linux-3.18.6/net/socket.c#1589,具体代码摘录如下:

1577/*

1578 *     For accept, we attempt to create a new socket, set up the link

1579 *     with the client, wake up the client, then return the new

1580 *     connected fd. We collect the address of the connector in kernel

1581 *     space and move it to user at the very end. This is unclean because

1582 *     we open the socket then return an error.

...

1589SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,

1590       int __user *, upeer_addrlen, int, flags)

1591{

...

1608 newsock = sock_alloc();

...

1612 newsock->type = sock->type;

1613 newsock->ops = sock->ops;

...

1621 newfd = get_unused_fd_flags(flags);

...

1627 newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);

...

1639 err = sock->ops->accept(sock, newsock, sock->file->f_flags);

...

1643 if (upeer_sockaddr) {

1644       if (newsock->ops->getname(newsock, (struct sockaddr *)&address,

...

1649       err = move_addr_to_user(&address,

...

1657 fd_install(newfd, newfile);

1658 err = newfd;

...

1668}

在TCP的服务器端通过socket函数创建的套接字描述符只是用来监听客户连接请求,accept函数内部会为每一个请求连接的客户创建一个新的套接字描述符专门负责与该客户端进行网络通信,并将该客户的网络地址和端口等地址信息返回到用户态。这里涉及更多的网络协议处理的接口如sock->ops->accept、ewsock->ops->getname。

send和recv接口的内核处理函数类似也是通过调用网络协议处理的接口来将具体的工作交给协议层来完成,比如sys_recv最终调用了sock->ops->recvmsg,sys_send最终调用了sock->ops->sendmsg,但send和recv接口涉及网络数据流,是理解网络部分的关键内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/441020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UVA - 11729】Commando War (贪心,时间调度问题)

题干:(Uva不放题干了) 题目大意:(实在是自己懒得写网上找了一个) 解题报告: 调度问题,直接贪心出完成任务需要的时间最长的那个人排序,就行了。 方法正确性的证明以前也…

【UVA - 1335】Beijing Guards (贪心,二分)

题干: 题目大意: 有n个人为成一个圈,其中第i个人想要r[i]种不同的礼物,相邻的两个人可以聊天,炫耀自己的礼物。如果两个相邻的人拥有同一种礼物,则双方都会很不高兴,问最少需要多少种不同的礼…

linux两个命令一起,paste命令 – 合并两个文件

paste命令会把每个文件以列对列的方式,一列列地加以合并 ,他就是相当于把两个不同的文件内容粘贴在一起,形成新的文件。注意:paste默认粘贴方式以列的方式粘贴,但是并不是不能以行的方式粘贴,加上-s选项就可…

【UVA - 11292】Dragon of Loowater (贪心,水题,模拟,twopointer双指针)

题干: 题目大意: n条恶龙,m个勇士,用勇士来杀恶龙。一个勇士只能杀一个恶龙。而且勇士只能杀直径不超过自己能力值的恶龙。每个勇士需要支付能力值一样的金币。问杀掉所有恶龙需要花费的最少金币。 解题报告: twopo…

linux 命令pg,linux下操作PostgreSQL的常用命令

一般性:\copyright 显示PostgreSQL的使用和发行许可条款\g [文件] or; 执行查询 (并把结果写入文件或 |管道)\h [名称] SQL命令语法上的说明,用*显示全部命令的语法说明\q 退出 psql查询缓存区\e [FILE] [LINE] 使用外部编辑器编辑查询缓存区(或文件)\ef…

【UVA - 10037】Bridge(过河问题,经典贪心)

题干: 题目大意: 有N个人要过桥,每个人速度不同,只有一个手电筒,每次最多只能过去两个人,问所有人最短的过桥时间为多少 解题报告: 首先让最快的两个人最后过,然后我们分奇偶考虑…

linux系统键盘记录器,可截获到 QQ 密码 键盘记录器源码

由于 QQ 密码做了特殊的保护,所以通过远程注入得到密码框内容以及通过钩子来得到键盘消息均不能探测到 QQ 的密码,但是通过对键盘驱动的过滤却是可以记录下 QQ 密码输入期间的内容,附上源码。#define DBG 1#include #include #include "…

【牛客 - 2B】树(思维,dp,有坑)

题干: shy有一颗树,树有n个结点。有k种不同颜色的染料给树染色。一个染色方案是合法的,当且仅当对于所有相同颜色的点对(x,y),x到y的路径上的所有点的颜色都要与x和y相同。请统计方案数。 输入描述: 第一行两个整数n&#xf…

linux安装后启动mysql,linux安装完mysql后启动错误

redhat as 4,使用rpm包安装完mysql的server 跟client后执行以下命令出错。[rootftp etc]# /usr/bin/mysqladmin -u root password new-password/usr/bin/mysqladmin: connect to server at localhost failederror: Cant connect to local MySQL server through socket /var/lib…

linux exchange邮件客户端,Linux中使用Hiri邮件客户端访问Exchange帐户

大家都知道 Microsoft Exchange 是企业级邮件市场占有率第一的产品,早已成为 Top 500 企业首选的邮件服务器和客户端标准配备。随着 Office 365 服务在全球的铺开,Exchange 的市场占有率更是越来越高,不少中小企业甚至个人用户都已经开始使用…

【CodeForces - 1084C】The Fair Nut and String(思维,组合数学)

题干: The Fair Nut found a string ss. The string consists of lowercase Latin letters. The Nut is a curious guy, so he wants to find the number of strictly increasing sequences p1,p2,…,pkp1,p2,…,pk, such that: For each ii (1≤i≤k1≤i≤k), sp…

群辉挂载linux nfs,Debian 9 挂载访问已设置的群晖NFS共享文件目录

说明博主先后尝试了 webdav、Samba方案后,最后毅然决然选择NFS的方案,通过挂载群晖的NFS共享文件目录作为Jellyfin的媒体库,媒体播放通常单个文件都比较大,而webdav在读取过程中需要比较长的缓存时间大文件传输并不友好。之前试过…

【牛客 - 289H】约会(思维,数学,奇偶数,水题)

题干: Hang等了一年,终于,朝气蓬勃的小学妹来了,在开学当天,就成功打入协会迎新群,在Hang的魅力(死皮赖脸)之下,有个小学妹要和他面基了,小学妹说&#xf…

c语言中调试时go的作用,C语言调用GO

C语言调用GO最近工作中遇到需要在c语言里面调用go语言的需求,总结了一下,下面代码里面的每一个注释都很有用,闲话不多说,直接上代码~示例GO代码:package main // 这个文件一定要在main包下面import "C" // 这…

【CodeForces - 768C】Jon Snow and his Favourite Number(思维,技巧,套路,数学异或,循环节,trick)

题干: Jon Snow now has to fight with White Walkers. He has n rangers, each of which has his own strength. Also Jon Snow has his favourite number x. Each ranger can fight with a white walker only if the strength of the white walker equals his st…

C语言第六次作业指针,c语言第六次作业解析

《c语言第六次作业解析》由会员分享,可在线阅读,更多相关《c语言第六次作业解析(36页珍藏版)》请在人人文库网上搜索。1、c 语言第六次作业解析第六次作业:指针(以下题目如无特殊声明, 请使用指针技术实现 , 尽量不要使用数组作为…

【HDU - 1069】Monkey and Banana (最长下降子序列 + 贪心,最长上升子序列类问题)

题干: A group of researchers are designing an experiment to test the IQ of a monkey. They will hang a banana at the roof of a building, and at the mean time, provide the monkey with some blocks. If the monkey is clever enough, it shall be able …

c 语言定义2维字符串数组赋值,二维数组赋值字符串 c 语言 二维字符串数组赋值问题...

C语言中二维字符数组应该怎样赋值?c语言二维数组如何定义字符串????急。。。二维字符数组的定义格式为:char 数组名[第一维大小][第二维大小]; 例如:char c[3][10]; //定义了一个3行10列的二维字…

【牛客 - 297D】little w and Exchange(上下界贪心)

题干: 旅行到K国的小w发现K国有着很多物美价廉的商品,他想要买一些商品。 结果一掏钱包,包里只剩下n张K国的纸币了,说起来也奇怪,K国纸币并不像其他国家一样都是1元,5元,10元…而是各种奇怪的…

c语言程序设计 doc,《C语言程序设计》.doc

《C语言程序设计》.doc《C语言程序设计》实验 编实验一 C程序的运行环境和运行C程序的方法2实验二 数据类型、运算符和表达式9实验三 最简单的C程序设计14实验四 逻辑结构程序设计20实验五 循环结构程序设计26实验六 数组31实验七 函数39实验八 编译预处理命令45实验九 指针50…