论文部分内容阅读
摘要:作为计算机相关专业的一门专业课,“TCP/IP网络设计及实现”课程教学采用Unix OS的联网软件开放源码作为素材,着重讲解TCP/IP协议软件的设计思想和实现技术。笔者针对课程教学中遇到的问题,提出了“实验主导教学,问题驱动学习”的教学模式。即教师在实验开展中导出问题,并指导学生结合源码来解决问题。整个教学过程以问题为原动力来组织教学内容,将知识点的讲解穿插在问题的解答中,既保证知识结构的完整和系统性,又在一定程度上增强教学活动的生动性,取得了较好的教学效果。
关键词:源码;协议;实验;问题驱动;自顶向下
作者简介:王雪梅(1978-),女,江苏盐城人,南京邮电大学计算机学院,讲师;林晓勇(1974-),男,江苏南京人,南京邮电大学通信与信息工程学院,讲师。(江苏 南京 210003)
基金项目:本文系江苏省教育厅教改重大项目(项目编号:JG66609JX26)、南京邮电大学教改项目(项目编号:JG00410JX14、JG00211JX21)的研究成果。
中图分类号:G642.3 文献标识码:A 文章编号:1007-0079(2011)28-0109-02
“TCP/IP网络设计及实现”课程是我校计算机学院面向计算机科学与技术专业、信息安全专业学生开设的一门专业必修课。该课程以Unix操作系统下TCP/IP软件源码为素材来讲解联网协议栈软件的设计思想和实现技术。通过呈现协议实现细节,深化学生对网络基础理论的理解,使其对协议的认识上升到实现的高度,并通过源码解析提高学生网络编程的能力,有助于在网络应用开发中迅速诊断、定位错误,并有效解决问题。以源码为载体是该课程的一大特色,但庞大的源码也使教学面临很大的挑战。笔者在课堂教学实施过程中尝试“实验主导教学,问题驱动学习”的教学模式,结合实验和问题来应用情景分析的教学方法,从一个网络应用实例出发,自顶向下深入探析内核中的协议处理过程,逐步揭示内核中联网软件的细节。该方法取得较好的教学效果。
一、课程教学面临的问题
在源码阅读的过程中,深入了解协议实现的细节,是课程的基本教学目标。教学过程中涉及两个突出的问题:其一,如何对上万行的源码进行合理组织与取舍,既保证教学过程的系统性,又能激发学生自主学习的意识,提高学习的热情。其二,如何将源码细节的学习与实际网络应用开发过程进行有效的结合,在课程进展的过程中,逐步提高学生诊断错误和解决问题的能力,为培养网络类研发人才奠定基础。
二、“实验主导教学,问题驱动学习”教学模式
既然教学立足于庞大的开放源码,为保证教学的有序开展,寻找一个有效的切入点,是解决上述问题的关键所在。作者在教学过程中适时引入实践环节,通过部署实验项目来辅助课堂教学,使学生在解决问题的过程中自主去查看源码,从实践中获真知。下文通过几个教学案例来阐释以实验为主导和以问题为动力的教学方式。
1.协议地址的手动配置
一般情况下,主机接入校园网时,IP地址的配置是自动完成的。开机后,网络上部署的DHCP服务器会自动为个人PC分配唯一的IP地址。但这个过程涉及的多次应用报文的交互,以及更新本地协议地址的细节对用户而言都是透明的。对此,我们设计相应的实验项目,要求学生编写程序,实现静态IP地址的手动配置,通过在程序中调用ioctl函数来熟悉ioctl相关命令的命名和处理过程。教学的开展过程如下:
(1)以管理员或超级用户身份登录,利用系统提供的网络连接属性配置窗口,选择“Internet(TCP/IP)属性”,填入随机的合法IP地址,和子网掩码、默认网关地址,以及DNS服务器地址。选择不同的IP地址多次执行,观察结果。事实证明,手动配置存在不成功的情况。接着,引导学生分析原因,从而得出第一个结论,手动配置可能会遭遇地址冲突问题。然后,教师及时提出问题:系统是如何检测出地址重复?在问题的驱动下,为学生预留思考和讨论的时间。最后教师给出准确答案,并定位到相应的源码进行释疑和解惑。
(2)以普通用户身份登录,执行与(1)相同的操作。系统提示权限不足,配置不成功。引导学生分析这一现象,再定位到代码中进行深入讲解。
(3)从手动配置过程引入ioctl机制的讲解。展示并解释ioctl函数原型后,介绍相关地址访问和配置命令,如SIOCGIFADDR,SIOCSIFADDR等。
(4)以地址配置为例深入详解内核中相关ioctl命令的实现细节,包括函数调用的流程,以及命令的具体处理过程。
(5)布置课外实验项目,编写简单的静态地址配置程序。允许用户在参数中指定新IP地址等信息。程序执行结果包括:配置前后所有接口的地址信息。
这个教学案例以实验操作为切入点,逐步引导学生发现问题、解决问题,从而引出并深入讲解ioctl机制,最终通过学生实践加深并巩固对ioctl的使用和理解。这种从表象深入本质,将教师课堂演示与学生课后实验有效结合的方法,取得较好的教学效果。
2.数据发送过程的自顶向下分析
我们直接使用教材[1]第一章提供的简单客户端程序,主要执行日期时间查询功能。在向网络上的日期时间服务器发送一个查询请求后,接收服务器的响应,并将结果显示在屏幕上。程序中涉及三个API函数的调用(socket,sendto,recvfrom),分别用来创建网络插口、通过插口发送和接收数据。下面作者从这个简单客户端程序出发,应用自顶向下的方法,在连续六个问题的驱动下,和学生一起探析数据的发送处理过程。
(1)这些API函数往往定义在函数库中,其目标码链接到可执行程序中。学生只要熟悉每个网络API函数的引用格式、调用方法就能够编写简单的网络应用程序。它们并不执行真正的网络操作,主要作用是验证入口参数的有效性,并通过系统调用进入内核,执行相应内核插口函数。这里,提出第一个问题:系统调用和过程调用有什么区别?这是两个很混淆的概念。为了加深学生的理解,在给出答案前,先促进学生自己思考并进行讨论。为回答该问题,着重从地址空间和调用方式两个方面进行比较。
(2)提出第二个问题:在程序中可以直接使用系统调用来访问内核中的插口函数吗?这个问题从程序移植角度来考虑。移植性的理解会使学生对程序设计的认识上升到一个新的高度。事实上,用户在编制程序时,如果熟悉所用操作系统的系统调用方式,完全可以直接进行系统调用来访问内核插口函数,但由于不同的操作系统实现系统调用的方法存在很大差异,如果在程序中直接进行系统调用,不利于程序的移植,影响程序的通用性。用户函数库以统一函数接口的调用方式屏蔽了操作系统的系统调用的差异性,提高程序的移植性。
(3)提出第三个问题:如何完成API函数与内核插口函数之间的关联与映射?在定位到相关源码后,通过和学生一起来阅读、分析代码,来逐步阐述映射机制。在BSD Net/3的386实现中,内核函数syscall和数据结构sysent扮演了重要的角色。当API函数中使用系统调用时,CPU转去执行内核函数syscall,由syscall执行映射的任务。sysent是系统维持的映射表sysent,每个表项存放一个插口函数的调用信息,其结构定义如下:
structsysent {
intsy_narg;
int (*sy_call)();
}
其中,sy_call是指向对应插口函数的指针,sy_narg表示传入sy_call所指函数的参数数目。插口函数具有唯一的编号,对应表中索引。映射表的初始化如下:
structsysentsysent [ ] = {…
{ 6,recvfrom }, /* 29 = recvfrom */…
{ 3,socket }, /* 97 = socket */…
{ 6,sendto }, /* 133 = sendto */…
}
syscall以编号为索引访问sysent结构数组,找到对应sysent表项,执行sy_call指向的插口函数。如何传递参数呢?syscall负责构造入口参数列表,将传入API的参数按照指定的顺序和格式存放到数组中,并将该数组作为入口参数传入到sy_call中。例如,API socket包含三个入口参数,进行系统调用时,被组织成socket_args结构。syscall以97作为索引访问sysent表,执行对应表项引用的内核函数socket,并将socket_args结构指针uap作为参数传入,执行结果存放在另一数组retval中返回。调用形式如下:
structsocket_args{
intdomain;
inttype;
intprotocol;
}
socket(struct proc*p,struct socket_args*uap, int *retval)。
(4)提出第四个问题:发送数据过程中,插口层是如何处理的?作为系统联网软件分层结构中最高的层次,插口层离用户最近,也是内核数据发送处理的第一站。该层包含许多插口操作函数,向应用进程提供协议无关的访问接口。在问题的引导下,教师结合源码,以应用程序对sendto的调用为起点,讲解插口的输出过程。应用程序执行sendto函数后,通过系统调用陷入执行插口层同名函数sendto。后者相继嵌套调用了sendit和sosend函数。这些函数的执行为协议层的数据封装做好准备,先后经过了四个步骤:其一,数据的转移。将待发数据从用户空间复制到内核中,分别构造存放目的地址和控制信息的mbuf链表和存放数据的mbuf链表。其二、判断插口发送缓存中是否有足够的空间来容纳待发送的数据。针对空间不足,存在两种不同处理方法。若插口是非阻塞式的,报错并立即返回。若插口设置工作在阻塞方式下,阻塞等待,直到插口发送缓冲有足够的空间为止。当然,为保证系统的性能,等待时间存在上限,由SO_SNDTIMEO选项确定。当等待定时超时,报错并返回。其三,将形成的数据mbuf链表加入到插口发送缓冲中。其四、调用相应协议的用户请求函数,执行PRU_SEND命令,启动协议内部的发送过程。
(5)提出第五个问题:协议层内部如何处理数据的发送?协议的用户请求函数是协议结构为插口层提供的访问点(AP),响应来自插口的不同命令,执行不同的处理过程。对于UDP协议,用户请求函数是udp_usrreq。它响应PRU_SEND命令后,调用udp_output函数,执行输出过程。udp_output的主要任务是为数据mbuf链表添加UDP头部,计算UDP校验和,并调用ip_output函数。ip_output是IP协议的输出处理函数,其处理过程如下:构造完整的IP头部,计算IP校验和。根据目的IP地址查找路由,并确定输出接口。若输出接口的MTU小于IP数据包的长度,执行分段功能。调用发送接口的输出函数。对于,以太网卡,执行ether_output。
(6)最后一个问题:接口层如何处理发送?以太接口的发送过程分成三部分:地址解析,帧封装和输出队列排队。首先根据下一跳路由器或目的主机的IP地址,获得相应MAC地址。封装以太帧头,写入源、目的MAC地址和类型字段,构造完整的数据帧后加入到接口的输出队列中。若接口当前状态空闲,立即启动硬件进行发送。
通过相继提出并解答上述六个问题,师生一起圆满完成一趟源码之旅。从插口函数的映射,到插口层、协议层、接口层的处理,我们从源码中自顶向下地见证了整个数据发送过程。
三、结束语
课程应用“实验主导教学,问题驱动学习”的教学模式,在实验中提出问题,通过查看源码来解决问题。整个教学过程以问题为原动力,组织教学内容,将知识点的讲解穿插在问题的解答中,既保证知识结构的完整和系统性,又在一定程度上增强教学活动的生动性,提高学生自主学习的热情。
参考文献:
[1]W.Richard Stevens.TCP/IP Illustrated Volume II:The Implementation[M].北京:机械工业出版社,2002.
[2]Douglas E.Comer.Internetworking with TCP/IP[M].北京:清华大学出版社,2000.
[3]James F Kurose,Keith W Ross.计算机网络——自顶向下方法与Internet特色[M].申震杰,等,译.北京:清华大学出版社,2003.
[4]张振荣,杨林峰,杨锋.基于探究式教学法的“计算机网络原理”课程的教学改革与实践[J].广西大学学报(自然科学版),2009,(7).
(责任编辑:麻剑飞)
关键词:源码;协议;实验;问题驱动;自顶向下
作者简介:王雪梅(1978-),女,江苏盐城人,南京邮电大学计算机学院,讲师;林晓勇(1974-),男,江苏南京人,南京邮电大学通信与信息工程学院,讲师。(江苏 南京 210003)
基金项目:本文系江苏省教育厅教改重大项目(项目编号:JG66609JX26)、南京邮电大学教改项目(项目编号:JG00410JX14、JG00211JX21)的研究成果。
中图分类号:G642.3 文献标识码:A 文章编号:1007-0079(2011)28-0109-02
“TCP/IP网络设计及实现”课程是我校计算机学院面向计算机科学与技术专业、信息安全专业学生开设的一门专业必修课。该课程以Unix操作系统下TCP/IP软件源码为素材来讲解联网协议栈软件的设计思想和实现技术。通过呈现协议实现细节,深化学生对网络基础理论的理解,使其对协议的认识上升到实现的高度,并通过源码解析提高学生网络编程的能力,有助于在网络应用开发中迅速诊断、定位错误,并有效解决问题。以源码为载体是该课程的一大特色,但庞大的源码也使教学面临很大的挑战。笔者在课堂教学实施过程中尝试“实验主导教学,问题驱动学习”的教学模式,结合实验和问题来应用情景分析的教学方法,从一个网络应用实例出发,自顶向下深入探析内核中的协议处理过程,逐步揭示内核中联网软件的细节。该方法取得较好的教学效果。
一、课程教学面临的问题
在源码阅读的过程中,深入了解协议实现的细节,是课程的基本教学目标。教学过程中涉及两个突出的问题:其一,如何对上万行的源码进行合理组织与取舍,既保证教学过程的系统性,又能激发学生自主学习的意识,提高学习的热情。其二,如何将源码细节的学习与实际网络应用开发过程进行有效的结合,在课程进展的过程中,逐步提高学生诊断错误和解决问题的能力,为培养网络类研发人才奠定基础。
二、“实验主导教学,问题驱动学习”教学模式
既然教学立足于庞大的开放源码,为保证教学的有序开展,寻找一个有效的切入点,是解决上述问题的关键所在。作者在教学过程中适时引入实践环节,通过部署实验项目来辅助课堂教学,使学生在解决问题的过程中自主去查看源码,从实践中获真知。下文通过几个教学案例来阐释以实验为主导和以问题为动力的教学方式。
1.协议地址的手动配置
一般情况下,主机接入校园网时,IP地址的配置是自动完成的。开机后,网络上部署的DHCP服务器会自动为个人PC分配唯一的IP地址。但这个过程涉及的多次应用报文的交互,以及更新本地协议地址的细节对用户而言都是透明的。对此,我们设计相应的实验项目,要求学生编写程序,实现静态IP地址的手动配置,通过在程序中调用ioctl函数来熟悉ioctl相关命令的命名和处理过程。教学的开展过程如下:
(1)以管理员或超级用户身份登录,利用系统提供的网络连接属性配置窗口,选择“Internet(TCP/IP)属性”,填入随机的合法IP地址,和子网掩码、默认网关地址,以及DNS服务器地址。选择不同的IP地址多次执行,观察结果。事实证明,手动配置存在不成功的情况。接着,引导学生分析原因,从而得出第一个结论,手动配置可能会遭遇地址冲突问题。然后,教师及时提出问题:系统是如何检测出地址重复?在问题的驱动下,为学生预留思考和讨论的时间。最后教师给出准确答案,并定位到相应的源码进行释疑和解惑。
(2)以普通用户身份登录,执行与(1)相同的操作。系统提示权限不足,配置不成功。引导学生分析这一现象,再定位到代码中进行深入讲解。
(3)从手动配置过程引入ioctl机制的讲解。展示并解释ioctl函数原型后,介绍相关地址访问和配置命令,如SIOCGIFADDR,SIOCSIFADDR等。
(4)以地址配置为例深入详解内核中相关ioctl命令的实现细节,包括函数调用的流程,以及命令的具体处理过程。
(5)布置课外实验项目,编写简单的静态地址配置程序。允许用户在参数中指定新IP地址等信息。程序执行结果包括:配置前后所有接口的地址信息。
这个教学案例以实验操作为切入点,逐步引导学生发现问题、解决问题,从而引出并深入讲解ioctl机制,最终通过学生实践加深并巩固对ioctl的使用和理解。这种从表象深入本质,将教师课堂演示与学生课后实验有效结合的方法,取得较好的教学效果。
2.数据发送过程的自顶向下分析
我们直接使用教材[1]第一章提供的简单客户端程序,主要执行日期时间查询功能。在向网络上的日期时间服务器发送一个查询请求后,接收服务器的响应,并将结果显示在屏幕上。程序中涉及三个API函数的调用(socket,sendto,recvfrom),分别用来创建网络插口、通过插口发送和接收数据。下面作者从这个简单客户端程序出发,应用自顶向下的方法,在连续六个问题的驱动下,和学生一起探析数据的发送处理过程。
(1)这些API函数往往定义在函数库中,其目标码链接到可执行程序中。学生只要熟悉每个网络API函数的引用格式、调用方法就能够编写简单的网络应用程序。它们并不执行真正的网络操作,主要作用是验证入口参数的有效性,并通过系统调用进入内核,执行相应内核插口函数。这里,提出第一个问题:系统调用和过程调用有什么区别?这是两个很混淆的概念。为了加深学生的理解,在给出答案前,先促进学生自己思考并进行讨论。为回答该问题,着重从地址空间和调用方式两个方面进行比较。
(2)提出第二个问题:在程序中可以直接使用系统调用来访问内核中的插口函数吗?这个问题从程序移植角度来考虑。移植性的理解会使学生对程序设计的认识上升到一个新的高度。事实上,用户在编制程序时,如果熟悉所用操作系统的系统调用方式,完全可以直接进行系统调用来访问内核插口函数,但由于不同的操作系统实现系统调用的方法存在很大差异,如果在程序中直接进行系统调用,不利于程序的移植,影响程序的通用性。用户函数库以统一函数接口的调用方式屏蔽了操作系统的系统调用的差异性,提高程序的移植性。
(3)提出第三个问题:如何完成API函数与内核插口函数之间的关联与映射?在定位到相关源码后,通过和学生一起来阅读、分析代码,来逐步阐述映射机制。在BSD Net/3的386实现中,内核函数syscall和数据结构sysent扮演了重要的角色。当API函数中使用系统调用时,CPU转去执行内核函数syscall,由syscall执行映射的任务。sysent是系统维持的映射表sysent,每个表项存放一个插口函数的调用信息,其结构定义如下:
structsysent {
intsy_narg;
int (*sy_call)();
}
其中,sy_call是指向对应插口函数的指针,sy_narg表示传入sy_call所指函数的参数数目。插口函数具有唯一的编号,对应表中索引。映射表的初始化如下:
structsysentsysent [ ] = {…
{ 6,recvfrom }, /* 29 = recvfrom */…
{ 3,socket }, /* 97 = socket */…
{ 6,sendto }, /* 133 = sendto */…
}
syscall以编号为索引访问sysent结构数组,找到对应sysent表项,执行sy_call指向的插口函数。如何传递参数呢?syscall负责构造入口参数列表,将传入API的参数按照指定的顺序和格式存放到数组中,并将该数组作为入口参数传入到sy_call中。例如,API socket包含三个入口参数,进行系统调用时,被组织成socket_args结构。syscall以97作为索引访问sysent表,执行对应表项引用的内核函数socket,并将socket_args结构指针uap作为参数传入,执行结果存放在另一数组retval中返回。调用形式如下:
structsocket_args{
intdomain;
inttype;
intprotocol;
}
socket(struct proc*p,struct socket_args*uap, int *retval)。
(4)提出第四个问题:发送数据过程中,插口层是如何处理的?作为系统联网软件分层结构中最高的层次,插口层离用户最近,也是内核数据发送处理的第一站。该层包含许多插口操作函数,向应用进程提供协议无关的访问接口。在问题的引导下,教师结合源码,以应用程序对sendto的调用为起点,讲解插口的输出过程。应用程序执行sendto函数后,通过系统调用陷入执行插口层同名函数sendto。后者相继嵌套调用了sendit和sosend函数。这些函数的执行为协议层的数据封装做好准备,先后经过了四个步骤:其一,数据的转移。将待发数据从用户空间复制到内核中,分别构造存放目的地址和控制信息的mbuf链表和存放数据的mbuf链表。其二、判断插口发送缓存中是否有足够的空间来容纳待发送的数据。针对空间不足,存在两种不同处理方法。若插口是非阻塞式的,报错并立即返回。若插口设置工作在阻塞方式下,阻塞等待,直到插口发送缓冲有足够的空间为止。当然,为保证系统的性能,等待时间存在上限,由SO_SNDTIMEO选项确定。当等待定时超时,报错并返回。其三,将形成的数据mbuf链表加入到插口发送缓冲中。其四、调用相应协议的用户请求函数,执行PRU_SEND命令,启动协议内部的发送过程。
(5)提出第五个问题:协议层内部如何处理数据的发送?协议的用户请求函数是协议结构为插口层提供的访问点(AP),响应来自插口的不同命令,执行不同的处理过程。对于UDP协议,用户请求函数是udp_usrreq。它响应PRU_SEND命令后,调用udp_output函数,执行输出过程。udp_output的主要任务是为数据mbuf链表添加UDP头部,计算UDP校验和,并调用ip_output函数。ip_output是IP协议的输出处理函数,其处理过程如下:构造完整的IP头部,计算IP校验和。根据目的IP地址查找路由,并确定输出接口。若输出接口的MTU小于IP数据包的长度,执行分段功能。调用发送接口的输出函数。对于,以太网卡,执行ether_output。
(6)最后一个问题:接口层如何处理发送?以太接口的发送过程分成三部分:地址解析,帧封装和输出队列排队。首先根据下一跳路由器或目的主机的IP地址,获得相应MAC地址。封装以太帧头,写入源、目的MAC地址和类型字段,构造完整的数据帧后加入到接口的输出队列中。若接口当前状态空闲,立即启动硬件进行发送。
通过相继提出并解答上述六个问题,师生一起圆满完成一趟源码之旅。从插口函数的映射,到插口层、协议层、接口层的处理,我们从源码中自顶向下地见证了整个数据发送过程。
三、结束语
课程应用“实验主导教学,问题驱动学习”的教学模式,在实验中提出问题,通过查看源码来解决问题。整个教学过程以问题为原动力,组织教学内容,将知识点的讲解穿插在问题的解答中,既保证知识结构的完整和系统性,又在一定程度上增强教学活动的生动性,提高学生自主学习的热情。
参考文献:
[1]W.Richard Stevens.TCP/IP Illustrated Volume II:The Implementation[M].北京:机械工业出版社,2002.
[2]Douglas E.Comer.Internetworking with TCP/IP[M].北京:清华大学出版社,2000.
[3]James F Kurose,Keith W Ross.计算机网络——自顶向下方法与Internet特色[M].申震杰,等,译.北京:清华大学出版社,2003.
[4]张振荣,杨林峰,杨锋.基于探究式教学法的“计算机网络原理”课程的教学改革与实践[J].广西大学学报(自然科学版),2009,(7).
(责任编辑:麻剑飞)