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
  • 相关阅读:
    服务器新环境搭建笔记
    mysql存储过程中的异常处理
    Maven为不同环境配置打包
    redis 绑定任意ip
    Redis 【Hash】 一句话说明
    Redis 【string】 一句话说明
    Redis 【keys】 一句话说明
    Redis命令参考【EXPIRE】
    Apache Maven 入门篇 ( 下 )
    Apache Maven 入门篇 ( 上 )
  • 原文地址:https://www.cnblogs.com/HZW20145322/p/6160954.html
Copyright © 2011-2022 走看看