请将Socket API编程接口、系统调用机制及内核中系统调用相关源代码、 socket相关系统调用的内核处理函数结合起来分析,并在X86 64环境下Linux5.0以上的内核中进一步跟踪验证。
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 int socket(int domain,int type, int protocol);
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,const sockaddr* my_addr,socklen_t addrlen);
客户端通常不需要命名socket采用的是匿名方式,操作系统自动分配的socket地址(客户端使用的就是这个分配的一个随机的非知名端口号)。
socket被命名之后还不能马上接受客户的连接,需要创建一个监听队列存放待处理的客户连接。
服务器通过listen调用被动的接连接。
#include <sys/socket.h>
int listen(int sockfd, int backlog);
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr, socklen *addrlen);
accept调用对于客户端的网络断开毫不知情。只是从监听队列当中取出连接,不论连接处于何种状态。
客户端通过connect调用主动的与服务器建立连接。
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
#include <unistd.h>
int close(int fd);
fd是待关闭的socket的描述符,将fd的引用计数减一只有当引用计数为0的时候才真正的关闭连接。一次fock将是的父进程当中打开的socket引用计数加一。因此必须都关闭才会真正的将连接关闭。
如上图,系统调用执行的流程如下:
基于上次实验构建的Menu OS系统来进行本次的socket系统调用内核处理函数的跟踪分析
即通过在Menu OS系统上运行TCP客户端/服务器程序,然后用gdb设置断点来跟踪分析socket内核处理函数。
使用跟踪分析 ~/Linux 内核的启动过程的 -s 和 -S 选项启动 MenuOS 系统。
$ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
# 打开 GDB 调试器
$ gdb
# 在 GDB 中输入以下命令:
# 在 gdb 界面中 targe remote 之前加载符号表
(gdb)file linux-3.18.6/vmlinux
# 建立 gdb 和 gdbserver 之间的连接
(gdb)target remote:1234
# 断点的设置可以在target remote之前,也可以在之后
(gdb)break #start_kernel#
# 按 c 让qemu上的Linux继续运行
(gdb)c
只需在断点处输入list即可查看相应的内核处理函数,,比如__sys_listen和__sys_bind源码如下:
int __sys_listen(int fd, int backlog)
{
struct socket *sock;
int err, fput_needed;
int somaxconn;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
if ((unsigned int)backlog > somaxconn)
backlog = somaxconn;
err = security_socket_listen(sock, backlog);
if (!err)
err = sock->ops->listen(sock, backlog);
fput_light(sock->file, fput_needed);
}
return err;
}
int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
{
struct socket *sock;
struct sockaddr_storage address;
int err, fput_needed;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
err = move_addr_to_kernel(umyaddr, addrlen, &address);
if (!err) {
err = security_socket_bind(sock,
(struct sockaddr *)&address,
addrlen);
if (!err)
err = sock->ops->bind(sock,
(struct sockaddr *)
&address, addrlen);
}
fput_light(sock->file, fput_needed);
}
return err;
}
原文:https://www.cnblogs.com/smjsoftware/p/12070191.html