20145321 《信息安全系统设计基础》第13周学习总结
教材内容总结
第十一章 网络编程
客户端-服务器编程模型
- 一个服务器进程 -- 管理某种资源 -- 经过操做这种资源来为它的客户端提供某种服务
- 一个应用是由一个服务器进程和一个或者多个客户端进程组成的
网络
对主机而言:网络是一种I/O设备git
- 从网络上接收到的数据从适配器通过I/O和存储器总线拷贝到存储器,典型地是经过DMA(直接存储器存取方式)传送。
物理上而言,网络是一个按照地理远近组成的层次系统。程序员
- 最底层是LAN(局域网)
- 适配器提供到网络的物理接口
以太网段(电缆+集线器)编程
- 包括一些电缆和集线器。每根电缆都有相同的最大位带宽,集线器不加分辩地将一个端口上收到的每一个位复制到其余全部的端口上,所以每台主机都能看到每一个位。
- 一台主机能够发送一段位,称为帧。每一个主机适配器都能看到这个帧,可是只有目的主机实际读取它。
- 多个以太网段能够链接成较大的局域网,称为桥接以太网。
- 网桥比集线器更充分的利用了电缆带宽。
- 每一个以太网适配器都有一个全球惟一的48位地址,存储在适配器的非易失性存储器上。
网络协议提供两种基本能力浏览器
- 命名机制:惟一的标示一台主机
- 传送机制:定义一种把数据位捆扎成不连续的片的同一方式
全球IP因特网
TCP/IP其实是一个协议族安全
- IP机制从某种意义上而言是不可靠的。TCP是一个构建在IP之上的复杂协议,提供了进程间可靠地全双工链接。
IP地址服务器
- 一个IP地址就是一个32位无符号整数
- IP/TCP为任意整数数据项定义了统一的网络字节顺序(大端字节顺序)
- 对inet-aton的调用传递的是指向结构的指针,而对inet_ntoa的调用传递的是结构自己
世界范围的主机集合网络
- 主机集合被映射为一组32位的IP地址
- 这组IP地址被映射为一组称为因特网域名的标识符
- 因特网主机上的进程可以经过链接和任何其余主机上的进程
因特网链接多线程
- 点对点、全双工、可靠
- 客户端套接字地址中的端口是由内核自动分配的,称为临时端口
- 服务端套接字地址中的端口一般是某个知名端口
套接字接口

函数并发
- socket函数:客户端和服务端使用socket函数来建立一个套接字描述符
- connect函数:创建和服务器的链接。
- open_clientfd函数:将socket和connect函数包装而成。客户端能够用它来和服务器创建链接。
- bind函数:将一本地地址与一套接口捆绑。
- listen函数:使用主动链接套接字变为被链接套接口,使得一个进程能够接受其它进程的请求,从而成为一个服务器进程。
- open_listenfd函数:socket、bind和listen函数结合。用于服务器建立一个监听描述符。
- accept函数:均被服务器用于和客户端创建链接。
Web服务器
协议socket
- Web 客户端和服务器之间的交互用的是一个基于文本的应用级协议,叫作 HTTP (Hypertext Transfer Protocol,超文本传输协议). HTTP 是一个简单的协议。一个 Web 客户端(即浏览器) 打开一个到服务器的因特网链接,而且请求某些内容。服务器响应所请求的内容,而后关闭链接。浏览器读取这些内容,并把它显示在屏幕上。
内容
- Web内容能够用一种叫作 HTML(Hypertext Markup Language,超文本标记语言)的语言来编写。一个 HTML 程序(页)包含指令(标记),它们告诉浏览器如何显示这页中的各类文本和图形对象。
- 对于Web客户端和服务端而言,内容是与一个MIME类型相关的字节序列
- Web 服务器以两种不一样的方式向客户端提供内容:
- 取一个磁盘文件,并将它的内容返回给客户端。磁盘文件称为静态内容 , 而返回文件给客户端的过程称为服务静态内容
- 运行一个可执行文件,并将它的输出返回给客户端。运行时可执行文件产生的输出称为态内容 ,而运行程序并返回它的输出到客户端的过程称为服务动态内容
- 每条由Web服务器返回的内容都是和他管理的某个文件相关联的。这些文件每个都有一个惟一的名字,叫作:URL
第十二章 并发编程
使用应用级并发的应用程序称为并发程序。现代操做系统提供了三种基本的构造并发程序的方法:
- 进程。每一个逻辑控制流都是一个进程,由内核来调度和维护。由于进程 有独立的虚拟地址空间,想要和其余流通讯,控制流必须使用某种显式的进程间通讯(IPC)机制。
- I/O 多路复用。在这种形式的并发编程中,应用程序在一个进程的上下文中显式地调度它们本身的逻辑流。逻辑流被模型化为状态机,数据到达文件描述符后,主程序显式地从一个状态转换到另外一个状态。由于程序是一个单独的进程,因此全部的流都共享同一个地址空间。
- 线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。是其余两种方式的混合体,像进程流同样由内核进行调度,而像I/O 多路复用流同样共享同一个虚拟地址空间
基于进程的并发进程
基于进程的并发服务器
- 首先,包括一个SIGCHLD处理程序,回收僵死子进程资源
- 其次,父子进程必须关闭它们各自的connfd拷贝,以避免存储器泄露
- 最后,知道父子进程的connfd都关闭了,到客户端的链接才会终止
构造并发程序最简单的方法就是用进程
- 第一步:服务器接受客户端的链接请求
- 第二步:服务器派生一个子进程为这个客户端服务
- 第三步:服务器接受另外一个链接请求
- 第四步:服务器派生另外一个子进程为新的客户端服务
进程的优劣
基于I/O多路复用的并发进程
I/O 多路复用技术基本的思路就是使用 select 函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。
服务器调用 select 函数来 检测两种不一样类型的输人事件:
- 来自一个新客户端的链接请求到达
- 一个己存在的客户 端的己链接描述符准备好能够读了。
基于I/O多路复用的并发事件驱动服务器
- 一个状态机就是:状态、输入事件和转移(状态机sk)
- 状态:等待描述符dk准备好可读
- 输入事件:描述符dk准备好能够读了
- 转移:从描述符dk读一个文本行
- 自循环是同一输入和输出状态之间的转移
I/O 多路复用技术的优劣
- 事件驱动设计的优势:
- 它比基于进程的设计给了程序员更多的对程序行为的控制。
- 一个基于 I/O 多路复用的事件驱动服务器是运行在单一进程上下文中的,因 此每一个逻辑流都能访问该进程的所有地址空间。
- 缺点就是编码复杂。咱们的事件驱动的并发 echo 服务器须要的代码比基于进程的服务器多三倍。不幸的是,随着并发粒度的减少,复杂性还会上升。这里的粒度是指每一个逻辑流每一个时间片执行的指令数量。
基于线程的并发编程
- 线程(thread) 就是运行在进程上下文中的逻辑流。
- 每一个线程都有它本身的线程上下文 (thread context),包括一个惟一的整数线程 (Thread ID, TID)、栈、栈指针、程序计数器、通用目的寄存器和条件码。全部的运行在一个进程里的线程共享该进程的整个虚拟地址空间。
- 基于线程的逻辑流结合了基于进程和基于 I/O 多路复用的流的特性。同进程同样,线程由内核自动调度,而且内核经过一个整数 ID 来识别线程。同基于 I/O 多路复用的流同样,多个线程 运行在单一进程的上下文中,所以共享这个进程虚拟地址空间的整个内容,包括它的代码、数据、堆、共享库和打开的文件。
- 线程执行模型
- 主线程:每一个进程开始生命周期时第一个运行的线程
- 对等线程:某时刻主线程建立的
- 线程的上下文切换要比进程的上下文切换快得多。
- 和一个进程相关的线程组成一个对等(线程)池 (pool),独立于其余线程建立的线程。
- 线程不像进程同样,不是按照严格的父子进程组织的
- 每一个对等线程都能读写相同共享数据
- Posix 线程
- Posix 线程 (Pthreads) 是在 C 程序中处理线程的一个标准接口。Pthreads 定义了大约 60 个函数,容许程序建立、杀死和回收线程,与对等线程安全地共享数据,还能够通知对等线程系统状态的变化。
- 线程的代码和本地数据被封装在一个线程例程(thread routine) 中。若是想传递多个参数给钱程例程,那么你应该将参数放 到一个结构中,并传递一个指向该结构的指针。想要线程例程返回多个参数,你能够返回一个指向一个结构的指针。
- 建立线程
- 线程经过调用pthread_create函数来建立其余线程
- 新线程能够经过调用pthread_self函数来得到它本身的线程ID
- 终止线程
- 当顶层的线程例程返回时,线程会隐式地终止。 经过调用 pthreadexit 函数,线程会显式地终止。若是主线程调用 pthreadexit , 它会等待全部其余对等线程终止,而后再终止主线程和整个进程,返回值为 thread_return。
- 回收已终止线程的资源
- 线程经过调用pthread_join函数等待其余线程终止
- 与wait函数不一样,pthread_join函数只能等待一个指定的线程终止
- 分离线程
- 一个分离的线程是不能被其余线程回收或杀死的。它的存储器资源在它终止时由系统自动释放
- 每一个可结合线程都应该要么被其余线程显示的收回,要么经过调用pthread_detach函数被分离
- pthreaddetach 函数分离可结合线程 tid. 线程可以经过以 pthreadself()为参数的 pthread_detach 调用来分离它们本身。
- 初始化线程
- pthread_once 函数容许你初始化与线程例程相关的状态。
- 一个基于线程的并发服务器
- 调用 pthread_ create 时,如何将已链接描述符传递给对等线程。最明显的方法就是传递一个指向这个描述符的指针。 对等线程间接引用这个指针,并将它赋值给一个局部变量。
多线程程序中的共享变量
线程存储器模型
- 一个变量是共享的,当且仅当多个线程引用这个变量的某个实例。
将变量映射到存储器(根据存储类型)
- 全局变量:全局变量是定义在函数以外的变量
- 本地自动变量:本地自动变量就是定义在函数内部可是没有static属性的变量
- 本地静态变量:本地静态变量是定义在函数内部有static属性的变量
共享变量
- 变量是共享的:当且仅当它的一个实例被一个以上的线程引用。
用信号量同步线程
进度图
- 进度图是将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,原点对应于没有任何线程完成一条指令的初始状态。
- 当n=2时,状态比较简单,是比较熟悉的二维坐标图,横纵坐标各表明一个线程,而转换被表示为有向边
- 转换规则:
- 合法的转换是向右或者向上,即某一个线程中的一条指令完成
- 两条指令不能在同一时刻完成,即不容许出现对角线
- 程序不能反向运行,即不能出现向下或向左
- 而一个程序的执行历史被模型化为状态空间中的一条轨迹线。
- 线程循环代码的分解: H:在循环头部的指令块 L:加载共享变量cnt到线程i中寄存器%eax的指令。 U:更新(增长)%eax的指令 S:将%eax的更新值存回到共享变量cnt的指令 T:循环尾部的指令块
- 临界区:对于线程i,操做共享变量cnt内容的指令L,U,S构成了一个关于共享变量cnt的临界区。
- 不安全区:两个临界区的交集造成的状态
- 安全轨迹线:绕开不安全区的轨迹线
信号量
- 信号量实现互斥的基本原理
- 两个或多个进程经过传递信号进行合做,能够迫使进程在某个位置暂时中止执行(阻塞等待),直到它收到一个能够“向前推动”的信号(被唤醒);
- 将实现信号灯做用的变量称为信号量,常定义为记录型变量s,其一个域为整型,另外一个域为队列,其元素为等待该信号量的阻塞进程(FIFO)。
- 使用信号量来实现互斥基本思想
- 将每一个共享变量(或者一组相关的共享变量)与一个信号量s(初始为1)联系起来,而后用P和V操做将相应的临界区包围起来。
- 互斥锁:以提供互斥为目的的二元信号量
- 加锁:一个互斥锁上执行P操做称为对互斥锁加锁,执行V操做称为对互斥锁解锁。对一个互斥锁加了锁但尚未解锁的线程称为占用了这个互斥锁。
- 计数信号量:一个呗用做一组可用资源的计数器的信号量
- wait(s)/signal(s)的应用
- 进程进入临界区以前,首先执行wait(s)原语,若s.count<0,则进程调用阻塞原语,将本身阻塞,并插入到s.queue队列排队;
- 注意,阻塞进程不会占用处理机时间,不是“忙等”。直到某个从临界区退出的进程执行signal(s)原语,唤醒它;
- 一旦其它某个进程执行了signal(s)原语中的s.count+1操做后,发现s.count ≤0,即阻塞队列中还有被阻塞进程,则调用唤醒原语,把s.queue中第一个进程修改成就绪状态,送就绪队列,准备执行临界区代码。
本周代码托管
代码托管连接
代码量统计

学习进度条
目标 |
3500行 |
30篇 |
300小时 |
第一周 |
50/50 |
1/2 |
10/10 |
第二周 |
120/170 |
1/3 |
20/30 |
第三周 |
130/300 |
1/4 |
20/50 |
第五周 |
130/430 |
2/6 |
25/75 |
第六周 |
50/480 |
2/8 |
25/100 |
第七周 |
53/533 |
1/9 |
20/120 |
第八周 |
0/533 |
2/11 |
15/135 |
第九周 |
68/601 |
2/13 |
20/155 |
第十周 |
468/1069 |
1/15 |
20/175 |
十一周 |
404/1473 |
2/17 |
20/195 |
十二周 |
0/1473 |
2/19 |
15/210 |
十三周 |
1001/2474 |
1/20 |
20/230 |