zoukankan      html  css  js  c++  java
  • 20145322《信息安全系统设计基础》第13周学习总结

    学号 《信息安全系统设计基础》第13周学习总结

    教材学习内容总结

    第十二章 并发编程

    1、并发:逻辑控制流在时间上重叠

    2、并发程序:使用应用级并发的应用程序称为并发程序

    3、三种基本的构造并发程序的方法:

    (1)进程,用内核来调用和维护,有独立的虚拟地址空间,显式的进程间通信机制。

    (2)I/O多路复用,应用程序在一个进程的上下文中显式的调度控制流。逻辑流被模型化为状态机。

    (3)线程,运行在一个单一进程上下文中的逻辑流。由内核进行调度,共享同一个虚拟地址空间。

    12.1 基于进程的并发编程

    1、构造并发服务器的自然方法就是,在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。

    2、因为父子进程中的已连接描述符都指向同一个文件表表项,所以父进程关闭它的已连接描述符的拷贝是至关重要的,而且由此引起的存储器泄露将最终消耗尽可用的存储器,使系统崩溃。

    12.1.1 基于进程的并发服务器

    基于进程的并发echo服务器的重点内容

    (1)需要一个SIGCHLD处理程序,来回收僵死子进程的资源。

    (2)父子进程必须关闭各自的connfd拷贝。对父进程尤为重要,以避免存储器泄露。

    (3)套接字的文件表表项中的引用计数,直到父子进程的connfd都关闭了,到客户端的连接才会终止。

    12.1.2 关于进程的优劣

    注意:进程的模型:共享文件表,但不是共享用户地址空间。 优点:一个进程不可能不小心覆盖两一个进程的虚拟存储器。

    缺点:独立的地址空间使得进程共享状态信息变得更加困难。进程控制和IPC的开销很高。

    Unix IPC是指所有允许进程和同一台主机上其他进程进行通信的技术,包括管道、先进先出(FIFO)、系统V共享存储器,以及系统V信号量。

    12.2 基于I/O多路复用的并发编程

    1、echo服务器必须响应两个相互独立的I/O时间:

    (1)网络客户端发起连接请求

    (2)用户在键盘上键入命令行。

    2、I/O多路复用技术的基本思路:使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。

    3、将描述符集合看成是n位位向量:b(n-1),……b1,b0

    每个位bk对应于描述符k,当期仅当bk=1,描述符k才表明是描述符集合的一个元素。可以做以下三件事:

    (1)分配它们;

    (2)将一个此种类型的变量赋值给另一个变量;

    (3)用FDZERO、FDSET、FDCLR和FDISSET宏指令来修改和检查它们。

    4、echo函数:将来自科幻段的每一行回送回去,直到客户端关闭这个链接。

    12.2.1 基于I/O多路复用的并发时间驱动服务器

    状态机就是一组状态、输入事件和转移,转移就是将状态和输入时间映射到状态,自循环是同一输入和输出状态之间的转移。

    12.2.2 I/O多路复用技术的优势

    事件驱动器的设计优点:

    (1)比基于进程的设计给了程序员更多的对程序行为的控制

    (2)运行在单一进程上下文中,因此,每个逻辑流都能访问该进程的全部地址空间,使得流之间共享数据变得很容易。

    (3)不需要进程上下文切换来调度新的流。

    缺点:

    (1)编码复杂

    (2)不能充分利用多核处理器

    粒度:每个逻辑流每个时间片执行的指令数量。并发粒度就是读一个完整的文本行所需要的指令数量。

    12.3 基于线程的并发编程

    1、线程:运行子啊进程上下文中的逻辑流。

    2、线程有自己的线程上下文,包括一个唯一的整数线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有运行在一个进程里的线程共享该进程的整个虚拟地址空间。

    12.3.1 线程执行模型

    1、主线程:每个进程开始生命周期时都是单一线程。

    对等线程:某一时刻,主线程创建的对等线程

    2、线程与进程的不同:

    (1)线程的上下文切换要比进程的上下文切换快得多;

    (2)和一个进程相关的线程组成一个对等池,独立于其他线程创建的线程。

    (3)主线程和其他线程的区别仅在于它总是进程中第一个运行的线程。

    3、对等池的影响

    (1)一个线程可以杀死它的任何对等线程;

    (2)等待它的任意对等线程终止;

    (3)每个对等线程都能读写相同的共享资源。

    12.3.2 Posix线程

    线程例程:线程的代码和本地数据被封装在一个线程例程中。每一个线程例程都以一个通用指针作为输入,并返回一个通用指针。

    12.3.3 创建线程

    pthread create函数创建一个新的线程,并带着一个输入变量arg,在新线程的上下文中运行线程例程f。新线程可以通过调用pthread _self函数来获得自己的线程ID。

    12.3.4 终止线程

    一个线程的终止方式:

    (1)当顶层的线程例程返回时,线程会隐式的终止;

    (2)通过调用pthread _exit函数,线程会显示地终止。如果主线程调用pthread _exit,它会等待所有其他对等线程终止,然后再终止主线程和整个进程。

    12.3.5 回收已终止线程的资源

    pthread _join函数会阻塞,直到线程tid终止,回收已终止线程占用的所有存储器资源。pthread _join函数只能等待一个指定的线程终止。

    12.3.6 分离线程

    1、在任何一个时间点上,线程是可结合的或者是分离的。一个可结合的线程能够被其他线程收回其资源和杀死;一个可分离的线程是不能被其他线程回收或杀死的。它的存储器资源在它终止时有系统自动释放。

    2、默认情况下,线程被创建成可结合的,为了避免存储器漏洞,每个可集合的线程都应该要么被其他进程显式的回收,要么通过调用pthread _detach函数被分离。

    12.3.7 初始化线程

    pthread _once函数允许初始化与线程例程相关的状态。

    once _control变量是一个全局或者静态变量,总是被初始化为PTHREAD _ONCE _INIT.

    12.3.8 一个基于线程的并发服务器

    对等线程的赋值语句和主线程的accept语句之间引入了竞争。

    12.4 多线程程序中的变量共享

    12.4.1 线程存储器模型

    1、每个线程和其他线程一起共享进程上下文的剩余部分。包括整个用户虚拟地址空间,是由只读文本、读/写数据、堆以及所有的共享库代码和数据区域组成的。线程也共享同样的打开文件的集合。

    2、任何线程都可以访问共享虚拟存储器的任意位置。寄存器是从不共享的,而虚拟存储器总是共享的。

    12.4.2 将变量映射到存储器

    1、全局变量:虚拟存储器的读/写区域只会包含每个全局变量的一个实例。

    2、本地自动变量:定义在函数内部但没有static属性的变量。

    3、本地静态变量:定义在函数内部并有static属性的变量。

    12.4.3 共享变量

    变量v是共享的,当且仅当它的一个实例被一个以上的线程引用。

    12.5 用信号量同步线程

    1、共享变量引入了同步错误的可能性。

    2、线程i的循环代码分解为五部分:

    Hi:在循环头部的指令块

    Li:加载共享变量cnt到寄存器%eax的指令,%eax表示线程i中的寄存器%eax的值

    Ui:更新(增加)%eax的指令

    Si:将%eaxi的更新值存回到共享变量cnt的指令

    Ti:循环尾部的指令块。

    12.5.1 进度图

    1、进度图将指令执行模式化为从一种状态到另一种状态的转换。转换被表示为一条从一点到相邻点的有向边。合法的转换是向右或者向上。

    2、临界区:对于线程i,操作共享变量cnt内容的指令构成了一个临界区。

    3、互斥的访问:确保每个线程在执行它的临界区中的指令时,拥有对共享变量的互斥的访问。

    4、安全轨迹线:绕开不安全区的轨迹线

    不安全轨迹线:接触到任何不安全区的轨迹线就叫做不安全轨迹线

    5、任何安全轨迹线都能正确的更新共享计数器。

    12.5.2 信号量

    1、当有多个线程在等待同一个信号量时,你不能预测V操作要重启哪一个线程。

    2、信号量不变性:一个正在运行的程序绝不能进入这样一种状态,也就是一个正确初始化了的信号量有一个负值。

    12.5.3 使用信号量来实现互斥

    1、二元信号量:将每个共享变量与一个信号量s联系起来,然后用P(S)和V(s)操作将这种临界区包围起来,这种方式来保护共享变量的信号量。

    2、互斥锁:以提供互斥为目的的二元信号量

    加锁:一个互斥锁上执行P操作称为对互斥锁加锁,执行V操作称为对互斥锁解锁。对一个互斥锁加了锁但还没有解锁的线程称为占用了这个互斥锁。

    计数信号量:一个呗用作一组可用资源的计数器的信号量

    12.5.4 利用信号量来调度共享资源

    1、信号量的作用:

    (1)提供互斥

    (2)调度对共享资源的访问

    2、生产者—消费者问题:生产者产生项目并把他们插入到一个有限的缓冲区中,消费者从缓冲区中取出这些项目,然后消费它们。

    3、读者—写者问题:

    (1)读者优先,要求不让读者等待,除非已经把使用对象的权限赋予了一个写者。

    (2)写者优先,要求一旦一个写者准备好可以写,它就会尽可能地完成它的写操作。

    (3)饥饿就是一个线程无限期地阻塞,无法进展。

    12.6 使用线程提高并行性

    写顺序程序只有一条逻辑流,写并发程序有多条并发流,并行程序是一个运行在多个处理器上的并发程序。并行程序的集合是并发程序集合的真子集。

    12.7 其他并发问题

    12.7.1 线程安全

    1、线程安全:当且仅当被多个并发线程反复地调用时,它会一直产生正确的结果。

    线程不安全:如果一个函数不是线程安全的,就是线程不安全的。

    2、线程不安全的类:

    (1)不保护共享变量的函数

    (2)保持跨越多个调用的状态的函数。

    (3)返回指向静态变量的指针的函数。解决办法:重写函数和加锁拷贝。

    (4)调用线程不安全函数的函数。

    12.7.2 可重入性

    1、可重入函数:当它们被多个线程调用时,不会引用任何共享数据。可重入函数是线程安全函数的一个真子集 。

    2、关键思想是我们用一个调用者传递进来的指针取代了静态的next变量。

    3、显式可重入:没有指针,没有引用静态或全局变量

    隐式可重入:允许它们传递指针

    4、可重入性即使调用者也是被调用者的属性,并不只是被调用者单独的属性。

    12.7.4 竞争

    1、竞争:当一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达它的控制流中的x点时,就会发生竞争。

    2、线程化的程序必须对任何可行的轨迹线都正确工作。

    12.7.5 死锁

    1、死锁:一组线程被阻塞了,等待一个永远也不会为真的条件。

    2、程序员使用P和V操作不当,以至于两个信号量的禁止区域重叠。

    3、重叠的禁止区域引起了一组称为死锁区域的状态。

    4、死锁是不可预测的。

    代码运行:

    首次编译时按照之前的方法编译,报错。根据错误提示,发现pthread库不是linux系统默认的库,因此pthread_creat创建线程时,在编译中要加上-lpthread参数。修正后顺利编译。

    countwithmutex.c

    PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁。先创建tidA线程后运行doit函数,利用互斥锁锁定资源,进行计数,执行完毕后解锁。后创建tidB,与tidA交替执行。由于定义的NLOOP值为5000,所以程序最后的输出值为10000.程序的最后还需要分别回收tidA和tidB的资源。

    count.c

    差别在于这个代码countwithmutex.c与代码doit函数的for循环中没有引入互斥锁,只进行了单纯的计数,创建两个线程共享同一变量都实现加一操作。

    condvar.c

    主函数中用srand(time(NULL))设置当前的时间值为种子,在后面的producer和consumer函数中调用rand()函数产生随机数。

    createthread.c

    程序主要演示了创建线程函数pthread_create()函数的使用,用来打印进程和线程的ID

    semphore.c

    sem_init函数sem_init(sem_t *sem, int pshared, umsigned int value);

    函数初始化一个定位在sem的匿名信号量;pshared参数为0指明信号量是由进程内线程共享,若为非0值则信号量在进程之间共享;value参数指定信号量的初始值。

    sem_init()成功时返回0;错误时返回-1,并把errno设置为合适的值。

    share.c

    threadexit.c

    hello_multi.c

    程序中的print_msg()函数中:在printf后的fflush(stdout);说明要立刻将要输出的内容输出,每输出一次停1秒,并循环5次。

    hello_single.c

    根据代码,先单独执行print_msg("hello");——输出5个hello,后输出5个带换行的world

    incprint.c

    由于定义中NUM=5,所以输出的count为1——5

    twordcount.c

    代码上传截图及链接:

    http://git.oschina.net/HZW20145322/CSAPP2E/tree/master/src/12conc?dir=1&filepath=src%2F12conc&oid=cfae35e3b80fa3d56e16902f674f562f5a96d655&sha=2ca3411af442ede6bf931e8da5ce0e79494a80e2

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 100/100 1/1 13/20
    第二周 100/200 1/2 15/38
    第三周 100/300 1/3 20/60
    第五周 70/370 2/5 30/90
    第六周 200/570 2/7 40/90
    第七周 0/570 2/9 30/120
    第八周 0/570 2/9 50/170
    第九周 78/570 2/11 40/170
    第十周 300/570 1/12 50/170
    第十一周 488/1058 1/13 55/225
    第十二周 10/1058 1/13 53/278
    第十三周 512/1058 1/13 51/329
  • 相关阅读:
    Linux 学习 step by step (1)
    ubuntu server nginx 安装与配置
    ubuntu server samba服务器配置
    iOS app集成支付宝支付流程及后台php订单签名处理
    mac 连接windows 共享内容
    linux 文件查找,which,whereis,locate,find
    ubuntu server vsftpd 虚拟用户及目录
    ubuntu server 安装 mantis bug tracker 中文配置
    ubuntu server vsftpd 匿名用户上传下载及目录设置
    linux 用户管理,用户权限管理,用户组管理
  • 原文地址:https://www.cnblogs.com/HZW20145322/p/6160954.html
Copyright © 2011-2022 走看看