2017-2018-1 20155319 《信息安全系统设计基础》第八周学习总结
教材学习内容总结
第11章 网络编程
- 网络应用随处可见。任何时候你浏览Web、发送Email或者弹出一个X window,你就正在使用一个网络应用程序。有趣的是,所有的网络应用都是基于相同的基本编程模型有着相似的整体逻辑结构,并且依赖相同的编程接口。
- 网络应用依赖于很多在系统研究中已经学习过的概念,例如,进程、信号、字节器映射以及动态存储分配,都扮演着重要的角色。还有一些新概念要掌握。我们需要理解基本的客户端-服务器编程模型,以及如何编写使用因特网提供的服务的客户端―服务器程序。最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web的服务器,能够为真实的Web,浏览器提供静态和动态的文本和图形内容。
第十二章 并发编程
三种基本的构造并发程序的方法:
1.进程
每个逻辑控制流是一个进程,由内核进行调度,进程有独立的虚拟地址空间
2.I/O多路复用
逻辑流被模型化为状态机,所有流共享同一个地址空间
3.线程
运行在单一进程上下文中的逻辑流,由内核进行调度,共享同一个虚拟地址空间
12.1 基于进程的并发编程
12.1.1 基于进程的并发服务器
•
使用SIGCHLD处理程序来回收僵死子进程的资源。
•
父进程必须关闭他们各自的connfd拷贝(已连接的描述符),避免存储器泄露。
•
因为套接字的文件表表项中的引用计数,直到父子进程的connfd都关闭了,到客户端的连接才会终止。
12.1.2 关于进程的优劣
1.优点:防止虚拟存储器被错误覆盖
2.缺点:开销高,共享状态信息才需要IPC机制
12.2 基于i/o多路复用的并发编程
使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。
int select(int n,fd_set *fdset,NULL,NULL,NULL);
返回已经准备好的描述符的非0的个数,若出错则为-1。
select函数处理类型为fd_set的集合,叫做描述符集合,看做一个大小为n位的向量:
bn-1,......,b1,b0
12.2.1 基于i/o多路复用的并发事件驱动服务器
•
I/O多路复用可以用作事件并发驱动程序的基础。
•
状态机:一组状态、输入事件、输出事件和转移。
•
自循环:同一输入和输出状态之间的转移。
注意:
init_pool:初始化客户端池
add_client:添加一个新的客户端到活动客户端池中
check_clients:回送来自每个准备好的已连接描述符的一个文本行
12.2.2 i/o多路复用技术的优劣
1.优点
•
相较基于进程的设计,给了程序员更多的对程序程序的控制
•
运行在单一进程上下文中,所以每个逻辑流都可以访问该进程的全部地址空间,共享数据容易实现
•
可以使用GDB调试
•
高效
2.缺点
•
编码复杂
•
不能充分利用多核处理器
12.3 基于线程的并发编程
每个线程都有自己的线程上下文,包括一个线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的线程共享该进程的整个虚拟地址空间。由于线程运行在单一进程中,因此共享这个进程虚拟地址空间的整个内容,包括它的代码、数据、堆、共享库和打开的文件。
1.线程执行模型
每个进程开始生命周期时都是单一线程(主线程),在某一时刻创建一个对等线程,从此开始并发地运行,最后,因为主线程执行一个慢速系统调用,或者被中断,控制就会通过上下文切换传递到对等线程。
2.Posix线程
Posix线程是C语言中处理线程的一个标准接口,允许程序创建、杀死和回收线程,与对等线程安全的共享数据。
线程的代码和本地数据被封装在一个线程例程中。
3.创建线程
线程通过调用pthread_create来创建其他线程。
int pthread_create(pthread_t *tid,pthread_attr_t *attr,func *f,void *arg);
成功则返回0,出错则为非零
当函数返回时,参数tid包含新创建的线程的ID,新线程可以通过调用pthread_self函数来获得自己的线程ID。
pthread_t pthread_self(void);返回调用者的线程ID。
4.终止线程
一个线程是通过以下方式之一来终止的。
•
当顶层的线程例程返回时,线程会隐式地终止。
•
通过调用pthread_exit函数,线程会显式地终止
void pthread_exit(void *thread_return);
5.回收已终止的线程资源
线程通过调用pthread_join函数等待其他线程终止。
int pthread_join(pthread_t tid,void **thread_return);
成功则返回0,出错则为非零
6.分离线程
在任何一个时间点上,线程是可结合或可分离的。一个可结合的线程能够被其他线程收回其资源和杀死,在被回收之前,它的存储器资源是没有被释放的。分离的线程则相反,资源在其终止时自动释放。
int pthread_deacth(pthread_t tid);
成功则返回0,出错则为非零
7.初始化线程
pthread_once允许初始化与线程例程相关的状态。
pthread_once_t once_control=PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t once_control,void (init_routine)(void));
总是返回0
12.4 多线程程序中的共享变量
一个变量是共享的。当且仅当多个线程引用这个变量的某个实例。
12.4.1 线程存储器模型
1.每个线程都有自己独立的线程上下文,包括一个唯一的整数线程ID,栈、栈指针、程序计数器、通用目的寄存器和条件码。
2.寄存器是从不共享的,而虚拟存储器总是共享的。
3.各自独立的线程栈被保存在虚拟地址空间的栈区域中,并且通常是被相应的线程独立地访问的。
12.4.2 将变量映射到存储器
•
全局变量:定义在函数之外的变量
•
本地自动变量:定义在函数内部但是没有static属性的变量。
•
本地静态变量:定义在函数内部并有static属性的变量。
12.4.3 共享变量
一个变量V是共享的,当且仅当它的一个实例被一个以上的线程引用。例如,示例程序中的变量cnt就是共享的,因为它只有一个运行时实例,并且这个实例被两个对等线程引用在另一方面,myid不是共享的,因为它的两个实例中每一个都只被一个线程引用。然而,认识到像msgs这样的本地自动变量也能被共享是很重要的。
12.5 用信号量同步线程
共享变量的同时引入了同步错误,即没有办法预测操作系统是否为线程选择一个正确的顺序。
○
12.5.1 进度图
进度图是将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,原点对应于没有任何线程完成一条指令的初始状态。
当n=2时,状态比较简单,是比较熟悉的二维坐标图,横纵坐标各代表一个线程,而转换被表示为有向边
转换规则:
•
合法的转换是向右或者向上,即某一个线程中的一条指令完成
•
两条指令不能在同一时刻完成,即不允许出现对角线
•
程序不能反向运行,即不能出现向下或向左
而一个程序的执行历史被模型化为状态空间中的一条轨迹线。
12.5.2 信号量
1.P(s):如果s是非零的,那么P将s减一,并且立即返回。如果s为零,那么就挂起这个线程,直到s变为非零。
2.V(s):将s加一,如果有任何线程阻塞在P操作等待s变为非零,那么V操作会重启线程中的一个,然后该线程将s减一,完成他的P操作。
信号量不变性:一个正确初始化了的信号量有一个负值。
信号量操作函数:
int sem_init(sem_t *sem,0,unsigned int value);//将信号量初始化为value
int sem_wait(sem_t *s);//P(s)
int sem_post(sem_t *s);//V(s)
代码托管
(statistics.sh脚本的运行结果截图)
上周考试错题总结
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 2/4 | 18/38 | |
第三周 | 500/1000 | 3/7 | 22/60 | |
第四周 | 300/1300 | 2/9 | 30/90 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:10小时
-
实际学习时间:8小时