20145236《信息安全系统设计基础》第13周学习总结
第11章 网络编程
网络应用随处可见。任何时候你浏览Web、发送邮件,你就在使用一个网络应用程序。有趣的是,所有网络应用都是基于相同的基本编程模型,有着相似的整体逻辑结构,并且依赖相同的编程接口。
11.1客户端-服务器编程模型
- 每个网络应用都是基于客户端-服务器模型的。采用这个模型,一个应用是由一个服务器户端提供某种服务。服务器管理某种资源,并且通过操作这种资源来为它的客户端提供某种服务。—个FTP服务器就管理了一组磁盘文件,它为客户端进行它会为客户端进行存储和检索。相似地一个电子邮件服务器管理了一些文件,它为客户端进行读和更新。
- 一个客户端-服务器事务由四步组成:
(1) 当一个客户端需要服务时,它向服务器发送一个请求,发起一个事务。
(2) 服务器收到请求后,解释它,并以适当的方式操作它的资源。
(3) 服务器给客户端发送一响应,并等待下一个请求。
(4) 客户端收到响应并处理它。例如,当Web浏览器收到来自服务器的一页后,它就在屏幕上显示此页。
11.2 网络
客户端和服务器通常运行在不同的主机上,并且通过计算机网络的硬件和软件资源来通信。
对于一个主机而言,网络只是又一种I/O设备,作为数据源和数据接收方。一个插到I/O总线扩展槽的适配器提供了到网络的物理接口。从网络上接收到的数据从适配器经过I/O和存储器总线拷贝到存储器,典型地是通过DMA传送。
- 一个以太网段,包括电缆(通常是双绞线)和一个叫做集线器的小盒子。每根电缆都有相同的最大位带宽。集线器不加分辩地将一个端口上收到的每个位复制到其他所有的端口上。因此,每台主机都能看到每个位。
- 每个以太网适配器都有—个全球唯一的48位地址,它存储在这个适配器的非易失性存储器上。一台主机可以发送一段位,称为帧。每个主机适配器都能看到这个帧,但是只有目的主机实际读取它。
- 桥接以太网是使用一些电缆和叫做网桥的小盒子,将多个以太网段连接起来形成的较大的局域网。电缆的带宽可以是不同的。
- 网桥比集线器更充分地利用了电缆带宽。
- 在层次的更高级别中,多个不兼容的局域网可以通过叫做路由器的计算机连接起来,组成一个Internet。
- 解决办法是一层运行在每台主机和路由器上的协议软件,它消除了不同网络之间的差异。
- 协议必须提供两种基本能力:
- 命名机制。不同的局域网技术有不同的和不兼容的方式来为主机分配地址。
- 传送机制。在电缆上编码位和将这些位封装成帧方面,不同的联网技术有不同和不兼容的方式。一个包是由包头和有效载荷组成,其中包头包括包的大小以及源主机和目的主机的地址,有效载荷包括从源主机发出的数据位。
- 互联网思想的精髓,封装是关键。
11.3 全球IP因特网
- 全球IP因特网是最著名和最成功的互联网络实现。每台因特网主机都运行实现TCP/TP协议的软件,几乎每个现代计算机系统都支持这个协议。因特网的客户端和服务器混合使用套接字接口函数和Unix I/O函数来进行通信。套接字函数典型地是作为会陷入内核的系统调用来实现的,并调用各种内核模式的TCP/IP函数。
- 把因特网看做一个世界范围的主机集合,满足以下特性:
- 主机集合被映射为一组32位的IP地址。
- 这组IP地址被映射为一组称为因特网域名的标识符。
- 因特网主机上的进程能够通过连接和任何其他因特网主机上的进程通信。
11.4套接字接口
listen函数将sockfd从一个主动套接字转化为一个监听套接字。该套接字可以接受来自客户端的连接请求。backlog参数暗示了内核在开始拒绝连接请求之前,该放入队列中等待的未完成连接请求的数量。
11.5Web服务器
Web客户端和服务器之间的交互用的是一个基于文本的应用级协议,叫做HTTP。HTTP是一个简单的协议。一个web客户端(即浏览器)打开一个到服务器的因特网连接。浏览器读取这些内容,并请求某些内容。服务器响应所请求的内容,然后关闭连接。浏览器读取并把它显示在屏幕内主要的区别是Web内容可以用HTML来编写。一个HTML程序(页)包含指令(标记)它们告诉浏览器如何显示这页中的各种文本和图形对象。
第12章 并发编程
到目前为止,我们主要将并发看做是一种操作系统内核用来运行多个应用程序的机制。但是,并发不仅仅局限于内核。它也可以在应用程序中扮演重要角色。例如,我们已经看到Unix信号处理程序如何允许应用响应异步事件,例如用户键入。或者程序访问虚拟存储器的个未定义的区域.应用级并发在其他情况下也是很有用的。
12.1基于进程的并发编程
在接受连接请求之后,服务器派生一个子进程,这个子进程获得服务器描述符表的完整拷贝。子进程关闭它的拷贝中的监听描述符3,而父进程关闭它的已连接描述符4的拷贝,因为不再需要这些描述符了。这就得到了图中的状态,其中子进程正忙于为客户端提供服务。因为父子进程中的已连接描述符都指向同一个文件表表项,所以父进程关闭它的已连接描述符的拷贝是至关重要的。否则,将永远不会释放已连接描述符4的文件表条目,而且由此引起的存储器泄漏将最终消耗尽可用的存储器,使系统崩溃。现在假设在父进程为客户端1创建了子进程之后,它接受一个新的客户端2的连接请求,并返回一个新的已连接描述符(比如描述符5)如图所示。然后,父进程又派生另一个子进程,这个子进程用已连接描述符5为它的客户端提供服务,如图所示。此时,父进程正在等待下一个连接请求,而两个子进程正在形地为它们各自的客户端提供服务。
12.2基于I/O多路复用的并发编程
一个服务器,它有两个I/O事件:1)网络客户端发起连接请求,2)用户在键盘上键入命令行。我们先等待那个事件呢?没有那个选择是理想的。如果accept中等待连接,那么无法相应输入命令。如果在read中等待一个输入命令,我们就不能响应任何连接请求(这个前提是一个进程)。
针对这种困境的一个解决办法就是I/O多路复用技术。基本思想是:使用select函数,要求内核挂起进程,只有在一个或者多个I/O事件发生后,才将控制返给应用程序。如图所示:横向的方格可以看作是一个n位的描述符向量。现在,我们定义第0位描述是“标准输入”,第3位描述符是“监听描述符”。
12.3基于线程的并发编程
每个线程都有自己的线程上下文,包括一个线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的线程共享该进程的整个虚拟地址空间。由于线程运行在单一进程中,因此共享这个进程虚拟地址空间的整个内容,包括它的代码、数据、堆、共享库和打开的文件。
一、线程执行模型
- 主线程
- 对等线程
- 主线程切换到对等线程
二、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)
四、终止线程
1.终止线程的方式:隐式终止、显示终止
2.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.4多线程程序中的共享变量
全局变量和static变量是存储在数据段,所以多线程共享之!由于线程的栈是独立的,所有线程中的自动变量是独立的。即使多个线程运行同一段代码总的自动变量,那么他们的值也是根据线程的不同而不同。
12.5用信号量同步线程
信号量通常称之为PV操作,虽然它的思想是将临界代码保护起来,达到互斥效果。这里面操作系统使用到了线程挂起。
将线程i的循环代码分解成五个部分:
12.6使用线程提高并行性
到目前为止,在对并发的研究中,我们都假设并发线程是在单处许多现代机器具有多核处理器。并发程序通常在这样的机器上运理器系统上执行的。然而,在多个核上并行地调度这些并发线程,而不是在单个核顺序地调度,在像繁忙的Web服务器、数据库服务器和大型科学计算代码这样的应用中利用这种并行性是至关重要的。
代码实践部分
condvar.c
运行结果:
mutex
用于保护资源,wait
函数用于等待信号,signal
函数用于通知信号,wait
函数中有一次对`mutex的释放和重新获取操作,因此生产者和消费者并不会出现死锁。
share.c
运行结果:
获得线程的终止状态,thr_fn 1
,thr_fn 2
和thr_fn 3
三个函数对应终止线程的三种方法,即从线程函数return
,调用pthread_exit
终止自己和调用pthread_cancel
终止同一进程中的另一个线程。
countwithmutex.c
运行结果:
引入互斥锁(Mutex),获得锁的线程可以完成”读-修改-写”的操作,然后释放锁给其它线程,没有获得锁的线程只能等待而不能访问共享数据。
semphore.c
运行结果:
semaphore
表示信号量,semaphore
变量的类型为sem_t,sem_init()
初始化一个semaphore
变量,value
参数表示可用资源 的数量,pshared
参数为0表示信号量用于同一进程的线程间同步。
count.c
运行结果:
这是一个不加锁的创建两个线程共享同一变量都实现加一操作的程序,在这个程序中虽然每个线程都给count加了5000,但由于结果的互相覆盖,最终输出值不是10000,而是5000。
代码托管
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 200/400 | 2/4 | 18/38 | |
第三周 | 100/500 | 1/5 | 10/48 | |
第四周 | 250/750 | 1/6 | 10/58 | |
第五周 | 100/850 | 1/7 | 10/68 | |
第六周 | 100/950 | 1/8 | 12/80 | |
第七周 | 200/1150 | 1/9 | 12/92 | |
第八周 | 124/1274 | 2/11 | 10/102 | |
第九周 | 205/1479 | 2/13 | 5/107 | |
第十周 | 333/1712 | 2/15 | 10/117 | |
第十一周 | 758/2470 | 3/18 | 12/129 | |
第十二周 | 25/2495 | 3/21 | 10/139 | |
第十三周 | 956/3451 | 1/22 | 10/149 |