Linux操作系统
操作系统
内存空间
进程空间地址划分
以Linux 64位系统为例。理论上,64bit内存地址可用空间为0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF(16位十六进制数),这是个相当庞大的空间,Linux实际上只用了其中一小部分(256T)。
Linux64位操作系统仅使用低47位,高17位做扩展(只能是全0或全1)。所以,实际用到的地址为空间为0x0000000000000000 ~ 0x00007FFFFFFFFFFF(user space)和0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF(kernel space),其余的都是unused space
user space 也就是用户区由以下几部分组成:代码段,数据段,BSS段,heap,stack
由下文的程序打印结果来看,stack空间变量地址,往变小处增长,堆空间地址往变大处增长。但栈地址大于堆地址。
#include void show_address(int *p) { std::cout 阻塞(进程等待输入而进入阻塞)
进程与线程的区别与联系
区别
-
调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是程序的一次执行过程,是资源分配的最小单位。线程是进程中执行运算的最小单位,是进程中的一个实体,是操作系统调度的基本单位。
-
并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行;
-
拥有资源:线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。进程所维护的是程序所包含的资源(静态资源), 如:地址空间,打开的文件句柄集,文件系统状态,信号处理handler等;线程所维护的运行相关的资源(动态资源),如:运行栈,调度相关的控制信息,待处理的信号集等;
-
系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
联系
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源;
- 处理机分给线程,即真正在处理机上运行的是线程;
- 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
为什么有了多线程还要用多进程?
进程是一个程序实体,多个程序需要多个进程。此外,进程地址空间相互隔离,安全。
为什么有了多进程还要多线程?
- 并发:每个进程有一个地址空间和一个控制线程,多线程并行实体共享同一个地址空间和所有可用数据的能力,这是多进程模型(具有不同的地址空间)无法做到的(跨进程通信太麻烦)。
- 开销少:线程比进程更轻量级,比进程更容易创建,更容易撤销。易于调度。
- 性能方面,若多个线程都是 CPU 密集型的,则不能获得性能上的增强,若是存在大量的计算和 IO 处理,多线程允许这些活动彼此重叠进行,从而会加快程序执行的速度。
什么是CPU密集型、IO密集型?
进程、线程和协程之间的区别和联系
什么时候用进程?什么时候用线程?
进程与线程的选择取决以下几点:
- 需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。
- 线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应
- 因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布用线程;
- 并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求;
- 需要更稳定安全时,适合选择进程;需要速度时,选择线程更好。
进程和线程的区别?什么时候用进程?什么时候用线程?
协程
- **协程是一种比线程更加轻量级的存在。**正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
- 最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
- 极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显;
- 不需要多线程的锁机制:因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
线程池的好处
- 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行。
- 第三:提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
进程与程序的区别
- 进程是程序的一次运行活动,属于一种动态的概念。程序是一组有序的静态指令,是一种静态的概念。
- 一个进程可以执行一个或多个程序。
- 程序可以作为一种软件资源长期保持着,而进程则是一次执行过程,它是暂时的,是动态地产生和终止的。
- 进程更能真实地描述并发,而程序不能。
- 进程由程序和数据两部分组成,进程是竞争计算机系统有限资源的基本单位
- 进程具有创建其他进程的功能;而程序没有。
- 进程还具有并发性和交往性,这也与程序的封闭性不同
线程同步的方式
(对共享内存进行访问的程序片段称作临界区域)
-
互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
互斥量只有两种状态:0,1,其中0表示解锁。当一个线程需要访问临界区时,如果该互斥量当前是解锁的(即临界区可用),则调用线程可进入临界区。
-
信号量:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
-
事件(信号):(允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务)通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
进程与线程之间的一种简单解释
进程同步的方式
信号量,互斥量,管程,文件锁
进程/线程之间的亲缘性
亲缘性的意思是进程/线程只在某个cpu上运行(多核系统),比如:
BOOL WINAPI SetProcessAffinityMask( _In_ HANDLE hProcess, _In_ DWORD_PTR dwProcessAffinityMask ); /* dwProcessAffinityMask 如果是 0 , 代表当前进程只在cpu0 上工作; 如果是 0x03 , 转为2进制是 00000011 . 代表只在 cpu0 或 cpu1上工作; */
使用CPU亲缘性的好处:设置CPU亲缘性是为了防止进程/线程在CPU的核上频繁切换,从而避免因切换带来的CPU的L1/L2 cache失效,cache失效会降低程序的性能。
进程线程协程上下文切换的性能比较
进程上下文包含了进程执行所需要的所有信息。
用户地址空间:包括程序代码,数据,用户堆栈等;
控制信息:进程描述符,内核栈等;
硬件上下文:进程恢复前,必须装入寄存器的数据统称为硬件上下文。
线程上下文切换和进程上下文切换一个最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
另外一个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
线程切换不需要切换数据区和堆,只需要切换线程自己的栈区域。
进程间的通信方式
(Inter Process Communication, IPC 几种通信方式)
-
共享内存( shared memory ):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
MySQL内部通信也使用了共享内存的方式,可以通过配置文件添加 --shared-memory实现
-
信号量( semophore ): 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
-
信号 ( sinal ):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。除了用于进程通信外,进程还可以发送信号给进程本身。
-
管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。
管道有三种:
① 普通管道:有两个限制:一是只支持半双工通信方式,即只能单向传输;二是只能在具有亲缘关系间的进程(父子进程)之间使用;
② 流管道:去除第一个限制,支持双向传输;
③ 命名管道:去除第二个限制,可以在不相关进程之间进行通信。
- 命名管道 (named pipe): 命名管道也是半双工的通信方式,它克服了管道没有名字的限制,并且它允许无亲缘关系进程间的通信。命令管道在文件系统中有对应的文件名,命名管道通过命令mkfifo或系统调用mkfifo来创建。 Microsoft SQL Server数据库默认安装后的本地连接使用的就是命名管道。 MySQL在 Window环境下,如果需要两个进程在同一台服务器上通信可以使用命名管道,通过 --enable-named-pipe选项设置。
-
消息队列( message queue ): 消息队列是由消息的链表结构实现,存放在内核中并由消息队列标识符标识。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
-
套接字( socket ): 也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
进程间通信——几种方式的比较和详细实例
关于 fork 和父子进程的理解
什么是缓冲区溢出?有什么危害?其原因是什么?
缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
危害有以下两点:
- 程序崩溃,导致拒绝服务
- 跳转并且执行一段恶意代码
造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。
缓冲区溢出攻击
作业(进程)调度算法
进程与作业的联系和区别
一、联系
- 一个作业通常包括几个进程,几个进程共同完成一个任务,即作业。
- 用户提交作业以后,当作业被调度,系统会为作业创建进程,一个进程无法完成时,系统会为这个进程创建子进程。
二、区别
-
进程是一个程序在一个数据集上的一次执行,而作业是用户提交给系统的一个任务。
-
一个作业可由多个进程组成,且必须至少由一个进程组成,反过来则不成立。
调度算法
-
先来先服务调度算法(FCFS)
每次调度都是从后备作业队列中选择第一个进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。
-
最短作业(进程)优先调度算法(SPF)(非抢占式)
最短作业优先(SJF)的调度算法总是选择剩余运行时间最短的那个作业(进程)进行。缺点:长作业的运行得不到保证。
-
最短剩余时间优化算法(抢占式)
先运行剩余时间最短的那个进程A,此时,如果来了一个新的进程B,剩余时间最短,当前运行的进程A就被挂起,而运行新的进程B。这种方式可以使新的短作业获得良好的服务。
-
轮转调度(RR)
在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列。每个进程被分配一个时间段,称为时间片。每次调度时,把CPU分配给队首进程,并令其执行一个时间片。如果在时间片结束时,该进程还在运行,则将剥夺CPU并分配给另一个进程。如果该进程在时间片结束前阻塞或结束,则CPU立即进行切换。
-
多级反馈队列调度算法
它是目前被公认的一种较好的进程调度算法。
(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。
(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。
几个常用的操作系统进程调度算法
死锁
死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
(在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态)
活锁:指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。
死锁产生的条件?
死锁产生的四个条件(四个条件必定是同时满足的,有一个条件不成立,则不会产生死锁)
- 互斥条件:一个资源一次只能被一个进程使用;
- 请求与保持条件(占有且等待):一个进程因请求资源而阻塞时,对已获得资源保持不放;
- 不剥夺条件:进程获得的资源,在未完全使用完之前,不能被其它进程强行剥夺;
- 循环等待条件:死锁发生时,系统中一定由两个或两个以上的进程组成的一个环路,该环路中的每一个进程都在都在等待着下一个进程所占有的资源
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了 。
怎么解决死锁
四点:预防死锁、避免死锁、检测死锁、解除死锁
死锁预防
死锁预防——确保系统永远不会进入死锁状态。
产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。
破坏请求与保持(占有且等待)条件
方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源, 这样就不会再有请求了 。
优点:简单易实施且安全。
- 缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。使进程经常发生饥饿现象。
方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。
破坏不剥夺条件
当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。
该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作失效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。
破坏循环等待条件
可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为 i 的资源时,那么它下一次申请资源只能申请编号大于 i 的资源。【哲学家就餐问题
这样虽然避免了循环等待,但是这种方法是比较低效的,资源的执行速度会变慢,并且可能在没有必要的情况下拒绝资源的访问,比如说,进程 3 想要申请资源 1,如果资源 1 并没有被其他进程占有,此时将它分配个进程 3 是没有问题的,但是为了避免产生循环等待,该申请会被拒绝,这样就降低了资源的利用率。
死锁的避免
死锁的避免—— 在使用前进行判断,只允许不会产生死锁的进程申请资源 。
银行家算法:该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。
死锁的检测
- 首先为每个进程和每个资源指定一个唯一的号码;
- 然后建立资源分配表和进程等待表。
略,面试不会问。
死锁的解除
一旦检测出死锁,就应立即釆取相应的措施,以解除死锁。
死锁解除的主要方法有:
-
资源剥夺法。挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源,而处于资源匮乏的状态。
-
撤销进程法。强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。
终止所有的死锁进程。这种方式简单粗暴,但是代价很大,很有可能会导致一些已经运行了很久的进程前功尽弃。
逐个终止进程,直至死锁状态解除。该方法的代价也很大,因为每终止一个进程就需要使用死锁检测来检测系统当前是否处于死锁状态。
-
进程回退法。让一(多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。
死锁的四个必要条件和解决办法
4 个生活场景详解 BAT 面试中的死锁问题
什么是活锁?与死锁的区别?
活锁指的是 任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
活锁应该是一系列进程在轮询地等待某个不可能为真的条件为真。活锁的时候进程是不会blocked,这会导致耗尽CPU资源。
为解决活锁可以引入一些随机性,例如如果检测到冲突,那么就暂停随机的一定时间进行重试。这回大大减少碰撞的可能性。典型的例子是以太网的CSMA/CD检测机制。
虚拟内存
虚拟内存 使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。
参考什么是虚拟内存?| 一分钟系列
物理内存:在应用中,自然是顾名思义,物理上,真实的插在板子上的内存是多大就是多大了。而在CPU中的概念,物理内存就是CPU的地址线可以直接进行寻址的内存空间大小。
虚拟地址、逻辑地址、线性地址、物理地址的区别。
虚拟地址:指的是由程序产生的由段选择符和段内偏移地址两个部分组成的地址。为什么叫它是虚拟的地址呢?因为这两部分组成的地址并没有直接访问物理内存,而是要通过分段地址的变换机构处理或映射后才会对应到相应的物理内存地址。
逻辑地址:指由程序产生的与段相关的偏移地址部分。不过有些资料是直接把逻辑地址当成虚拟地址,两者并没有明确的界限。
线性地址:指的是虚拟地址到物理地址变换之间的中间层,是处理器可寻指的内存空间(称为线性地址空间)中的地址。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址可以再经过变换产生物理地址。若是没有采用分页机制,那么线性地址就是物理地址。
物理地址:指的是现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果!
分页和分段
- 段是信息的逻辑单位,它是根据用户的需要划分的,因此段对用户是可见的 ;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的。
- 段的大小不固定,有它所完成的功能决定;页大大小固定,由系统决定
- 段向用户提供二维地址空间;页向用户提供的是一维地址空间
- 段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制。
在网上找到了一个比较形象的比喻,挺不错的,呵呵,列出来如下: 打个比方,比如说你去听课,带了一个纸质笔记本做笔记。笔记本有100张纸,课程有语文、数学、英语三门,对于这个笔记本的使用,为了便于以后复习方便,你可以有两种选择。 第一种是,你从本子的第一张纸开始用,并且事先在本子上做划分:第2张到第30张纸记语文笔记,第31到60张纸记数学笔记,第61到100张纸记英语笔记,最后在第一张纸做个列表,记录着三门笔记各自的范围。这就是分段管理,第一张纸叫段表。 第二种是,你从第二张纸开始做笔记,各种课的笔记是连在一起的:第2张纸是数学,第3张是语文,第4张英语……最后呢,你在第一张纸做了一个目录,记录着语文笔记在第3、7、14、15张纸……,数学笔记在第2、6、8、9、11……,英语笔记在第4、5、12……。这就是分页管理,第一张纸叫页表。你要复习哪一门课,就到页表里查寻相关的纸的编号,然后翻到那一页去复习
分段和分页
磁盘调度算法:
1.先来先服务(FCFS),按访问请求到达的先后顺序服务。简单,公平,但是效率不高,相临两次请求可能会造成最内到最外柱面寻道,使磁头反复移动,增加了服务时间,对机器不利。
2.最短寻道时间优先(SSTF),优先选择距当前磁头最近的访问请求进行服务,主要考虑寻道优先。改善了磁盘平均服务时间,但是造成某些访问请求长期等待得不到服务。
3.扫描算法(SCAN),当设备无访问请求时,磁头不动;当有访问请求时,磁头按一个方向移动,在移动过程中对遇到的访问请求进行服务,然后判断该方向上是否还有访问请求,如果有则继续.
4.循环扫描算法(CSCAN):循环扫描调度算法是在扫描算法的基础上改进的。磁臂改为单项移动,由外向里。当前位置开始沿磁臂的移动方向去选择离当前磁臂最近的哪个柱面的访问者。如果沿磁臂的方向无请求访问时,再回到最外,访问柱面号最小的作业请求。
节选自路人甲: 常见面试题整理–操作系统篇(每位开发者必备)
内核中各种同步机制(自旋锁大内核锁顺序锁等)
文件
read
read函数只是一个通用的读文件设备的接口。是否阻塞需要由设备的属性和设定所决定。
一般来说,读字符终端、网络的socket描述字,管道文件等,这些文件的缺省read都是阻塞的方式。
如果是读磁盘上的文件,一般不会是阻塞方式的。但使用锁和fcntl设置取消文件O_NOBLOCK状态,也会产生阻塞的read效果。
- 缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。使进程经常发生饥饿现象。
-
-
-
-