操作系统系统的三大核心功能:进程管理,内存管理和操作系统三大核心功能。这里想先从进程说起。
进程
一个程序加载到内存后就变为进程(进程是执行中的程序)。
进程 = 程序 + 执行
进程是表示资源分配的基本单位,又是调度运行的基本单位。例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I/O设备等。然后,把该进程放人进程的就绪队列。进程调度程序选中它,为它分配CPU以及其它有关资源,该进程才真正运行。所以,进程是系统中的并发执行的单位。
【选看:从物理内存的分配来看,每个进程占用一片内存空间,从这点上说,进程不过就是内存的某片空间。由于在任意时刻,CPU上只能执行一条指令,所以任意时刻上在CPU上执行的进程只有一个,而到底执行哪条指令由物理程序计数器指定,也就是说在物理层面上,所有进程共用一个程序计数器。逻辑上讲,程序计数器可以有多个,因为每个进程都可以暂时挂起让别的进程执行,所以每个进程就需要一个程序计数器去记住每次挂起的位置,便于恢复时从上次挂起的位置开始。
多道编程:发明进程是为了多道编程,多道编程目的提交计算机CPU的效率,多核CPU或者单核来回切换】
进程的状态
三个状态之间可以相互转换,一共有6种:
但是后两种不可以直接转换。
进程管理的手段
操作系统来管理进程,用于维护进程记录的结构就是进程表或进程控制块(PCB,Process Control Block).PCB存放的就是有关该进程的资料。
进程的缺陷
1.只能在一个时间干一件事情,例如两场电影在两个房间放,没办法两个都看。
2.进程如果在执行过程中阻塞,例如等待输入,整个进程就将挂起(暂停),而无法继续执行。这样,即使进程里面有部分工作不依赖于输入数据,也无法推进。
正是有了上述进程的缺陷,因此人们发明了线程。
线程
线 程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。如果把进程理解为在逻辑上操作系统所完成的任务,那么线程表示完成该任务的许多可能的子任 务之一。我们知道进程是运转的程序,是为了在CPU上实现多道编程而发明的一个概念、
线程管理
线程共享一个进程空间,因此许多资源是共享的。这些共享的资源显然不需要存放在线程控制块里,而实在存放在进程控制块即可。但是线程是不同的执行序列,总会有不能共享的资源。
线程共享的:地址空间,全局变量,打开的文件等。
线程独享的:程序计数器,寄存器和栈等。
线程管理方式
内核态线程:由操作系统管理,
用户态线程:用户自己操作管理。
线程通信
由于每个进程至少有一个线程,进程的通信其实就是里面的线程通信,其实下面的一些通信方式即是进程也是线程的通信方式,所以接下里统一使用线程通信来讲解。
1.管道
2.记名管道
如果要在两个不想管的线程,如两个 不同进程里面的线程,之间进行管道通信,则需要使用记名管道。顾名思义,记名管道是一个有名字的通信管道。
3.套接字
套接字是另外一种可以用于进程间通信的机制。使用套接字进行通信需要双方均创建一份套接字,其中一方作为服务器方,另外一方作为客户方。服务器方必须先创建一个服务区套接字,然后在该套接字上进行加监听,等待远方的连接请求。欲与服务器通信的客户则创建一个客户套接字,然后向服务区套接字发送连接请求。服务器套接字在收到连接请求后,将在服务器机器上创建一个客户套接字,与远方的客户机上的客户套接字形成点到点的通信通道。之后,客户端和服务器就可以通过send和reve命令在套接字上进行交流了。
4.信号(线程电报)
管道和套接字虽然得到了广泛的应用,但是还是有缺点的,比如第一,必须事先在通信的进程间建立连接,这会消耗系统资源。第二,通信是自愿的。既一方虽然可以随意往管道或套接字发送信号,对方却可以选择接受时间,比如对方对此充耳不闻,如果通信一旦建立,我们就像进行尽可能多的通信,如果信息量微小,用管道和套接字就有点杀鸡用牛刀,效率低下。因此我们需要一种不同的机制来处理如下通信需求:1.箱迫使一方对我们的通信立即作出回应。2.我们不愿事先建立任何连接,而是临时突然觉得需要向某个进程通信3.传输的信息量微小,使用管道和套接字不划算。因此信号产生了。
信号就是一个内核对象,或者说是一个内核数据结构。发送方将该数据结构填好,并指明该信号的目标进程后再发出,类似于我们日常生活中的电报。
5.信号量
就像铁路上的信号量,一个进程在信号变为0或者1的情况下推进,当进程完成任务后,则将信号再改为0或1.
6.共享内存
7.消息队列
咋一看有点像管道,但它不是管道,首先它无需固定的读写进程,任何进程都可以读写。其次它可以同时支持多个进程,多个进程可以读写消息队列,即所谓的多对多,而不是管道的点对点。
其他通信机制
例如windows支持的进程通信方式就有剪贴板等等。
线程同步
线程同步的目的就是不管线程之间的执行如何穿插,其运行结果都是正确的,或者说,我们要保证多线程执行下结果的确定性。
线程同步就是让所有线程按照一定的规则执行,使得其正确性和效率都有迹可寻。
一.锁
二.信号量
信号量功能很强大,不光是一个同步原语,还是一个通信原语,而且它还能作为锁来使用。上面提到可以作为通信的信号量,这里讲作为同步原语和锁的能力。
semaphore说白了就是一个计数器,其取值为当前累计的信号数量,它支持两个操作加法up和减法down。
三.管程
--引入iOS线程同步的多种方式,只有锁?
答案:不只有锁。
1.@synchronized 。锁
@synchronized(obj)指令使用的obj为该锁的唯一标识,只有当标识相同时,才为满足互斥。
2.NSLock
NSLock是Cocoa提供给我们最基本的锁对象,这也是我们经常所使用的,除lock和unlock方法外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,如果锁不可用(已经被锁住),刚并不会阻塞线程,并返回NO。lockBeforeDate:方法会在所指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。
3.dispatch_semaphore信号量
dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数,分别是
dispatch_semaphore_create 传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。
dispatch_semaphore_signal long dispatch_semaphore_signal(dispatch_semaphore_t dsema) 这个函数会使传入的信号量dsema的值加1;
dispatch_semaphore_wait long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);这个函数会使传入的信号量dsema的值减1;这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,不能直接传入整形或float型数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。dispatch_semaphore
是信号量,但当信号总量设为 1 时也可以当作锁来。在没有等待情况出现时,它的性能比 pthread_mutex 还要高,但一旦有等待情况出现时,性能就会下降许多。相对于 OSSpinLock 来说,它的优势在于等待时不会消耗 CPU 资源。
对以上各个锁进行1000000此的加锁解锁的空操作时间如下:
OSSpinLock: 46.15 ms
dispatch_semaphore: 56.50 ms
pthread_mutex: 178.28 ms
NSCondition: 193.38 ms
NSLock: 175.02 ms
pthread_mutex(recursive): 172.56 ms
NSRecursiveLock: 157.44 ms
NSConditionLock: 490.04 ms
@synchronized: 371.17 ms
总的来说:
OSSpinLock和dispatch_semaphore的效率远远高于其他。
@synchronized和NSConditionLock效率较差。
鉴于OSSpinLock的不安全,所以我们在开发中如果考虑性能的话,建议使用dispatch_semaphore。
如果不考虑性能,只是图个方便的话,那就使用@synchronized。
进程调度
CPU调度就是达到极小化平均响应时间,极大化系统吞吐量,保证系统各个部件都处于繁忙状态和提供貌似公平的机制。。
先来先服务调度算法(FCFS)
优点:简单,人人都能理解,实现容易。
缺点:短的共工作有可能变得很慢,因为前面有很长的工作。
时间片轮转
是对FIFO算法的一种改进,主要目的是改善程序的相应时间,其方法就是周期性的进行进程切换。
优点:缺程序排在长程序后面也有机会执行。
缺点:如果时间片选择过小,则进程切换所用的系统消耗将太多,是的系统的大部分时间花再进程的上下文切换上,而用来真正执行程序的有用时间很少,从而降低系统效率,并造成浪费。
短任务优先(STCF)
短任务的优先级比长任务的高,而我们总是安排优先级高的程序先运转。
优点:总是运行需要执行时间最短的 程序,其系统平均响应时间在我们之前已经讨论过的几种调度算法里面是最优的。
缺点:可能造成长程序无法得到CPU时间而导致饥饿。另外一个就是我们不知道每个程序还需要运转多久,可有用一些启发式的方法来进行估算。