Socket与系统调用深度分析
Posted meijlin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Socket与系统调用深度分析相关的知识,希望对你有一定的参考价值。
实验要求:
- Socket API编程接口之上可以编写基于不同网络协议的应用程序;
- Socket接口在用户态通过系统调用机制进入内核;
- 内核中将系统调用作为一个特殊的中断来处理,以socket相关系统调用为例进行分析;
- socket相关系统调用的内核处理函数内部通过“多态机制”对不同的网络协议进行的封装方法
请将Socket API编程接口、系统调用机制及内核中系统调用相关源代码、 socket相关系统调用的内核处理函数结合起来分析,并在X86 64环境下Linux5.0以上的内核中进一步跟踪验证。
完成一篇图文并茂、逻辑严谨、代码详实的实验报告
一、操作系统的运行机制
计算机系统的各种硬件资源是有限的,操作系统的发展过程大体上就是一个想方设法不断提高资源利用率的过程,为了更好的管理这些资源,进程是不允许直接操作的,所有对这些资源的访问都必须有操作系统控制。也就是说操作系统是使用这些资源的唯一入口,想要使用这些资源就要通过操作系统提供的系统调用(System Call)。
系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调用。CPU可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级别,用户态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态呢?
答案是中断。操作系统一般是通过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。如I/O中断、时钟中断等。在x86机器上可以通过int指令进行软件中断,而在磁盘完成读写操作后会向CPU发起硬件中断。
中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表(Interrupt Vector Table),这个数组存储了所有中断处理程序的地址,而中断号就是相应中断在中断向量表中的偏移量。
一般地,系统调用都是通过中断实现的,系统调用运行在系统的内核态。通过系统调用的方式来使用系统功能,可以保证操作系统的稳定性和安全性,防止用户随意更改或访问系统的数据和命令。接下来就来看一下Linux下系统调用具体的实现过程。
二、系统调用过程
用户通过操作系统运行上层程序(如用户自编程序),此时CPU在用户态下运行,当用户程序试图执行一条内核态才能运行的命令时,如系统调用,就会触发中断处理程序,通过中断机制进入内核态,由内核态程序接管CPU,执行系统调用,执行完毕后,退出中断处理程序,返回通胡程序断点处继续执行。可以画个图表示一下:
三、API与系统调用
上面我们知道了系统调用的过程,系统调用也像一个个函数,但是程序员在编程中是直接通过系统调用来实现功能吗?答案很明显是否,一般情况下,程序员编写程序通过编程语言中的应用编程接口(API)而不是直接通过系统调用来编程。这点很重要,因为应用程序使用的这种编程接口实际上并不需要和内核提供的系统调用一一对应。
以Socket API 为例,用户编程调用socket API,程序执行时API调用系统调用,进入内核态执行系统调用,功能完成后返回用户态,程序从断点处继续执行。
一个API定义了一组应用程序使用的编程接口。它们可以由一个系统调用实现,也可以通过调用多个系统调用来实现。实际上,API可以在各种不同的操作系统上实现,给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。
在Unix世界中,最流行的应用编程接口是基于POSIX标准的,其目标是提供一套大体上基于Unix的可移植操作系统标准。POSIX是说明API和系统调用之间关系的一个极好例子。在大多数Unix系统上,根据POSIX而定义的API函数和系统调用之间有着直接关系。
Linux的系统调用像大多数Unix系统一样,作为C库的一部分提供如下图所示。C库实现了 Unix系统的主要API,包括标准C库函数和系统调用。所有的C程序都可以使用C库,而由于C语言本身的特点,其他语言也可以很方便地把它们封装起来使用。
从程序员的角度看,系统调用无关紧要,他们只需要跟API打交道。相反,内核只跟系统调用打交道;库函数及应用程序是怎么使用系统调用不是内核所关心的。
四、socket相关系统调用跟踪分析
使用上次实验时构建的menuOS系统,进行系统调用分析,重新启动qemu虚拟机,注意本次实验需要重新编译x86_64内核,并且修改相关的menuOS以及lab3的Makefile文件,以启动64位linux系统,修改如下:
1 qemu-system-x86_64 -kernel ../../linux-5.0.1/arch/x86_64/boot/bzImage -initrd ../rootfs.img
执行hello/hi,结果如下:
执行正常,打开lab3中的main.c,查看功能是如何实现的;
main函数:
1 int main() 2 { 3 BringUpNetInterface(); 4 PrintMenuOS(); 5 SetPrompt("MenuOS>>"); 6 MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL); 7 MenuConfig("quit","Quit from MenuOS",Quit); 8 MenuConfig("replyhi", "Reply hi TCP Service", StartReplyhi); 9 MenuConfig("hello", "Hello TCP Client", Hello); 10 ExecuteMenu(); 11 }
可以看到main函数会打印出一些信息,运行系统,等待输入
Replyhi函数
1 int StartReplyhi(int argc, char *argv[]) 2 { 3 int pid; 4 /* fork another process */ 5 pid = fork(); 6 if (pid < 0) 7 { 8 /* error occurred */ 9 fprintf(stderr, "Fork Failed!"); 10 exit(-1); 11 } 12 else if (pid == 0) 13 { 14 /* child process */ 15 Replyhi(); 16 printf("Reply hi TCP Service Started! "); 17 } 18 else 19 { 20 /* parent process */ 21 printf("Please input hello... "); 22 } 23 }
当输入Replyhi之后,且条件满足(pid==0),就会继续调用Replyhi函数;
1 int Replyhi() 2 { 3 char szBuf[MAX_BUF_LEN] = "