Linux系统

    xiaoxiao2021-03-25  256

    创建空文件的几种方式

    touch file vi file %借助空设备 cat /dev/null > file echo -n > file %-n使echo不输出换行 dd if=/dev/zero of=<filename> bs=1 count=0 %借助zero设备,使用dd指令方式,通用性更强,但效率更低。(bs可以省略)

    死锁和四个必要条件

    产生死锁的原因主要是: (1) 因为系统资源不足。 (2) 进程运行推进的顺序不合适。 (3) 资源分配不当等。 如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。


    产生死锁的四个必要条件: (1) 互斥条件:一个资源每次只能被一个进程使用。 (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 (3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

    银行家算法

    最著名的的死锁避免算法。

    系统处于安全状态时,一定不会发生死锁; 系统处于不安全状态时,不一定会发生死锁;

    例题:

    某系统有n台互斥使用的同类设备,3个并发进程需要3,4,5台设备,可确保系统不发生死锁的设备数n最小为 (10) 解: 进程1(3台):申请到2台,无法工作; 进程2(4台):申请到3台,无法工作; 进程3(5台):申请到4台,无法工作; 申请总数:2+3+4=9,此时若只有9台,3个进程持续申请且申请不到,造成死锁。 所以还必须要加多1台,当有10台时,可以打破死锁。

    并发、并行、同步、异步的区别

    并发

    在操作系统中,一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。 并发又可分为互斥和同步。 互斥:进程间相互排斥地使用临界资源的现象,就叫互斥。 同步:进程之间相互依赖,前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时,第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。 其中并发又有伪并发和真并发,伪并发是指单核处理器的并发,真并发是指多核处理器的并发。

    并行

    在单处理器的多道程序设计系统中,进程被交替执行,表现出一种并发的外部特征;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。 并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也就是说并发事件之间不一定要同一时刻发生。

    同步和异步

    异步和同步是相对的。同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。 线程就是实现异步的一个方式。异步让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。

    多线程

    多线程是程序设计的逻辑层概念,它是进程中并发运行的一段代码。多线程可以实现线程间的切换执行。

    异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。

    Linux内核地址分布

    直接映射区:线性空间中从3G开始最大896M的区间,为直接内存映射区,该区域的线性地址和物理地址存在线性转换关系:线性地址=3G+物理地址。

    动态内存映射区:该区域由内核函数vmalloc来分配,特点是:线性空间连续,但是对应的物理空间不一定连续。vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。

    永久内存映射区:该区域可访问高端内存。访问方法是使用alloc_page(_GFP_HIGHMEM)分配高端内存页或者使用kmap函数将分配到的高端内存映射到该区域。

    固定映射区:该区域和4G的顶端只有4k的隔离带,其每个地址项都服务于特定的用途,如ACPI_BASE等。

    进程与线程

    就绪与等待

    就绪是已获得所需要的资源,只等待CPU的调度即可运行; 等待是指等待资源。

    进程控制块

    进程控制块(PCB)是操作系统用来记录进程详细状态好相关信息的基本数据结构,它和进程一一对应,是进程的唯一标识。 进程控制块记录了进程的标识信息/状态信息/控制信息。 (1)标识信息:唯一标识一个进程。 (2)状态信息:记录进程使用处理器时的各种现场信息,主要有CPU通用寄存器的内容,CPU状态寄存器的内容以及栈指针。 (3)控制信息:操作系统对进程进行调度管理时用到的信息。

    线程和进程的区别与联系

    进程是程序在一个数据集合上的执行过程,是操作系统分配资源的基本单位。 线程是进程中的一个实体,是操作系统调度的最小单位。

    进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。线程:相对于进程而言,线程是一个更加接近执行体的概念,它可以与同一进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。 两者都可以提高程序的并发度,提高程序运行效率和响应时间。

    线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在多处理机上运行,而进程则可以跨机器迁移。

    根本区别:每个进程都有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的: 1、速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。 2、资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。 3、同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。

    线程通信

    消息队列

    事件

    事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。比如在某些网络应用程序中,一个线程如A负责侦听通信端口,另外一个线程B负责更新用户数据,利用事件机制,则线程A可以通知线程B何时更新用户数据。每个Cevent对象可以有两种状态:有信号状态和无信号状态。Cevent类对象有两种类型:人工事件和自动事件。 ps:事件是内核对象,可以解决线程间同步问题,因此也能解决互斥问题。

    条件变量

    主要由于多个线程可能更改全局变量,因此全局变量最好加上volatile关键字。

    互斥锁

    互斥与临界区很相似,但是使用时相对复杂一些(互斥量为内核对象),不仅可以在同一应用程序的线程间实现同步,还可以在不同的进程间实现同步,从而实现资源的安全共享。

    自旋锁

    自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。 其作用是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。

    但是它也有些不足之处:

    自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁。

    因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或双核的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。

    信号量

    信号量的用法和互斥的用法很相似,不同的是它可以同一时刻允许多个线程访问同一个资源,PV操作

    进程通信

    进程通信又称IPC(Inter-Process Communication),指多个进程之间相互通信,交换信息的方法。根据进程通信时信息量大小的不同,可以将进程通信划分为两大类型: 1、低级通信,控制信息的通信(主要用于进程之间的同步,互斥,终止和挂起等等控制信息的传递) 2、高级通信,大批数据信息的通信(主要用于进程间数据块数据的交换和共享,常见的高级通信有管道,消息队列,共享内存等)。

    套接字( socket )

    共享内存( shared memory )

    共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

    管道

    管道是指用于连接一个读进程和一个写进程以实现进程之间通信的一种共享文件。向管道提供输入的是发送进程,也称为 写进程,负责向管道输入数据,数据的格式是字符流。接受管道 数据的接受进程为读进程。

    消息队列

    FIFO

    互斥量

    信号

    信号量

    孤儿进程、僵尸进程

    定义

    孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。 僵尸进程:一个已经终止、但是其父进程尚未对其进行善后处理的(获取终止进程的有关信息,释放他仍占用的资源)的进程。

    僵尸进程的危害

    僵尸进程即父进程未对其终止状态做善后处理的进程,占有一定的系统资源,如果产生大量的僵尸进程,就会造成系统资源枯竭(如:没有可用的进程ID、文件描述符)。 孤儿进程:孤儿进程是没有父进程的进程,当一个进程成为孤儿进程时,它就会被init进程所收养,而init进程被编写成无论何时只要有一个子进程终止,init就会调用一个wait函数获取其终止状态,即防止了在系统中有很多僵死进程。 所以,僵尸进程会对系统造成影响,而孤儿进程则不会。

    缓存(页面置换)算法-FIFO、LFU、LRU

    缺页次数就是指在缓存中找不到目标页面的次数。

    FIFO

    做法:淘汰最早进入内存的页面

    FIFO 算法认为:随着时间的推移,在内存中待的时间最长的页面,被访问的可能性最小

    实际中:有可能把经常要访问的页面淘汰出去

    LFU

    LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。 注意LFU和LRU算法的不同之处,LRU的淘汰规则是基于访问时间,而LFU是基于访问次数的。

    LRU

    LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

    例题:

    在一个请求页式存储管理中,一个程序的页面走向为 3、4、2、1、4、5、3、4、5、1、2,并采用 LRU 算法。设分配给该程序的存储块数 S 分别为 3 和 4,在该访问中发生的缺页次数 F 是

    以下用x表示缺页的情况 1.S=3 3,4,2,1,4,5,3,4,5,1,2 --------------------- 3 4 2 1 4 5 3 4 5 1 2 3 4 2 1 4 5 3 3 5 1 3 4 2 1 4 5 4 3 5 x x x x x x x x 所以F=8 2.S=4 3,4,2,1,4,5,3,4,5,1,2 --------------------- 3 4 2 1 4 5 3 4 5 1 2 3 4 2 1 4 5 3 3 3 1 3 4 2 1 4 5 4 5 3 3 3 2 1 1 1 4 5 x x x x x x x 所以F=7

    中断

    外部中断处理过程首先要保护现场,使得中断处理完之后能够恢复程序的执行状态继续执行。保护现场有两个含义: (1)由中断隐指令自动保存程序的断点(程序计数器); (2)由操作系统的中断服务程序保存通用寄存器和状态寄存器的内容。

    虚拟内存

    虚拟存储器的最大容量是由(计算机系统的地址结构和内、外存空间)决定的。 换句话说,虚拟存储器的最大容量 = min(内存+外存,2^n)。n为计算机的地址总线位数。

    分页存储管理

    逻辑页=逻辑地址/页面大小。 假设进程A的逻辑地址为0x0457(十六进制),物理页的大小为512字节,则逻辑页号= 1111/512 = 2(取整舍去余数)

    可变分区法分配内存

    内部碎片指的是在存储管理时,给程序配了一定的内存,但是没有全部使用,有一部分空闲而浪费。 而外部碎片指的是可用内存无法满足用户要求而导致的浪费问题,比如用户要求2MB的内存,但是可用内存中有1MB,无法满足用户需求。

    可变分区就是用户申请分区时系统根据用户的情况给用户分配大小的内存空间,而不是分配大小固定的内存。克服了固定分区方式中内存浪费的问题,解决了内部碎片的问题,而不能解决外部碎片的问题,因为有时候可能用户申请的内存大于系统闲置的内存。

    适应算法

    最先适应算法:依次判定后找到第一个满足要求的。 最佳适应算法:对空闲区按从小到大排序,然后找第一个满足要求的 最差适应算法:对空闲区按从大到小排序,然后找第一个满足要求的

    分段和分页

    分页和分段系统有许多相似之处。两者都采用离散分配方式,且都要通过地址映射机构来实现地址变换。 但在概念上两者完全不同,主要表现在下述三个方面。

    页是信息的物理单位,用户不可见,长度固定。分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率。可以说分页仅仅是由于系统管理的需要而不是用户的需要。段是信息的逻辑单位,用户可见,长度不定。 它含有一组其意义相对完整的信息。分段的目的是为了 能更好地满足用户的需要 。 页的大小固定且由系统确定 ,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而一个系统只能有一种大小的页面。 段的长度却不固定, 决定于用户所编写的程序 ,通常由编译程序根据信息的性质来划分。分页的作业地址空间是 一维 的,即单一的线性地址空间,程序员只需利用 一个记忆符,即可表示一个地址; 分段的作业地址空间则是 二维 的,程序员在标识一个地址时, 既需给出段名,又需给出段内地址。

    分段:不定长,连续 分页:定长,可能连续

    分段管理的优点

    可以实现有意义的共享; 方便地址转换; 程序不需要连续的内存。

    分段尺寸最大为具体内存。在动态链接时先将主程序所对应的目标程序装入内存并启动运行,运行过程中需要调用某段时才将该段内存合并进行链接。 而作业的大小不受内存大小限制,由虚拟存储器解决空间不够问题,允许作业装入的时候只装入一部分,另一部分放在 磁盘 上,当需要的时候再装入到主存。 这样以来,在一个小的主存空间就可以运行一个比它大的作业。同 时,用户编程的时候也摆脱了一定要编写小于主存容量的作业的限制。

    简言之,分段尺寸受内存空间的限制,但作业总的尺寸不受内存空间的限制。

    缓冲

    引入缓冲的目的

    1.缓和处理机和I/O设备间速度不匹配的矛盾 2.减少对CPU的中断次数 3.提高CPU和I/O设备之间的并行性

    单缓冲和双缓冲

    对于单缓冲: 假定从磁盘把一块数据输入到缓冲区的时间为T,操作系统将该缓冲区中的数据传送到用户区的时间为M,而CPU对这一块数据处理的时间为 C。由于T和C是可以并行的,当T>C时,系统对每一块数据的处理时间为M+T,反之则为M+C,故可把系统对每一块数据的处理时间表示为Max(C, T)+M。 对于双缓冲: 系统处理一块数据的时间可以粗略地认为是MAC(C, T)。如果C<T,可使块设备连续输入;如果C>T,则可使CPU不必等待设备输入。

    core文件

    linux默认不产生core文件,需要进行设置 ulimit -c。 core文件在进程当前工作目录的下创建。通常与程序在相同的路径下。但如果程序中调用了chdir函数,则有可能改变了当前工作目录。这时core文件创建在chdir指定的路径下。 当然程序崩溃不一定都产生core文件。

    进程调度

    进程分类

    交互式进程 想想你使用vi编辑文本,大量的人机交互,进程不断的进入睡眠状态等待你的输入,CPU占用不高但是要求响应迅速,比如你敲了键盘,过了10秒钟才在编辑器上显示出来,这用户体验是多么的糟糕。

    批处理进程 最典型的就是编译工程这种进程了,如果我们编译一个大型的工程,我们敲了个make,也许1个小时才能编译成功,但是没有人会两个眼睛盯着屏幕看编译的过程,我们更关心的是编译的结果。对于这种进程,只要他在跑就行了,不必给他太多的关注和资源。

    实时进程 linux所说的实时进程是软实时,就是尽量的实时,如果做不到,也不会出现毁灭性的后果。比如视频播放器,解码速度有点慢,顶多是视频播放有点卡,用户体验不好,但是不会机毁人亡。

    不同的进程,就用不同的调度策略伺候之,对于实时进程,我们就采用实时调度策略:FIFO or Round Robin(时间片轮转)。对于批处理进程和交互进程,我们采用CFS调度器,核心思想比较简单,“完全公平”,降低了代码复杂度,很好地满足了各种需求。

    实时进程调度策略

    实时进程调度有两个维度,一个是优先级,一个是调度策略。 其中,调度策略又可以分为: FIFO(先来先服务),时间片轮转,多级反馈(时间片轮转的一种)。

    先来先服务(FIFO)

    先来先服务(FIFO)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。 当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。 在进程调度中采用FIFO算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。

    短作业(进程)优先

    短作业(进程)优先调度算法SJ§F,是指对短作业或短进程优先调度的算法。它们可以分别用于作业调度和进程调度。 短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。 而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。

    时间片轮转(round robin)

    在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列,每次调度时,把CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几ms 到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。换言之,系统能在给定的时间内响应所有用户的请求。

    多级反馈队列

    多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,调度算法的实施过程如下所述。

    (1) 设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。

    (2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按先来先服务(FIFO)原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按先来先服务(FIFO)原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。

    (3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。

    完全公平调度算法(CFS)

    根据进程的不同分类Linux采用不同的调度策略。对于实时进程,采用FIFO或者Round Robin的调度策略。对于普通进程,则需要区分交互式和批处理式的不同。 传统Linux调度器提高交互式应用的优先级,使得它们能更快地被调度。而CFS和RSDL等新的调度器的核心思想是“完全公平”。这个设计理念不仅大大简化了调度器的代码复杂度,还对各种调度需求的提供了更完美的支持。注意Linux通过将进程和线程调度视为一个,同时包含二者。进程可以看做是单个线程,但是进程可以包含共享一定资源(代码和/或数据)的多个线程。因此进程调度也包含了线程调度的功能。

    CFS原理

    cfs定义了一种新的模型,它给cfs_rq(cfs的run queue)中的每一个进程安排一个虚拟时钟,vruntime。如果一个进程得以执行,随着时间的增长(也就是一个个tick的到来),其vruntime将不断增大。没有得到执行的进程vruntime不变。 而调度器总是选择vruntime跑得最慢的那个进程来执行。这就是所谓的“完全公平”。为了区别不同优先级的进程,优先级高的进程vruntime增长得慢,以至于它可能得到更多的运行机会。

    CFS的实现原理

    CFS的思想就是让每个调度实体的vruntime互相追赶,而每个调度实体的vruntime增加速度不同,权重越大的增加的越慢,这样就能获得更多的cpu执行时间。 调度实体sched_entity代表一个调度单位,进程的vruntime和权重都保存在这个结构中。 那么所有的sched_entity怎么组织在一起呢?红黑树。所有的sched_entity以vruntime为key(实际上是以vruntime-min_vruntime为key,是为了防止溢出,反正结果是一样的)插入到红黑树中,同时缓存树的最左侧节点,也就是vruntime最小的节点,这样可以迅速选中vruntime最小的进程。 注意只有等待CPU的就绪态进程在这棵树上,睡眠进程和正在运行的进程都不在树上。

    CFS还有一个重要特点,即调度粒度小。CFS之前的调度器中,除了进程调用了某些阻塞函数而主动参与调度之外,每个进程都只有在用完了时间片或者属于自己的时间配额之后才被抢占。而CFS则在每次tick都进行检查,如果当前进程不再处于红黑树的左边,就被抢占。在高负载的服务器上,通过调整调度粒度能够获得更好的调度性能。

    作业调度和进程调度

    作业调度负责创建进程将其调入内存,进程调度才能获取CPU。

    线程池

    什么是线程池?

    其实线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:

    先启动若干数量的线程,并让这些线程都处于睡眠状态,当客户端有一个新请求时,就会唤醒线程池中的某一个睡眠线程,让它来处理客户端的这个请求,当处理完这个请求后,线程又处于睡眠状态。

    可能你也许会问:为什么要搞得这么麻烦,如果每当客户端有新的请求时,我就创建一个新的线程不就完了?这也许是个不错的方法,因为它能使得你编写代码相对容易一些,但你却忽略了一个重要的问题——性能! 假设高峰期每秒的客户端请求并发数超过100,如果为每个客户端请求创建一个新线程的话,那耗费的CPU时间和内存将是惊人的,如果采用一个拥有200个线程的线程池,那将会节约大量的的系统资源,使得更多的CPU时间和内存用来处理实际的服务,而不是频繁的线程创建与销毁。

    数据库连接池

    数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。

    一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样会造成系统的性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 连接池技术尽可能多地重用了消耗内存的资源,大大节省了内存,提高了服务器的服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

    最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费;最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。

    线程池的组成

    一个线程池包括以下四个基本组成部分:

    线程池管理器(ThreadPool):用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;工作线程(PoolWorker):线程池中的线程,在没有任务时处于等待状态,可以循环的执行任务;任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

    线程池技术正是关注如何缩短或调整线程创建、销毁的时间的技术,从而提高服务器程序性能的。它把线程创建、销毁分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段。

    线程池还显著减少了创建线程的数目。

    消息队列和管道、FIFO的区别

    消息队列是 Linux IPC 中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据。 消息队列和之前讨论过的 管道和 FIFO 有很大的区别,主要有以下两点: 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和 FIFO 是相反的,进程向其中写消息时,管道和 FIFO 必需已经打开来读,否则写进程就会阻塞(默认情况下)。

    IPC 的持续性不同。 管道和 FIFO 是 随进程 的持续性,当管道和 FIFO 最后一次关闭发生时,仍在管道和 FIFO 中的数据会被丢弃。 消息队列是 随内核 的持续性,即一个进程向消息队列写入消息后,然后终止,另外一个进程可以在以后某个时刻打开该队列读取消息。只要内核没有重新自举,消息队列没有被删除。

    内存管理的几种方式

    段式管理(每次分配的大小不固定)

    把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上(计算机最耗时间的大家都知道是I/O吧)。

    页式管理(每次分配的大小固定)

    把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。

    段页式(整体分段,段内分页)

    把主存分为一段一段的,每一段的空间要比一页小很多,这种方法在空间利用率上比页式管理高很多,但有另外一个缺点:一个程序片段可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上。 段页式管理每取一数据要访问3次内存: 第一次是由段表地址寄存器得段表始址后访问段表,由此取出对应段的页表在内存中的地址。 第二次则是访问页表得到所要访问的物理地址。 第三次才能访问真正需要访问的物理单元。

    大、小端模式和网络字节序

    大端模式:高字节放在低地址,符合正常逻辑,用于网络数据传输;

    小端模式:高字节放在高地址,和普通逻辑相反。

    (0x12345678举例子) 1)大端模式: 低地址 -----------------> 高地址 0x12 | 0x34 | 0x56 | 0x78 2)小端模式: 低地址 ------------------> 高地址 0x78 | 0x56 | 0x34 | 0x12

    网络字节序

    网络上的数据流是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它是将这个字节作为高位还是低位来处理呢? 网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位。而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。可见多字节数值在发送前,在内存中数值应该以大端法存放。 比如我们经过网络发送0x12345678这个整形,在80X86平台中,它是以小端法存放的,在发送前需要使用系统提供的htonl将其转换成大端法存放。

    转载请注明原文地址: https://ju.6miu.com/read-1104.html

    最新回复(0)