zoukankan      html  css  js  c++  java
  • 20135203齐岳 信息安全系统设计基础第十三周学习总结

    20135203齐岳 信息安全系统设计基础第十三周学习总结

    学习计时:8/9共小时(计划/实际)

    • 读书:4/5

    • 代码:1/1

    • 作业:1/1

    • 博客:2/2

    第十二章 并发编程

    一、学习目标

    1. 掌握三种并发的方式:进程、线程、I/O多路复用
    2. 掌握线程控制及相关系统调用
    3. 掌握线程同步互斥及相关系统调用

    二、学习任务

    1. 阅读教材,完成课后练习(书中有参考答案)
    2. 考核:练习题把数据变换一下
    3. 加分题:课后作业最多两人一组,互相不能重复,1星题目每人最多加一分,2星题目每人最多加二分,3星题目每人最多加三分,4星题目每人最多加四分。

    三、学习过程

    如果逻辑控制流在时间上重叠,那么他们就是并发的。应用级并发在以下情况中发挥作用:

    • 访问慢速I/O设备。
    • 与人交互。
    • 通过推迟工作以降低延迟。
    • 服务多个网络客户端。
    • 在多核机器上进行并行计算。

    使用应用级并发的应用程序称为并发程序。现代操作系统提供了三种基本的构造并发程序的方法:

    • 进程。每个逻辑控制流都是一个进程,由内核来调度和维护。控制流使用显式的进程间通信(IPC)机制。
    • I/O多路复用。应用程序在一个进程的上下文中显式地调度他们自己的逻辑流。所有的流都共享同一个地址空间。
    • 线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。

    12.1 基于进程的并发编程

    基于进程的并发服务器

    • 使用SIGCHLD处理程序来回收僵死子进程的资源。
    • 父进程必须关闭他们各自的connfd拷贝(已连接的描述符),避免存储器泄露。
    • 因为套接字的文件表表项中的引用计数,直到父子进程的connfd都关闭了,到客户端的连接才会终止。

    关于进程的优劣

    • 在父子进程之间共享状态信息,通过共享文件表,但是不共享用户地址空间。
    • 使用显式的进程间通信(IPC)机制。但开销很高,往往比较慢。

    基于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
    

    对描述符集合的处理方法:

    • 分配他们
    • 将一个此种类型的变量赋值给另一个变量
    • 用FD_ZERO,FD_SET,FD_CLR和FD_ISSET宏指令来修改和检查他们。

    基于I/O多路复用的并发事件驱动服务器

    I/O多路复用可以用作事件并发驱动程序的基础。

    状态机:一组状态、输入事件、输出事件和转移。

    自循环:同一输入和输出状态之间的转移。

    I/O多路复用技术的优劣

    • 相比基于进程的设计给了程序员更多的对进程行为的控制,运行在单一进程上下文中,每个逻辑流都能访问全部的地址空间,在流之间共享数据很容易。
    • 编码复杂,随着并发粒度的减小,复杂性还会上升。粒度:每个逻辑流每个时间片执行的指令数量。

    12.3 基于线程的并发编程

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

    线程执行模型

    每个进程开始生命周期时都是单一线程(主线程),在某一时刻创建一个对等线程,从此开始并发地运行,最后,因为主线程执行一个慢速系统调用,或者被中断,控制就会通过上下文切换传递到对等线程。

    Posix线程

    Posix线程是C语言中处理线程的一个标准接口,允许程序创建、杀死和回收线程,与对等线程安全的共享数据。

    线程的代码和本地数据被封装在一个线程例程中,

    创建线程

    线程通过调用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。
    

    终止线程

    一个线程是通过以下方式之一来终止的。

    • 当顶层的线程例程返回时,线程会隐式地终止。

    • 通过调用pthread_exit函数,线程会显式地终止

      void pthread_exit(void *thread_return);

    回收已终止的线程资源

    线程通过调用pthread_join函数等待其他线程终止。

    int pthread_join(pthread_t tid,void **thread_return);
    											   成功则返回0,出错则为非零
    

    分离线程

    在任何一个时间点上,线程是可结合或可分离的。一个可结合的线程能够被其他线程收回其资源和杀死,在被回收之前,它的存储器资源是没有被释放的。分离的线程则相反,资源在其终止时自动释放。

    int pthread_deacth(pthread_t tid);
    											成功则返回0,出错则为非零
    

    初始化线程

    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 多线程程序中的共享变量

    一个变量是共享的。当且仅当多个线程引用这个变量的某个实例。

    线程存储器模型

    • 每个线程都有自己独立的线程上下文,包括一个唯一的整数线程ID,栈、栈指针、程序计数器、通用目的寄存器和条件码。
    • 寄存器是从不共享的,而虚拟存储器总是共享的。
    • 各自独立的线程栈被保存在虚拟地址空间的栈区域中,并且通常是被相应的线程独立地访问的。

    将变量映射到存储器

    • 全局变量:定义在函数之外的变量
    • 本地自动变量:定义在函数内部但是没有static属性的变量。
    • 本地静态变量:定义在函数内部并有static属性的变量。

    共享变量

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

    12.5用信号量同步线程

    共享变量的同时引入了同步错误,即没有办法预测操作系统是否为线程选择一个正确的顺序。

    进度图

    将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,将指令模型化为从一种状态到另一种状态的转换。

    信号量

    • P(s):如果s是非零的,那么P将s减一,并且立即返回。如果s为零,那么就挂起这个线程,直到s变为非零。
    • 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)
    

    使用信号量来实现互斥

    • 二元信号量(互斥锁):将每个共享变量与一个信号量s联系起来,然后用P(s)(加锁)和V(s)(解锁)操作将相应的临界区包围起来。

    • 禁止区:s<0,因为信号量的不变性,没有实际可行的轨迹线能够直接接触不安全区的部分

    12.6 使用线程来提高并行性

    并行程序的加速比通常定义为:

    其中,p为处理器核的数量,T为在p个核上的运行时间。

    12.7 其他并发问题

    线程安全

    定义四个(不相交的)线程不安全函数类:

    • 不保护共享变量的函数。
    • 保持跨越多个调用状态的函数。
    • 返回指向静态变量指针的函数。
    • 调用线程不安全函数的函数。

    竞争

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

    为消除竞争,我么可以动态地为每个整数ID分配一个独立的块,并且传递给线程例程一个指向这个块的指针。

    死锁

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

    • 程序员使用P和V操作顺序不当,以至于两个信号量的禁止区域重叠。
    • 重叠的禁止区域引起了一组称为死锁区域的状态。
    • 死锁是一个相当难的问题,因为它是不可预测的。

    互斥锁加锁顺序规则:如果对于程序中每对互斥锁(s,t),给所有的锁分配一个全序,每个线程按照这个顺序来请求锁,并且按照逆序来释放,这个程序就是无死锁的。

    参考资料

    1.《深入理解计算机系统》

    2.《计算机操作系统》

  • 相关阅读:
    soapui + groovy 接口自动化测试 第九章
    soapui + groovy 接口自动化测试 第八章
    soapui + groovy 接口自动化测试 第七章
    soapui + groovy 接口自动化测试 第六章
    soapui + groovy 接口自动化测试 第五章
    soapui + groovy 接口自动化测试 第四章
    soapui + groovy 接口自动化测试 第三章
    soapui + groovy 接口自动化测试 第二章
    Jekins
    ES6温习
  • 原文地址:https://www.cnblogs.com/July0207/p/5023779.html
Copyright © 2011-2022 走看看