20145329 《信息安全系统设计基础》第十三周学习总结
教材学习内容总结
十一章网络编程
11.1客户端——服务器模型
- 每个网络应用都是基于客户端——服务器模型的
- 客户端——服务器模型中的基本操作是事务,一个客户端——服务器事务由四步组成:
- 当一个客户端需要服务时,向一个服务器发送一个请求,发起一个事务
- 服务器收到请求后,解释他,并以适当的方式操作它的资源
- 服务器给客户端发送一个响应,并等待下一个请求
- 客户端收到响应并处理它
- 无论客户端和服务器是怎样映射到主机的,客户端——服务器模型是相同的
11.2网络
- 客户端和服务器通常运行在不同的主机上,通过计算机网络的硬件和软件资源来进行通信。网络是一种I/O设备,数据通过直接存储器存取方式从适配器经过I/O和存储器总线在存储器和网络间传送。
- 以太网
最流行的局域网技术,一个以太网段包括一些电缆和一个叫集线器的小盒子。
每个以太网适配器都有一个全球唯一的48位地址,它存储在这个适配器的非易失性存储器上。
11.3全球IP英特网
- 英特网可以看作是一个世界范围的主机集合,满足一下特性:
- 主机集合被映射为一组32位的IP地址
- 这组IP地址被映射为一组称为英特网域名的标识符
- 英特网主机上的进程能够通过连接和其他任何英特网主机上的进程通信
-
IP地址结构
struct in_addr{
unsigned int s_addr;
} -
TCP/IP为任意整数数据项定义了统一的网络字节顺序:大端字节顺序,即使主机字节顺序是小端法,IP地址结构中存放的数据总是以大端法网络字节顺序存放的。
-
Unix实现网络和主机字节顺序转换的函数
.#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort); -
Linux上查看主机点分十进制地址:
Linux> hostname -i -
ip地址和点分十进制地址之间的转换
.#include <arpa/inet.h>
int inet_aton(const char *p,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
-
英特网域名
英特网应用程序通过调用gethostbyname和gethostbyaddr函数,从DNS数据库中检索任意的主机条目
-
英特网连接
一个套接字连接的是一个端口,由相应的套接字地址,套接字地址由一个英特网地址和一个16位的整数端口组成。客户端发起连接请求时,客户端套接字地址中的端口由内核自动分配。一个链接由它的套接字地址唯一确定,这对套接字地址称为套接字对。
Web服务器
-
Web服务和常规检索服务的主要区别
web内容可以用一种叫做HTML的语言来编写 -
对于web客户端与服务器而言,内容是一个与MIME(多用途的网际邮件扩充协议)类型相关的字节序列
-
web服务器可以以两种不同的方式向客户端提供内容
- 取一个磁盘文件,并将它的内容返回给客户端,磁盘文件称为静态内容,返回文件给客户端的过程称为服务静态内容
- 运行一个可执行文件,将输出返回给客户端,运行使能可执行文件产生的输出为动态内容,该过程称为服务动态内容
并发编程
-
并发:逻辑控制流在时间上重叠,引种操作系统内核用来运行多个应用程序的机制
-
应用级并发
- 访问慢速I/O设备:等待数据时,运行其他进程,保持CPU繁忙
- 与人交互:计算机与人交互要求有同时执行多个任务的能力
- 通过推迟工作以降低延迟:创建一个并发服务器,为每个客户端创建一个单独的逻辑流
- 在多核机器上进行并行运算:多核处理器中包含多个CPU
- 构造并发进程的方法
- 进程:有独立的虚拟地址空间,要和其他程序通信需采用显式的进程间通信机制
- I/O多路复用:一个进程在它的上下文中显式的调用自己的逻辑流
- 线程:运行在一个单一进程的上下文逻辑流中,由内核调用,并且内核通过一个整数ID来唯一标识,共享进程虚拟地址空间的全部内容,是进程和I/O多路复用的混合体。
12.1基于进程的并发编程
- 并发服务器工作过程简述
- 服务器监听描述符上的链接请求
- 接受客户端1的请求,返回一个已连接描述符
- 在父进程中接受客户端连接请求后,服务器派生出一个子进程,获得服务器描述表的完整拷贝
- 子进程关闭拷贝中的监听描述符,父进程关闭已连接描述符的拷贝
- 进程的优劣
优点:进程不可能不小心覆盖另一进程的虚拟存储器
缺点:为了共享信息,必须使用显示的IPC机制,使系统开销变大
12.2基于I/O多路复用的并发编程
-
基本思想
使用select函数,要求内核挂起进程,只有在一个或多个I/O时间发生后,在将控制返回给应用程序 -
select函数
- select函数处理描述符集合,进行
1)分配他们
2)将此种类型的变量赋值给另一变量
3)用FD_ZERO、FD_SET、FD_CLR和FD_ISSET宏指令来修改和检查他们 - select函数的两个输入
1)读集合的描述符集合
2)该读集合的基数 - select函数一直阻塞,直到读集合中至少一个描述符准备好可以读
- 每次调用select函数时,更新读集合
- 基于I/O多路复用的并发事件驱动服务器
- 逻辑流模型化为状态机,包括
一个状态:描述符准备好可以读
一个输入事件:描述符准备好可以读了
一个转移:描述符读到一个文本行 - 借助select函数检测输入事件的发生,当每个已连接描述符准备好可以读时,服务器就为相应的状态机执行转移,从描述符读和写回一个文本行
- I/O多路复用技术的优劣
优点:
- 比基于进程的设计给程序员更多的对程序行为的控制
- 每个逻辑流都能访问该进程全部的地址空间,使得在流之间共享数据变得很容易
缺点: - 编程复杂
- 不能充分利用多核处理器
基于线程的并发编程
-
主线程:
每个进程开始时都是单一线程,线程可以杀死任何对等进程或者等待它任意对等线程终止 -
对等线程
进程在某一时刻创建一个对等进程并发进行,当主线程执行一个慢系统调用或者它被系统的间隔计时器阻断时,控制会通过上下文切换到对等进程 -
线程和进程执行的区
- 线程上下文比较小,切换较快
- 不是按照严格的父子层次来组织
-
Posix线程
线程例程:封装线程的代码和本地数据,以通用指针作为输入,并返回一个通用指针
创建线程:pthread_creat函数
终止线程:
(1)顶层线程例程返回时,线程会隐式地终止
(2)通过调用pthread_exit函数,进程会显式地终止
已终止进程资源:调用prhread_join函数等待其他进程终止
分离线程:
(1)可结合线程:能被其他线程回收其资源和杀死
(2)可分离线程:不可以被其他线程回收其资源和杀死,存储器资源在它终止时由系统自动释放
(3)调用pthread_detach函数分离 -
基于线程的并发编程
(1)调用pthread_creat传递一个指向描述符的指针传递你哦啊舒服给对等线程
(2)隐式收回资源,使线程可分离,防止存储器资源泄露
多线程程序中的共享变量
-
全局变量
全局变量是定义在函数之外的变量。在运行时,虚拟存储器的读/写区域只包含每个全局变量的一个实例,任何线程都可以引用。 -
本地自动变量
本地自动变量就是定义在函数内部但是没有 static 属性的变量。在运行时,每个线程的栈都包含它自己的所有本地自动变量的实例。即使当多个线程执行同一个线程例程时也是如此。 -
本地静态变量
本地静态变量是定义在函数内部并有 static 属性的变量。和全局变量一样,虚拟存储器的读/写区域只包含在程序中声明的每个本地静态变量的一个实例。
用信号量同步线程
-
进度图
进度图是将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,原点对应于没有任何线程完成一条指令的初始状态。 -
线程循环代码的分解:
H:在循环头部的指令块
L:加载共享变量cnt到线程i中寄存器%eax的指令。
U:更新(增加)%eax的指令
S:将%eax的更新值存回到共享变量cnt的指令
T:循环尾部的指令块 -
进度图相关概念
临界区:对于线程i,操作共享变量cnt内容的指令L,U,S构成了一个关于共享变量cnt的临界区。
不安全区:两个临界区的交集形成的状态
安全轨迹线:绕开不安全区的轨迹线 -
信号量实现互斥的基本原理
- 两个或多个进程通过传递信号进行合作,可以迫使进程在某个位置暂时停止执行(阻塞等待),直到它收到一个可以“向前推进”的信号(被唤醒);
- 将实现信号灯作用的变量称为信号量,常定义为记录型变量s,其一个域为整型,另一个域为队列,其元素为等待该信号量的阻塞进程(FIFO)。
信号量定义:
type semaphore=record
count: integer;
queue: list of process
end;
var s:semaphore;
-
使用信号量来实现互斥基本思想
将每个共享变量(或者一组相关的共享变量)与一个信号量s(初始为1)联系起来,然后用P和V操作将相应的临界区包围起来。 -
其它并发问题
- 线程安全
一个线程是安全的,当且仅当被多个并发线程反复的调用时,它会一直产生正确的结果。
-
可重入性
当它们被多个线程调用时,不会引用任何共享数据。
(1)显式可重入的:
所有函数参数都是传值传递,没有指针,并且所有的数据引用都是本地的自动栈变量,没有引用静态或全剧变量。
(2)隐式可重入的:
调用线程小心的传递指向非共享数据的指针。 -
在线程化的程序中使用已存在的库函数
使用线程不安全函数的可重入版本,名字以_r为后缀结尾。 -
竞争
竞争发生的原因:
一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达它的控制流中的x点。也就是说,程序员假定线程会按照某种特殊的轨迹穿过执行状态空间,忘了一条准则规定:线程化的程序必须对任何可行的轨迹线都正确工作。
消除方法:
动态的为每个整数ID分配一个独立的块,并且传递给线程例程一个指向这个块的指针
- 死锁
一组线程被阻塞了,等待一个永远也不会为真的条件。
代码调试
-
condvar.c
-
count.c
-
countwithmutex.c
-
cp_t
-
createthread.c
-
hello_multi.c
-
hello_multi1.c
-
semphore.c
-
share.c
-
threadexit.c
本周代码托管截图
其他(感悟、思考等,可选)
本周学习还是比较有趣的,虽然代码还是不怎么能看懂,但是显然看一些自己学过的东西还是有熟悉感的,学起来比较轻松一点。
代码链接
http://git.oschina.net/jdy1453/linux-arrange
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 50/50 | 1/1 | 20/20 | |
第二周 | 50/100 | 1/2 | 30/50 | |
第三周 | 100/200 | 1/3 | 20/70 | |
第四周 | 0/200 | 0/3 | 20/90 | |
第五周 | 50/250 | 1/4 | 20/110 | |
第六周 | 50/300 | 1/5 | 20/130 | |
第七周 | 0/300 | 1/6 | 20/150 | |
第八周 | 0/300 | 1/7 | 20/170 | |
第九周 | 100/400 | 2/9 | 20/190 | |
第十周 | 0/400 | 1/10 | 20/210 | |
第十一周 | 600/1000 | 2/11 | 30/230 | |
第十二周 | 500/1500 | 2/13 | 30/260 | |
第十三周 | 1174/2674 | 2/13 | 30/260 |