系统级程序设计笔记(unit7——程序间的交互与通信)

这个专题的所有学习笔记来自于对武汉大学计算机学院软件工程专业大三上学期的专业必修课《系统级程序设计》的学习(教材为深入理解计算机系统CSAPP),涉及的编程语言全部为C语言和C++语言。

该博客为第7单元的学习笔记,这一单元的主要内容是程序间的交互与通信,部分内容来自《深入理解计算机系统》的第12章的内容。对应ssd6课程的lecture8。


程序间的交互与并发编程

(1)要点:线程是用于在程序中引入并发性的一种流行且有用的工具。
线程通常比进程更高效,线程间共享数据比进程间要容易得多。
然而,易于共享带来了难以诊断的同步错误的可能性。
编写线程程序的程序员必须小心地使用适当的同步机制保护共享数据。
线程调用的函数必须是线程安全的。必须避免竞争和僵局的出现。
Sync/Async 同步/异步
Multiple process/ Multiple thread 多进程/多线程
Concurrent programming 并发程序设计
Async I/O / Blocking I/O / Non-Blocking I/O 异步I/O和I/O阻塞/非阻塞I/O
Callback / Event 回调/事件

(2)应用级并发:
①访问慢速IO设备:当应用等待来自慢速IO设备的数据到达时,内核会运行其他进程使CPU保持繁忙。每个应用都可以按照类似的方式通过交替执行IO请求和其他有用的工作来利用并发。
②与人交互:和计算机交互的人要求计算机有同时执行多个任务的能力。
③通过推迟工作来降低延迟:有时应用程序能够通过推迟其他操作和并发地执行它们,利用并发来降低某些操作的延迟。
④服务多个网络客户端:创建一个并发服务器,为每个客户端创建一个单独的逻辑流,有效避免慢速客户端独占服务器。
⑤在多核机器上进行并行计算:现代系统配备多核处理器,多核处理器中包含有多个CPU。并行执行比交错执行更快。
使用应用级并发的程序称为并发程序(concurrent program)

(3)三种基本的构造并发程序的方法:
①进程。每个逻辑控制流都是一个进程,由内核调度和维护。因为进程有独立的虚拟地址空间,想要和其他流通信,控制流必须使用某种显式的进程间通信(interprocess communication,IPC)机制
②I/O多路复用。应用程序在一个进程的上下文中显式地调度它们自己的逻辑流。所有的流共享同一个地址空间
③线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。线程像进程流一样由内核进行调度,而像IO多路复用流一样共享同一个地址空间。

(4)基于进程的并发编程
构造并发程序最简单的办法就是用进程,使用fork、exec和waitpad这些大家都很熟悉的函数。例如,一个构造并发服务器的自然方法就是,在父进程接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。

fork()——创建一个新的进程。当一个进程调用它,完成后就出现两个几乎一模一样的进程,其中由fork()创建的新进程被称为子进程,而原来的进程称为父进程.子进程是父进程的一个拷贝,即子进程从父进程得到了数据段和堆栈的拷贝,这些需要分配新的内存;而对于只读的代码段,通常使用共享内存方式进行访问.
子进程和父进程有区别的地方:子进程有不同于父进程的进程标识符。子进程有自己的父进程描述符的副本。

exex()——exec命令用于调用并执行指令的命令。exec命令通常用在shell脚本程序中,可以调用其他的命令。如果在当前终端中使用命令,则当指定的命令执行完毕后会立即退出终端。

builtin命令用于执行指定的shell内部命令,并返回内部命令的返回值。builtin命令在使用时,将不能够再使用Linux中的外部命令。当系统中定义了与shell内部命令相同的函数时,使用builtin显式地执行shell内部命令,从而忽略定义的shell函数。

waitpad()——如果在调用waitpid()函数时,当指定等待的子进程已经停止运行或结束了,则waitpid()会立即返回;但是如果子进程还没有停止运行或结束,则调用waitpid()函数的父进程则会被阻塞,暂停运行。

(5)进程间通信(IPC,Inter-Process Communication)指至少两个进程或线程间传送数据或信号的一些技术或方法
假设我们有两个客户端和一个服务器。服务器正在监听一个监听描述符(比如描述符3)上的连接请求,现在假设服务器接受了客户端1的连接请求并返回一个已连接描述符(比如描述符4),如下图所示,是服务器接受客户端的连接请求:
这里写图片描述
在接受连接请求后,服务器派生一个子进程,这个子进程获得服务器描述符表的完整副本。子进程关闭它的副本中的监听描述符3,而父进程关闭它的已连接描述符4的副本,因为不再需要这些描述符了。得到下图的状态,服务器派生一个子进程为这个客户端服务,其中子进程正忙于为客户端提供服务。
这里写图片描述
因为父、子进程中的已连接描述符都指向同一个文件表项,所以父进程关闭它的已连接描述符的副本是至关重要的。否则,将永不会释放已连接描述符4的文件表条目,而且由此引起的内存泄漏将最终消耗光可用的内存,使系统崩溃。
现在,假设在父进程为客户端1创建了子进程之后,它接受一个新的客户端2的连接请求,并返回一个新的已连接描述符(比如描述符5),如下图所示:
这里写图片描述
然后父进程又派生另一个子进程,这个子进程用已连接描述符5为它的客户端提供服务。如下图所示:
这里写图片描述
此时,父进程正在等待下一个连接请求,而两个子进程正在并发地为它们各自的客户端提供服务。

Thread(线程)

(1)线程就是运行在进程上下文中的逻辑流,线程由内核自动调度,每个线程都有自己的线程上下文,包括一个唯一的整数线程ID(TID,Thread ID)、栈、栈指针、程序计数器、通用目的寄存器和条件码。(2017-2018年期末考试考到了这里,填空题)
这里写图片描述
所有的运行在一个进程里的线程共享该进程的整个虚拟空间。
多个线程运行在单一进程的上下文中,因此共享这个进程虚拟地址空间的所有内容,包括它的代码、数据、堆、共享库和打开的文件。
这里写图片描述

(2)多线程程序中的共享变量
①线程的基础内存模型
一组并发线程运行在一个进程的上下文中。每个线程都有自己独立的线程上下文,包括线程ID、栈、栈指针、程序计数器、条件码和通用目的寄存器值。每个线程和其他线程一起共享进程上下文的剩余部分。这包括整个用户虚拟地址空间,它是由只读文本(代码)、读/写数据、堆以及所有的共享库代码和数据区域组成的。线程也共享相同的打开文件的集合。
②变量映射到内存:多线程C程序变量根据它们的存储类型被映射到虚拟内存(全局变量、本地自动变量、本地静态变量)
③一个变量是共享的,当且仅当多个线程引用这个变量的某个实例。

(3)与传统的单线程相比,多线程设计时需要注意哪些问题?
答:使用安全的C函数,对共享成员的访问尽量独立化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值