20145314郑凯杰《信息安全系统设计基础》第13周学习总结
明确教材学习目标
学习目标
- 掌握三种并发的方式:进程、线程、I/O多路复用
- 掌握线程控制及相关系统调用
- 掌握线程同步互斥及相关系统调用
教材学习内容总结
网络编程
-
客户端-服务器编程模型
- 每个网络应用都是基于客户端-服务器模型。
- 一个应用是由一个服务器进程和一个或者多个客户端进程组成。
- 服务器管理某种资源,并通过操作资源来为客户端提供某种服务。
- 基本操作是事务。
-
四个步骤:
- 当客户端需要服务时,向服务器发送请求,发起一个事务。
- 服务器收到请求后,解释它,并以适当的方式操作它的资源。
- 服务器给客户端发送一个响应,并等待下一个请求。
- 客户端收到响应并处理它。
-
每个以太网适配器都有一个全球唯一的48位地址。
-
每台因特网主机都运行实现TCP/IP协议的软件。
-
因特网的客户端和服务器混合使用套接字接口函数和Unix I/O函数来进行通信。
-
套接字接口
sockaddr_in
的16字节结构
- sin_family成员是AF_INET
- sin_port成员是一个16位的端口号
- sin_addr成员是一个32位的IP地址。
- IP地址和端口号总时以网络字节顺序(大端法)存放的。
- _in是互联网络的缩写,不是输入input的缩写。
-
Web服务器
Web服务器使用HTTP协议和它们的客户端(浏览器等)彼此通信。
并发编程
逻辑控制流在时间上重叠,那么它们就是并发的。并发出现在计算机系统的许多不同层面上。
使用应用级并发的应用程序称为并发程序。现代操作系统提供了三种基本的构造并发程序的方法:
- 进程。每个逻辑控制流都是一个进程,由内核来调度和维护。因为进程 有独立的虚拟地址空间,想要和其他流通信,控制流必须使用某种显式的进程间通信 (interprocess communication, IPC) 机制。
- I/O 多路复用。在这种形式的并发编程中,应用程序在一个进程的上下文中显式地调度它们自己的逻辑流。逻辑流被模型化为状态机,数据到达文件描述符后,主程序显式地从一个状态转换到另一个状态。因为程序是一个单独的进程,所以所有的流都共享同一个地址空间。
- 线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。是其他两种方式的混合体,像进程流一样由内核进行调度,而像I/O 多路复用流一样共享同一个虚拟地址空间。
-
12.1基于进程的并发编程
- 构造并发程序最简单的方法就是用进程。
- 一个构造并发服务器的自然方法就是,在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。
-
第一步:服务器接受客户端的连接请求;
-
第二步:服务器派生一个子进程为这个客户端服务;
-
第三步:服务器接受另一个连接请求;
-
基于进程的并发服务器
-
第四步:服务器派生另一个子进程为新的客户端服务。基于进程的并发 echo 服务器.父进程派生一个子进程来处理每个新的连接请求:
Posix线程
Posix线程是C程序中处理线程的一个标准接口。
万能函数:
void func(void parameter)
typedef void (uf)(void para)
创建线程
1.创建线程:
pthread_create函数
#include <pthread.h>
typedef void *(func)(void *);
int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg);
功能:创建一个新的线程,带着一个输入变量arg,在新线程的上下文运行线程例程f。
2.查看线程ID
pthread_self函数
#include <pthread.h>
pthread_t pthread_self(void);
功能:返回调用者的线程ID(TID)
3、终止线程
-
终止线程的方式:隐式终止、显式终止
-
pthread_exit函数
#include <pthread.h> void pthread_exit(void *thread_return);
3.pthread_cancle函数
#include <pthread.h>
void pthread_cancle(pthread_t tid);
五、回收已终止线程的资源
pthread_join函数:
#include <pthread.h>
int pthread_join(pthread_t tid,void **thrad_return);
六、分离线程
pthread_detach函数
#include <pthread.h>
void pthread_detach(pthread_t tid);
功能:分离可结合线程tid。
七、初始化线程
pthread_once函数
#include <pthread.h>
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t once_control, void (init_routine)(void));
第四节 多线程程序中的共享变量
- 线程存储器模型
- 将变量映射到存储器
- 共享变量
12.5 用信号量同步线程
- 进度图
进度图转换规则:
- 合法的转换是向右或者向上,即某一个线程中的一条指令完成
- 两条指令不能在同一时刻完成,即不允许出现对角线
- 程序不能反向运行,即不能出现向下或向左
- 而一个程序的执行历史被模型化为状态空间中的一条轨迹线。
线程循环代码的分解:
- H:在循环头部的指令块
- L:加载共享变量cnt到线程i中寄存器%eax的指令。
- U:更新(增加)%eax的指令
- S:将%eax的更新值存回到共享变量cnt的指令
- T:循环尾部的指令块
临界区使用原则:有空让进、无空等待、多中择一、让权等待
- 信号量
信号量定义:
type semaphore=record
count: integer;
queue: list of process
end;
var s:semaphore;
- 使用信号量来实现互斥
第七节 并发问题
一、线程安全性
四个不相交的线程不安全函数类以及应对措施:
不保护共享变量的函数——用P和V这样的同步操作
保持跨越多个调用的状态的函数——重写
返回指向静态变量的指针的函数——①重写;②使用加锁-拷贝技术。
二、可重入性
1.显式可重入的
所有函数参数都是传值传递,没有指针,并且所有的数据引用都是本地的自动栈变量,没有引用静态或全剧变量。
2.隐式可重入的
调用线程小心的传递指向非共享数据的指针。
三、竞争
竞争发生的原因:
一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达它的控制流中的x点。也就是说,程序员假定线程会按照某种特殊的轨迹穿过执行状态空间,忘了一条准则规定:线程化的程序必须对任何可行的轨迹线都正确工作。
消除方法:
动态的为每个整数ID分配一个独立的块,并且传递给线程例程一个指向这个块的指针
四、死锁
一组线程被阻塞了,等待一个永远也不会为真的条件。
代码上传
咯咯:
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 2000行 | 30篇 | 400小时 | |
第九周 | 832/950 | 17/17 | 140/140 | |
第十周 | 968/1050 | 18/18 | 160/160 | |
第十一周 | 1100/1200 | 19/19 | 180/180 | |
第十二周 | 1197/1300 | 21/21 | 200/200 |
| 第十三周 | 1300/1400 | 22/22 | 210/210 | |