操作系统基础篇

1.线程和进程以及它们之间的区别
进程是具有一定功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源调度和分配的一个独立单位。(关键词:资源调度分配的独立单位)

线程是进程的实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。(关键词:CPU调度分派的基本单位)

一个进程可以有多个线程(至少一个),多个线程也可以并发执行。

进程作为资源(如内存)分配的基本单位,作为其下属的线程都是可以享用其被分配到的资源的,而且线程可以共享同一块被分配的资源。而进程之间是一般不能分享彼此的资源的,进程想要互相通信,必须通过进程间通信(Inter-process communication,IPC)的机制来完成,主要包括以下几种:
管道(pipe,半双工),流管道(s_pipe,全双工),有名管道(FIFO,全双工)
信号量(sophomore/mutex)
信号(signal)
消息队列
共享内容
套接字(socket)
线程可以再分为两类:

一类是用户级线程(user level thread)。对于这类线程,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。在应用程序启动后,操作系统分配给该程序一个进程号,以及其对应的内存空间等资源。应用程序通常先在一个线程中运行,该线程被成为主线“程。在其运行的某个时刻,可以通过调用线程库中的函数创建一个在相同进程中运行的新线程。 用户级线程的好处是非常高效,不需要进入内核空间,但并发效率不高。

另一类是内核级线程(kernel level thread)。对于这类线程,有关线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只能调用内核线程的接口。内核维护进程及其内部的每个线程,调度也由内核基于线程架构完成。内核级线程的好处是,内核可以将不同线程更好地分配到不同的CPU,以实现真正的并行计算。

事实上,在现代操作系统中,往往使用组合方式实现多线程,即线程创建完全在用户空间中完成,并且一个应用程序中的多个用户级线程被映射到一些内核级线程上,相当于是一种折中方案。

这里再说明一个“程序”和”进程“的区别。

程序是一个静态概念,它是指在计算机的文件系统里以文件形式存储的一段可运行代码。而进程是一个动态概念,它通常是指操作系统里一个程序在一个数据集合上一次运行过程的体现。即进程是程序的运行逻辑实际运作起来的载体。

好比扫雷是一个存在于你的开始菜单里的游戏程序,当你打开它时,你发现任务管理器里会有一个winmine的进程,而你关掉扫雷后,这个wunmine进程就消失了,但是扫雷程序还在你的菜单里。所谓一个是静态,一个是动态!

线程同步的方式有哪些?

首先要明白,什么是线程同步,为什么要同步?

所谓同步,就是并发的线程在一些关键点上可能需要互相等待与互通信息,这种相互制约的等待与互通信息称为进程同步。
1
“同”其实是协同,而不是同时,因为我们知道多线程终究是不能同时执行的(看起来那么多程序同时运行互相不干扰那是从宏观层面看是这样),那么线程之间由于执行权在不断地切换,如果不同线程都做不同的事,处理不同的数据倒也没什么问题,关键是有时候会有那么一些数据,是被多个线程共享的,大家都有可能对这些数据进行一些操作。

比如,银行系统同时收到两个请求向同一个账户A存钱,都是要存入100元.账户A中本来有1000元。那么系统对两个请求肯定是分别生成一个线程去完成各自的对账户进行加上100元的操作,可是当其中一个线程1运行完以下语句:

account=account+100
1
当线程1读取到account在内存中的值1000并加上了100得到1100,但还没更新到数据库中时,线程1由于丧失了执行权,线程2来接手了,线程2也执行这一句,但它读到的account值依然是1000(数据库的数据尚未更新),也把它加上100,变成1100,然后要更新到数据库。那后面不管谁先更新到数据库,数据库里A账户的记录都只会是1100,而不是1200了,这就出岔子了。

要防范这种问题也不难,我们可以让给执行账户值更改一直到把新的值更新到数据库里的整个操作给”封闭“起来,一次只允许一个线程来对同一个账户执行这样一些操作,如果多个线程想同时来执行这个操作,对不起,因为这些操作被”封闭“了,不等里面的一个线程把它要做的修改更新到数据库之前,其它线程一概不许进入,乖乖在外面排队等着吧。

这只是一个简单的例子,于是我们需要一些机制来保证这样的问题不会出现,让不同的线程在操作这样一些共同的数据时不会出问题,再或者是有时候有些事必须等A线程做完了,B线程才能接着后面的做,我们要让这些需要多个线程合作的任务都正常进行,因此就要让它们”同步“!

这里再引入一个临界资源与临界区的概念:

临界资源是指一次仅允许一个线程使用的资源,许多物理设备,如打印机都有这种性质。除了物理设备外,还有一些软件资源,若被多线程所共享也具有这一特点,如变量、数据、表格、队列等。它们虽可以为若干线程所共享,但一次只能为一个线程所利用。

临界区指的是一个访问共用资源(被多个线程共享的临界资源)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机。
1
2
3
如上所述,同步机制所要解决的绝大多数问题,都出在临界区这儿,我们后面的同步机制都是在临界区上做文章,以避免出现问题。

同步有以下这样些个方式/机制:

互斥量(mutex):互斥量是一种公共资源,在指定时刻,它只能被一个线程占有(也就是所有权特性),而且占有它的线程可以反复申请这个互斥量。只有拥有互斥量的线程才有访问公共资源的权限。因为互斥量只有一个,所以可以保证公共资源不会被多个线程同时访问。(比如Java中的synchronized代码块,需要你提供一个类的对象或Class类作为锁,这个锁就可以理解为互斥量)

信号量(semaphore):每个信号量都是公共资源,其值是一个32位计数。信号量的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。
实现的P,V操作算法描述:
P操作:while s>0
s=s-1,
V操作:s=s+1。
P表示申请一个资源,如果条件满足(即右可以分配的资源),则把资源分配给提出申请的进程,并且时资源数目s减1。V表示资源使用哪完毕之后,要把占有的资源释放,并且资源数目s加1 。

事件(信号 signal):通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作

猜你喜欢

转载自blog.csdn.net/u012420317/article/details/88780673