20145337 《信息安全系统设计基础》第十三周学习总结
教材学习内容总结
网络编程
客户端-服务器编程模型
- 一个服务器进程 -> 管理某种资源 -> 通过操作这种资源来为它的客户端提供某种服务
- 一个或多个客户端进程
基本操作:事务 - 当一个客户端需要服务时,向服务器发送一个请求,发起一个事务。
- 服务器收到请求后,解释它,并以适当的方式操作它的资源。
- 服务器给客户端发送一个相应,并等待下一个请求。
- 客户端收到响应并处理它。
- 客户端和服务器都是进程,不是机器或者主机
- 一台主机可同时运行许多不同客户端服务器,且同一个客户端服务器的事务可在同一台或不同台主机运行
网络
- 对主机而言,网络是一种I/O设备:从网络上接收到的数据从适配器经过I/O和存储器总线拷贝到存储器,典型地是通过DMA(直接存储器存取方式)传送。
- 物理上,网络是一个按照地理远近组成的层次系统:最低层是LAN(局域网),最流行的局域网技术是以太网。
- 以太网段
- 包括一些电缆和集线器。每根电缆都有相同的最大位带宽,集线器不加分辩地将一个端口上收到的每个位复制到其他所有的端口上,因此每台主机都能看到每个位。
- 每个以太网适配器都有一个全球唯一的48位地址,存储在适配器的非易失性存储器上。
- 一台主机可以发送一段位:帧,到这个网段内其它任何主机。每个帧包括一些固定数量的头部位(标识此帧的源和目的地址及帧长)和数据位(有效载荷)。每个主机都能看到这个帧,但是只有目的主机能读取。
- 使用电缆和网桥,多个以太网段可以连接成较大的局域网,称为桥接以太网。这些电缆的带宽可以是不同的。
- 多个不兼容的局域网可以通过叫做路由器的特殊计算机连接起来,组成一个internet互联网络。
- 互联网重要特性:由采用不同技术,互不兼容的局域网和广域网组成,并能使其相互通信。其中不同网络相互通信的解决办法是一层运行在每台主机和路由器上的协议软件,消除不同网络的差异。
- 协议提供的两种基本能力
- 命名机制:唯一的标示一台主机
- 传送机制:定义一种把数据位捆扎成不连续的片的同一方式
- 互联网络思想的精髓,封装是关键
全球IP因特网
- TCP/IP协议族
- 混合使用套接字接口函数和UnixI/O函数进行通信
- TCP提供进程间可靠的、全双工连接。
- 特性:
- 主机集合被映射为一组32位的IP地址
- 这组IP地址被映射为一组称为因特网域名的标识符
- 因特网主机上的进程能够通过连接和任何其他主机上的进程
- 检索并打印一个DNS主机条目:
#include "csapp.h"
int main(int argc, char **argv)
{
char **pp;
struct in_addr addr;
struct hostent *hostp;
if (argc != 2) {
fprintf(stderr, "usage: %s <domain name or dotted-decimal>\n",
argv[0]);
exit(0);
}
if (inet_aton(argv[1], &addr) != 0)
hostp = Gethostbyaddr((const char *)&addr, sizeof(addr), AF_INET);
else
hostp = Gethostbyname(argv[1]);
printf("official hostname: %s\n", hostp->h_name);
for (pp = hostp->h_aliases; *pp != NULL; pp++)
printf("alias: %s\n", *pp);
for (pp = hostp->h_addr_list; *pp != NULL; pp++) {
addr.s_addr = ((struct in_addr *)*pp)->s_addr;
printf("address: %s\n", inet_ntoa(addr));
}
exit(0);
}
套接字接口
Web服务器
- 协议
- Web 客户端和服务器之间的交互用的是一个基于文本的应用级协议,叫做 HTTP (Hypertext Transfer Protocol,超文本传输协议). HTTP 是一个简单的协议。一个 Web 客户端(即浏览器) 打开一个到服务器的因特网连接,并且请求某些内容。服务器响应所请求的内容,然后关闭连接。浏览器读取这些内容,并把它显示在屏幕上。
- 内容
- Web内容可以用一种叫做 HTML(Hypertext Markup Language,超文本标记语言)的语言来编写。一个 HTML 程序(页)包含指令(标记),它们告诉浏览器如何显示这页中的各种文本和图形对象。
- Web 服务器以两种不同的方式向客户端提供内容:
- 取一个磁盘文件,并将它的内容返回给客户端。磁盘文件称为静态内容 (static content), 而返回文件给客户端的过程称为服务静态内容 (serving static content)。 运行一个可执行文件,并将它的输出返回给客户端。运行时可执行文件产生的输出称为态内容 (dynamic content),而运行程序并返回它的输出到客户端的过程称为服务动态内容 (serving dynamic content)。
并发编程
-应用级并发
- 访问慢速I/O设备
- 与人交互
- 通过推迟工作以降低延迟
- 服务多个网络客户端
- 在多核机器上进行并行计算
- 进程
- I/O多路复用
- 线程
基于进程的并发编程
- 构造并发服务器的自然方法就是,在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。
- 因为父子进程中的已连接描述符都指向同一个文件表表项,所以父进程关闭它的已连接描述符的拷贝是至关重要的,而且由此引起的存储器泄露将最终消耗尽可用的存储器,使系统崩溃。
- 优点:
- 进程有独立的地址空间
- 缺点:
- 进程控制和IPC开销高,速度慢
基于I/O多路复用的并发编程
- echo服务器必须响应两个相互独立的I/O时间:
- 网络客户端发起连接请求
- 用户在键盘上键入命令行
- I/O多路复用技术的基本思路:使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。
- 将描述符集合看成是n位位向量:b(n-1),……b1,b0 ,每个位bk对应于描述符k,当且仅当bk=1,描述符k才表明是描述符集合的一个元素。可以做以下三件事:
- 分配它们;
- 将一个此种类型的变量赋值给另一个变量;
- 用FDZERO、FDSET、FDCLR和FDISSET宏指令来修改和检查它们。
- echo函数:将来自科幻段的每一行回送回去,直到客户端关闭这个链接。
- 状态机就是一组状态、输入事件和转移,转移就是将状态和输入时间映射到状态,自循环是同一输入和输出状态之间的转移。
- 事件驱动器的设计优点:
- 比基于进程的设计给了程序员更多的对程序行为的控制
- 运行在单一进程上下文中,因此,每个逻辑流都能访问该进程的全部地址空间,使得流之间共享数据变得很容易。
- 不需要进程上下文切换来调度新的流。
- 缺点:
- 编码复杂
- 不能充分利用多核处理器
- 粒度:每个逻辑流每个时间片执行的指令数量。并发粒度就是读一个完整的文本行所需要的指令数量。
基于线程的并发编程
- 线程:运行在进程上下文中的逻辑流。
- 线程有自己的线程上下文,包括一个唯一的整数线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有运行在一个进程里的线程共享该进程的整个虚拟地址空间。
- Posix 线程
- Posix 线程 (Pthreads) 是在 C 程序中处理线程的一个标准接口。Pthreads 定义了大约 60 个函数,允许程序创建、杀死和回收线程,与对等线程安全地共享数据,还可以通知对等线程系统状态的变化。
- 线程的代码和本地数据被封装在一个线程例程(thread routine) 中。如果想传递多个参数给钱程例程,那么你应该将参数放 到一个结构中,并传递一个指向该结构的指针。想要线程例程返回多个参数,你可以返回一个指向一个结构的指针。
- 创建线程
- pthread_create 函数创建一个新的线程,并带着一个输入变量arg,在新线程的上下文中运行线程例程f。能用attr参数来改变新创建线程的默认属性。
- 当 pthreadcreate 返回时,参数 tid包含新创建线程的ID。新线程可以通过调用 pthreadself 函数来获得它自己的线程 ID.
- 终止线程
- 当顶层的线程例程返回时,线程会隐式地终止。 通过调用 pthreadexit 函数,线程会显式地终止。如果主线程调用 pthreadexit , 它会等待所有其他对等线程终止,然后再终止主线程和整个进程,返回值为 thread_return。
- 回收已终止线程的资源
- 线程通过调用 pthread_join 函数等待其他线程终止。
- pthreadjoin 函数会阻塞,直到线程 tid 终止,将线程例程返回的 (void*) 指针赋值为 threadreturn 指向的位置,然后回收己终止线程占用的所有存储器资源。
- pthread join 函数只能等待一个指定的线程终止。
- 分离线程
- 在任何一个时间点上,线程是可结合的 (joinable) 或者是分离的 (detached)。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是没有被释放的。相反,一个分离的线程是不能被其他线程回收或杀死的。它的存储器资源在它终止时由系统自动释放。
- 默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被其他线程显式地收回,要么通过调用 pthread_detach 函数被分离。
- pthreaddetach 函数分离可结合线程 tid. 线程能够通过以 pthreadself()为参数的 pthread_detach 调用来分离它们自己。
- 初始化线程
- pthread_once 函数允许你初始化与线程例程相关的状态。
- 一个基于线程的并发服务器
- 调用 pthread_ create 时,如何将已连接描述符传递给对等线程。最明显的方法就是传递一个指向这个描述符的指针。 对等线程间接引用这个指针,并将它赋值给一个局部变量。
进度图
- 进度图将指令执行模式化为从一种状态到另一种状态的转换。转换被表示为一条从一点到相邻点的有向边。合法的转换是向右或者向上。
- 临界区:对于线程i,操作共享变量cnt内容的指令构成了一个临界区。
- 互斥的访问:确保每个线程在执行它的临界区中的指令时,拥有对共享变量的互斥的访问。
- 安全轨迹线:绕开不安全区的轨迹线
- 不安全轨迹线:接触到任何不安全区的轨迹线就叫做不安全轨迹线
- 任何安全轨迹线都能正确的更新共享计数器。
信号量实现互斥
- 生产者-消费问题
- 读者-写者问题
代码调试中的问题和解决过程
-
condvar
-
消费者等待生产者产产出产品后打印
-
count
-
不加锁的创建两个线程共享同一变量都实现加一操作
-
countwithmutex
-
引入互斥锁
-
cp_t
-
用法: ./cp_t [源文件名][目的文件名][创建线程数]
-
createthread
-
打印进程和线程ID
-
semphore
-
share
-
获得线程的终止状态
-
threadexit
-
hello_multi
-
hello_multi1
-
hello_single
-
incprint
-
twordcount1
-
统计文件1和文件2两个文件的总字数
-
twordcount2
-
twordcount3
-
分别对文件1、2两个文件进行字数统计,并把两个文件的字数加起来统计总字数
-
twordcount4
-
分别运行代码根据锁的情况分别统计字数,再把两个文件的字数加起来统计总字数
本周代码托管截图
其他(感悟、思考等,可选)
这周主要学习了网络编程及并发编程的相关知识,本身看着比较复杂,但结合JAVA WEB和操作系统这两门课,就容易理解多了。在JAVA WEB中实现客户端-服务器模型 寥寥几十行代码,只是学到了皮毛,仔细看十一章,才明白其中原理。期末将近,最后几周希望能补上一些,花更多的时间理解把知识掌握扎实。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第十三周 | 200/200 | 2/2 | 20/20 | 学习网络编程及并发编程相关知识 |