概念:node
线程:是操做系统可以进行运算调度的最小单位。是进程中的一个执行流程,一个进程中能够运行多个线程。linux
进程:一个执行中的程序的实例。程序员
进程在执行过程当中拥有独立的内存单元,而多个线程共享内存web
进程和线程的主要差异在于它们是不一样的操做系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不一样执行路径。线程有本身的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,因此多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行而且又要共享某些变量的并发操做,只能用线程,不能用进程。算法
进程在执行过程当中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的全部线程共享该进程的全部资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。可是每一个线程拥有本身的栈段,栈段又叫运行时段,用来存放全部局部变量和临时变量。数组
进程是资源分配的最小单位,线程是CPU调度的最小单位;缓存
系统开销: 因为在建立或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。所以,操做系统所付出的开销将显著地大于在建立或撤消线程时的开销。相似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少许寄存器的内容,并不涉及存储器管理方面的操做。可见,进程切换的开销也远大于线程切换的开销。安全
通讯:因为同一进程中的多个线程具备相同的地址空间,导致它们之间的同步和通讯的实现,也变得比较容易。进程间通讯IPC,线程间能够直接读写进程数据段(如全局变量)来进行通讯——须要进程同步和互斥手段的辅助,以保证数据的一致性。在有的系统中,线程的切换、同步和通讯都无须操做系统内核的干预网络
进程间不会相互影响 ;线程一个线程挂掉将致使整个进程挂掉数据结构
在 linux 下进程间通讯的几种主要手段简介:
管道(Pipe)及有名管道(named pipe):管道:传输资源。本质上是内核的一块缓冲区。(特性:半双工,单向通讯)。 Linux一切皆文件,操做系统为管道提供操做的方法:文件操做。管道可用于具备亲缘关系进程间的通讯,有名管道克服了管道没有名字的限制,所以,除具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯;
消息队列(Message):
消息队列其实是操做系统在内核为咱们建立的一个队列,经过这个队列的标识符key,每个进程均可以打开这个队列,每一个进程均可以经过这个队列向这个队列中插入一个结点或者获取一个结点来完成不一样进程间的通讯。
用户组织一个带有类型的数据块,添加到队列中,其余的进程从队列中获取数据块,即消息队列发送的是一个带有类型的数据块;消息队列是一个全双工通讯,可读可写(能够发送数据,也能够接受数据)。
消息队列是消息的连接表,包括Posix消息队列system V消息队列。有足够权限的进程能够向队列中添加消息,被赋予读权限的进程则能够读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
** 共享内存**:使得多个进程能够访问同一块内存空间,是最快的可用IPC形式。是针对其余通讯机制运行效率较低而设计的。每每与其它通讯机制,如信号量结合使用,来达到进程间的同步及互斥。
信号量(semaphore):主要做为进程间以及同一进程不一样线程之间的同步手段。
套接口(Socket):更为通常的进程间通讯机制,可用于不一样机器之间的进程间通讯。起初是由Unix系统的BSD分支开发出来的,但如今通常能够移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
各类通讯方式的比较和优缺点:
管道:速度慢,容量有限,只有父子进程能通信
有名管道(named pipe):任何进程间都能通信,但速度慢
消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
信号量:不能传递复杂消息,只能用来同步
共享内存:可以很容易控制容量,速度快,但要保持同步,好比一个进程在写的时候,另外一个进程要注意读写的问题,至关于线程中的线程安全,固然,共享内存区一样能够用做线程间通信,不过没这个必要,线程间原本就已经共享了同一进程内的一块内存
进程间通讯主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。
1.管道:
管道主要包括无名管道和命名管道:管道可用于具备亲缘关系的父子进程间的通讯,有名管道除了具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯
1.1 普通管道PIPE:
1)它是半双工的(即数据只能在一个方向上流动),具备固定的读端和写端
2)它只能用于具备亲缘关系的进程之间的通讯(也是父子进程或者兄弟进程之间)
3)它能够当作是一种特殊的文件,对于它的读写也可使用普通的read、write等函数。可是它不是普通的文件,并不属于其余任何文件系统,而且只存在于内存中。
1.2 命名管道FIFO:
1)FIFO能够在无关的进程之间交换数据
2)FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
2. 系统IPC:
2.1 消息队列
消息队列,是消息的连接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。 (消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特色)具备写权限得进程能够按照必定得规则向消息队列中添加新信息;对消息队列有读权限得进程则能够从消息队列中读取信息;
特色:
1)消息队列是面向记录的,其中的消息具备特定的格式以及特定的优先级。
2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
3)消息队列能够实现消息的随机查询,消息不必定要以先进先出的次序读取,也能够按消息的类型读取。
2.2 信号量semaphore
信号量(semaphore)与已经介绍过的 IPC 结构不一样,它是一个计数器,能够用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通讯数据。
特色:
1)信号量用于进程间同步,若要在进程间传递数据须要结合共享内存。
2)信号量基于操做系统的 PV 操做,程序对信号量的操做都是原子操做。
3)每次对信号量的 PV 操做不只限于对信号量值加 1 或减 1,并且能够加减任意正整数。
4)支持信号量组。
2.3 信号signal
信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生。
2.4 共享内存(Shared Memory)
它使得多个进程能够访问同一块内存空间,不一样进程能够及时看到对方进程中对共享内存中数据得更新。这种方式须要依靠某种同步操做,如互斥锁和信号量等
特色:
1)共享内存是最快的一种IPC,由于进程是直接对内存进行存取
2)由于多个进程能够同时操做,因此须要进行同步
3)信号量+共享内存一般结合在一块儿使用,信号量用来同步对共享内存的访问
3. 套接字SOCKET:
socket也是一种进程间通讯机制,与其余通讯机制不一样的是,它可用于不一样主机之间的进程通讯。
一、临界区:
经过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;
二、互斥量 Synchronized/Lock:
采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。由于互斥对象只有一个,因此能够保证公共资源不会被多个线程同时访问
三、信号量 Semphare:
为控制具备有限数量的用户资源而设计的,它容许多个线程在同一时刻去访问同一个资源,但通常须要限制同一时刻访问此资源的最大线程数目。
四、事件(信号),Wait/Notify:
经过通知操做的方式来保持多线程同步,还能够方便的实现多线程优先级的比较操做
并发(concurrency):指宏观上看起来两个程序在同时运行,好比说在单核cpu上的多任务。可是从微观上看两个程序的指令是交织着运行的,你的指令之间穿插着个人指令,个人指令之间穿插着你的,在单个周期内只运行了一个指令。这种并发并不能提升计算机的性能,只能提升效率。
并行(parallelism):指严格物理意义上的同时运行,好比多核cpu,两个程序分别运行在两个核上,二者之间互不影响,单个周期内每一个程序都运行了本身的指令,也就是运行了两条指令。这样说来并行的确提升了计算机的效率。因此如今的cpu都是往多核方面发展。
线程产生的缘由:
进程可使多个程序能并发执行,以提升资源的利用率和系统的吞吐量;可是其具备一些缺点:
进程在同一时间只能干一件事
进程在执行的过程当中若是阻塞,整个进程就会挂起,即便进程中有些工做不依赖于等待的资源,仍然不会执行。
所以,操做系统引入了比进程粒度更小的线程,做为并发执行的基本单位,从而减小程序在并发执行时所付出的时空开销,提升并发性。和进程相比,线程的优点以下:
从资源上来说,线程是一种很是"节俭"的多任务操做方式。在linux系统下,启动一个新的进程必须分配给它独立的地址空间,创建众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工做方式。
从切换效率上来说,运行于一个进程中的多个线程,它们之间使用相同的地址空间,并且线程间彼此切换所需时间也远远小于进程间切换所须要的时间。据统计,一个进程的开销大约是一个线程开销的30倍左右。(
从通讯机制上来说,线程间方便的通讯机制。对不一样进程来讲,它们具备独立的数据空间,要进行数据的传递只能经过进程间通讯的方式进行,这种方式不只费时,并且很不方便。线程则否则,因为同一进程下的线程之间共享数据空间,因此一个线程的数据能够直接为其余线程所用,这不只快捷,并且方便。
除以上优势外,多线程程序做为一种多任务、并发的工做方式,还有以下优势:
一、使多CPU系统更加有效。操做系统会保证当线程数不大于CPU数目时,不一样的线程运行于不一样的CPU上。
二、改善程序结构。一个既长又复杂的进程能够考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序才会利于理解和修改。
多进程模型的优点是CPU
多线程模型主要优点为线程间切换代价较小,所以适用于I/O密集型的工做场景,所以I/O密集型的工做场景常常会因为I/O阻塞致使频繁的切换线程。同时,多线程模型也适用于单机多核分布式场景。
多进程模型,适用于CPU密集型。同时,多进程模型也适用于多机分布式场景中,易于多机扩展。
死锁是指两个或两个以上进程在执行过程当中,因争夺资源而形成的相互等待的现象。死锁发生的四个必要条件以下:
互斥条件:进程对所分配到的资源不容许其余进程访问,若其余进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源;
请求和保持条件:进程得到必定的资源后,又对其余资源发出请求,可是该资源可能被其余进程占有,此时请求阻塞,但该进程不会释放本身已经占有的资源
不可剥夺条件:进程已得到的资源,在未完成使用以前,不可被剥夺,只能在使用后本身释放
环路等待条件:进程发生死锁后,必然存在一个进程-资源之间的环形链
解决死锁的方法即破坏上述四个条件之一,主要方法以下:
资源一次性分配,从而剥夺请求和保持条件
可剥夺资源:即当进程新的资源未获得知足时,释放已占有的资源,从而破坏不可剥夺的条件
资源有序分配法:系统给每类资源赋予一个序号,每一个进程按编号递增的请求资源,释放则相反,从而破坏环路等待的条件
互斥锁(mutex)机制,以及互斥锁和读写锁的区别
一、互斥锁和读写锁区别:
互斥锁:mutex,用于保证在任什么时候刻,都只能有一个线程访问该对象。当获取锁操做失败时,线程会进入睡眠,等待锁释放时被唤醒。
读写锁:rwlock,分为读锁和写锁。处于读操做时,能够容许多个线程同时得到读操做。可是同一时刻只能有一个线程能够得到写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程得到写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。
互斥锁和读写锁的区别:
1)读写锁区分读者和写者,而互斥锁不区分
2)互斥锁同一时间只容许一个线程访问该对象,不管读写;读写锁同一时间内只容许一个写者,可是容许多个读者同时读对象。
二、Linux的4种锁机制:
互斥锁:mutex,用于保证在任什么时候刻,都只能有一个线程访问该对象。当获取锁操做失败时,线程会进入睡眠,等待锁释放时被唤醒
读写锁:rwlock,分为读锁和写锁。处于读操做时,能够容许多个线程同时得到读操做。可是同一时刻只能有一个线程能够得到写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程得到写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。
自旋锁:spinlock,在任什么时候刻一样只能有一个线程访问对象。可是当获取锁操做失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提升效率。但若是加锁时间过长,则会很是浪费CPU资源。
RCU:即read-copy-update,在修改数据时,首先须要读取数据,而后生成一个副本,对副本进行修改。修改完成后,再将老数据update成新的数据。使用RCU时,读者几乎不须要同步开销,既不须要得到锁,也不使用原子指令,不会致使锁竞争,所以就不用考虑死锁问题了。而对于写者的同步开销较大,它须要复制被修改的数据,还必须使用锁机制同步并行其它写者的修改操做。在有大量读操做,少许写操做的状况下效率很是高。
A* a = new A; a->i = 10;在内核中的内存分配上发生了什么?
一、程序内存管理:
一个程序本质上都是由BSS段、data段、text段三个组成的。能够看到一个可执行程序在存储(没有调入内存)时分为代码段、数据区和未初始化数据区三部分。
BSS段(未初始化数据区):一般用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态分配,程序结束后静态变量资源由系统自动释放。
数据段:存放程序中已初始化的全局变量的一块内存区域。数据段也属于静态内存分配
代码段:存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经肯定,而且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量
text段和data段在编译时已经分配了空间,而BSS段并不占用可执行文件的大小,它是由连接器来获取内存的。
bss段(未进行初始化的数据)的内容并不存放在磁盘上的程序文件中。其缘由是内核在程序开始运行前将它们设置为0。须要存放在程序文件中的只有正文段和初始化数据段。
data段(已经初始化的数据)则为数据分配空间,数据保存到目标文件中。
数据段包含通过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中获得,而后连接器获得这个大小的内存块,紧跟在数据段的后面。当这个内存进入程序的地址空间后所有清零。包含数据段和BSS段的整个区段此时一般称为数据区。
可执行程序在运行时又多出两个区域:栈区和堆区。
栈区:由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。而后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增加的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。
堆区:用于动态分配内存,位于BSS和栈中间的地址区域。由程序员申请分配和释放。堆是从低地址位向高地址位增加,采用链式存储结构。频繁的 malloc/free形成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照必定的算法搜索可用的足够大的空间。所以堆的效率比栈要低的多。
二、A* a = new A; a->i = 10:
1)A *a:a是一个局部变量,类型为指针,故而操做系统在程序栈区开辟4/8字节的空间(0x000m),分配给指针a。
2)new A:经过new动态的在堆区申请类A大小的空间(0x000n)。
3)a = new A:将指针a的内存区域填入栈中类A申请到的地址的地址。即*(0x000m)=0x000n。
4)a->i:先找到指针a的地址0x000m,经过a的值0x000n和i在类a中偏移offset,获得a->i的地址0x000n + offset,进行*(0x000n + offset) = 10的赋值操做,即内存0x000n + offset的值是10。
一个类,里面有static,virtual,之类的,来讲一说这个类的内存分布
一、static修饰符
1)static修饰成员变量
对于非静态数据成员,每一个类对象都有本身的拷贝。而静态数据成员被当作是类的成员,不管这个类被定义了多少个,静态数据成员都只有一份拷贝,为该类型的全部对象所共享(包括其派生类)。因此,静态数据成员的值对每一个对象都是同样的,它的值能够更新。
由于静态数据成员在全局数据区分配内存,属于本类的全部对象共享,因此它不属于特定的类对象,在没有产生类对象前就可使用。
2)static修饰成员函数
与普通的成员函数相比,静态成员函数因为不是与任何的对象相联系,所以它不具备this指针。从这个意义上来讲,它没法访问属于类对象的非静态数据成员,也没法访问非静态成员函数,只能调用其余的静态成员函数。
Static修饰的成员函数,在代码区分配内存。
二、C++继承和虚函数
C++多态分为静态多态和动态多态。静态多态是经过重载和模板技术实现,在编译的时候肯定。动态多态经过虚函数和继承关系来实现,执行动态绑定,在运行的时候肯定。
动态多态实现有几个条件:
(1) 虚函数;
(2) 一个基类的指针或引用指向派生类的对象;
基类指针在调用成员函数(虚函数)时,就会去查找该对象的虚函数表。虚函数表的地址在每一个对象的首地址。查找该虚函数表中该函数的指针进行调用。
每一个对象中保存的只是一个虚函数表的指针,C++内部为每个类维持一个虚函数表,该类的对象的都指向这同一个虚函数表。
虚函数表中为何就能准确查找相应的函数指针呢?由于在类设计的时候,虚函数表直接从基类也继承过来,若是覆盖了其中的某个虚函数,那么虚函数表的指针就会被替换,所以能够根据指针准确找到该调用哪一个函数。
三、virtual修饰符
若是一个类是局部变量则该类数据存储在栈区,若是一个类是经过new/malloc动态申请的,则该类数据存储在堆区。
若是该类是virutal继承而来的子类,则该类的虚函数表指针和该类其余成员一块儿存储。虚函数表指针指向只读数据段中的类虚函数表,虚函数表中存放着一个个函数指针,函数指针指向代码段中的具体函数。
若是类中成员是virtual属性,会隐藏父类对应的属性。
软连接和硬连接区别
为了解决文件共享问题,Linux引入了软连接和硬连接。除了为Linux解决文件共享使用,还带来了隐藏文件路径、增长权限安全及节省存储等好处。若1个inode号对应多个文件名,则为硬连接,即硬连接就是同一个文件使用了不一样的别名,使用ln建立。若文件用户数据块中存放的内容是另外一个文件的路径名指向,则该文件是软链接。软链接是一个普通文件,有本身独立的inode,可是其数据块内容比较特殊。
用户态和内核态区别
协程是一种用户态的轻量级线程,协程的调度彻底由用户控制。从技术的角度来讲,“协程就是你能够暂停执行的函数”。协程拥有本身的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其余地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操做栈则基本没有内核切换的开销,能够不加锁的访问全局变量,因此上下文的切换很是快。
1) 一个线程能够多个协程,一个进程也能够单独拥有多个协程。
2) 线程进程都是同步机制,而协程则是异步。
3) 协程能保留上一次调用时的状态,每次过程重入时,就至关于进入上一次调用的状态。
4)线程是抢占式,而协程是非抢占式的,因此须要用户本身释放使用权来切换到其余协程,所以同一时间其实只有一个协程拥有运行权,至关于单线程的能力。
5)协程并非取代线程, 并且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程须要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器能够关联任意线程或线程池, 可使当前线程, UI线程, 或新建新程.。
6)线程是协程的资源。协程经过Interceptor来间接使用线程这个资源。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”全部select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操做,将数据从kernel拷贝到用户进程。
这个图和blocking IO的图其实并无太大的不一样,事实上还更差一些。由于这里须要使用两个系统调用(select和recvfrom),而blocking IO只调用了一个系统调用(recvfrom)。可是,用select的优点在于它能够同时处理多个connection。
强调:
1. 若是处理的链接数不是很高的话,使用select/epoll的web server不必定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优点并非对于单个链接能处理得更快,而是在于能处理更多的链接。
2. 在多路复用模型中,对于每个socket,通常都设置成为non-blocking,可是,如上图所示,整个用户的process实际上是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。
结论: select的优点在于能够处理多个链接,不适用于单个链接
(1)select==>时间复杂度O(n)
它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至所有),咱们只能无差异轮询全部流,找出能读出数据,或者写入数据的流,对他们进行操做。因此select具备O(n)的无差异轮询复杂度,同时处理的流越多,无差异轮询时间就越长。
(2)poll==>时间复杂度O(n)
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,而后查询每一个fd对应的设备状态, 可是它没有最大链接数的限制,缘由是它是基于链表来存储的.
(3)epoll==>时间复杂度O(1)
epoll能够理解为event poll,不一样于忙轮询和无差异轮询,epoll会把哪一个流发生了怎样的I/O事件通知咱们。因此咱们说epoll其实是事件驱动(每一个事件关联上fd)的,此时咱们对这些流的操做都是有意义的。(复杂度下降到了O(1))
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
epoll的高效就在于,当咱们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然能够飞快的返回,并有效的将发生事件的句柄给咱们用户。这是因为咱们在调用epoll_create时,内核除了帮咱们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储之后epoll_ctl传来的socket外,还会再创建一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据便可。有数据就返回,没有数据就sleep,等到timeout时间到后即便链表没数据也返回。因此,epoll_wait很是高效。 那么,这个准备就绪list链表是怎么维护的呢?当咱们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上以外,还会给内核中断处理程序注册一个回调函数,告诉内核,若是这个句柄的中断到了,就把它放到准备就绪list链表里。因此,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。 如此,一颗红黑树,一张准备就绪句柄链表,少许的内核cache,就帮咱们解决了大并发下的socket处理问题。执行epoll_create时,建立了红黑树和就绪链表,执行epoll_ctl时,若是增长socket句柄,则检查在红黑树中是否存在,存在当即返回,不存在则添加到树干上,而后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时马上返回准备就绪链表里的数据便可。
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就能够采用指针的方式读写操做这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操做而没必要再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而能够实现不一样进程间的文件共享。以下图所示:
总而言之,常规文件操做须要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只须要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不一样数据不通的繁琐过程。所以mmap效率更高。
由上文讨论可知,mmap优势共有一下几点:
一、对文件的读取操做跨过了页缓存,减小了数据的拷贝次数,用内存读写取代I/O读写,提升了文件读取效率。
二、实现了用户空间和内核空间的高效交互方式。两空间的各自修改操做能够直接反映在映射的区域内,从而被对方空间及时捕捉。
三、提供进程间共享内存及相互通讯的方式。无论是父子进程仍是无亲缘关系的进程,均可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而经过各自对映射区域的改动,达到进程间通讯和进程间共享的目的。
同时,若是进程A和进程B都映射了区域C,当A第一次读取C时经过缺页从磁盘复制文件页到内存中;但当B再读C的相同页面时,虽然也会产生缺页异常,可是再也不须要从磁盘中复制文件过来,而可直接使用已经保存在内存中的文件数据。
四、可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操做的一个方面,解决方案每每是借助硬盘空间协助操做,补充内存的不足。可是进一步会形成大量的文件I/O操做,极大影响效率。这个问题能够经过mmap映射很好的解决。换句话说,但凡是须要用磁盘空间代替内存的时候,mmap均可以发挥其功效。
MD5的典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。
MD5将整个文件看成一个大文本信息,经过其不可逆的字符串变换算法,产生了这个惟一的MD5信息摘要。
MD5就能够为任何文件(无论其大小、格式、数量)产生一个一样独一无二的“数字指纹”,若是任何人对文件作了任何改动,其MD5值也就是对应的“数字指纹”都会发生变化。
MD5其实是一种有损压缩技术,压缩前文件同样MD5值必定同样,反之MD5值同样并不能保证压缩前的数据是同样的。在密码学上发生这样的几率是很小的,因此MD5在密码加密领域有一席之地。可是专业的黑客甚至普通黑客也能够利用MD5值,实际是有损压缩技术这一原理,将MD5的逆运算的值做为一张表俗称彩虹表的散列表来破解密码。