本次实验的主要内容是从socket接口入手,通过跟踪相关函数在内核中的运行过程了解socket相关的系统调用是如何工作的。操作系统分为用户态和内核态,应用程序一般工作在用户态,而操作系统则通过系统调用为工作在其上的进程提供服务。系统调用是操作系统提供给用户程序访问内核的桥梁,通过系统调用,运行于用户态的用户程序能够调用到运行于内核态的系统内核提供的功能。系统调用一般是由软中断实现的,在Linux上该功能是由中断号为0x80的系统调用处理程序system_call提供。下面以Linux socket API为例,探究Linux中系统调用是如何进行的。
网络相关的主要的系统调用
1 系统调用号 函数名 系统调用 所在文件 2 41 socket sys_socket net/socket.c 3 42 connect sys_connect net/socket.c 4 43 accept sys_accept net/socket.c 5 44 sendto sys_sendto net/socket.c 6 45 recvfrom sys_recvfrom net/socket.c 7 46 sendmsg sys_sendmsg net/socket.c 8 47 recvmsg sys_recvmsg net/socket.c 9 48 shutdown sys_shutdown net/socket.c 10 49 bind sys_bind net/socket.c 11 50 listen sys_listen net/socket.c 12 51 getsockname sys_getsockname net/socket.c 13 52 getpeername sys_getpeername net/socket.c 14 53 socketpair sys_socketpair net/socket.c 15 54 setsockopt sys_setsockopt net/socket.c 16 55 getsockopt sys_getsockopt net/socket.c
lab3中的main.c文件
MenuConfig("replyhi", "Reply hi TCP Service", StartReplyhi); MenuConfig("hello", "Hello TCP Client", Hello);
syswrapper.h中代码
#include<arpa/inet.h> /* internet socket */
/**/
#define PrepareSocket(addr,port) int sockfd = -1; struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; socklen_t addr_len = sizeof(struct sockaddr); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(port); serveraddr.sin_addr.s_addr = inet_addr(addr); memset(&serveraddr.sin_zero, 0, 8); sockfd = socket(PF_INET,SOCK_STREAM,0); #define InitServer() int ret = bind( sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)); if(ret == -1) { fprintf(stderr,"Bind Error,%s:%d\n", __FILE__,__LINE__); close(sockfd); return -1; } listen(sockfd,MAX_CONNECT_QUEUE);
/**/
#define InitializeService() \
PrepareSocket(IP_ADDR,PORT); \
InitServer();
socket(),listen(),bind()以及close()等函数
#启动qemu 加载内核,不加-S,因为跟踪的系统调用,不是启动过程
qemu -kernel ../linux-5.4.2/arch/x86/boot/bzImage -initrd ../rootfs.img -append nokaslr -s
#打开一个新的终端
gdb
file ~/linux-5.4.2/vmlinux
b sys_socketcall
target remote:1234
c
#在qume中输入
replyhi
在sys_socketcall打了断点后,运行我们的replyhi程序,即在断点处停止了,我们查看其函数调用栈,发现其进入系统调用的顺序是 entry_SYSENTER_32() ---> do_syscall_32_irqs_on()---->sys_socketcall()。内核中为 socket 设置的总入口为 sys_socketcall(),其代码在 net/socket.c 中,而该函数实际上则调用的是SYSCALL_DEFINE2:
1 /*与socket相关的系统调用总入口。 */ 2 /* 3 *函数的第一个参数 call 即为具体的操作码,而参数 args 为指向一个数组的指针,可以根据具体操作码的不同,确定从用户空间复制参数的数量; 4 */ 5 asmlinkage long sys_socketcall(int call, unsigned long __user *args) 6 { 7 unsigned long a[6]; 8 unsigned long a0,a1; 9 int err; 10 11 if(call<1||call>SYS_RECVMSG) 12 return -EINVAL; 13 14 /* copy_from_user should be SMP safe. */ 15 if (copy_from_user(a, args, nargs[call])) 16 return -EFAULT; 17 18 a0=a[0]; 19 a1=a[1]; 20 21 switch(call) 22 { 23 case SYS_SOCKET: 24 err = sys_socket(a0,a1,a[2]); 25 break; 26 case SYS_BIND: 27 err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]); 28 break; 29 case SYS_CONNECT: 30 err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]); 31 break; 32 case SYS_LISTEN: 33 err = sys_listen(a0,a1); 34 break; 35 case SYS_ACCEPT: 36 err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]); 37 break; 38 case SYS_GETSOCKNAME: 39 err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]); 40 break; 41 case SYS_GETPEERNAME: 42 err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]); 43 break; 44 case SYS_SOCKETPAIR: 45 err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]); 46 break; 47 case SYS_SEND: 48 err = sys_send(a0, (void __user *)a1, a[2], a[3]); 49 break; 50 case SYS_SENDTO: 51 err = sys_sendto(a0,(void __user *)a1, a[2], a[3], 52 (struct sockaddr __user *)a[4], a[5]); 53 break; 54 case SYS_RECV: 55 err = sys_recv(a0, (void __user *)a1, a[2], a[3]); 56 break; 57 case SYS_RECVFROM: 58 err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3], 59 (struct sockaddr __user *)a[4], (int __user *)a[5]); 60 break; 61 case SYS_SHUTDOWN: 62 err = sys_shutdown(a0,a1); 63 break; 64 case SYS_SETSOCKOPT: 65 err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]); 66 break; 67 case SYS_GETSOCKOPT: 68 err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]); 69 break; 70 case SYS_SENDMSG: 71 err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]); 72 break; 73 case SYS_RECVMSG: 74 err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]); 75 break; 76 default: 77 err = -EINVAL; 78 break; 79 } 80 return err;
涉及到socket的系统调用都使用统一的入口sys_socketcall,再通过SYSCALL_DEFINE2进入不同的分支,调用不同的系统调用,如在本次实验中,就包括sys_socket,sys_bind,sys_listen,sys_connect,sys_accept4等等。
原文:https://www.cnblogs.com/yongjason/p/12069682.html
【推荐】腾讯云热门云产品限时秒杀,爆款1核2G云服务器99元/年!
【推荐】阿里云双11返场来袭,热门产品低至一折等你来抢!
【活动】京东云服务器_云主机低于1折,低价高性能产品备战双11
【活动】ECUG For Future 技术者的年度盛会(杭州,1月4-5日)
· Socket与系统调用深度分析
· Socket与系统调用深度分析
· Socket与系统调用深度分析
· Socket与系统调用深度分析
· Socket与系统调用深度分析
» 更多推荐...
· Facebook和Instagram将限制电子烟等内容传播
· 蚂蚁金服换将!新任CEO:区块链是蚂蚁金服金融科技应用底盘之一
· 黄仁勋独家回应下一代7nm订单由台积电转单三星传言的真相
· 岩浆中的巨大能量——流体金属氢
· 龙芯之后 统一操作系统UOS与国产云桌面完成适配
» 更多新闻...