linux原理

linux进程间通信等方式

  • (1) 管道(pipe):管道允许一个进程和另一个与它有共同祖先的进程之间进行通信;
  • (2) 命名管道(FIFO):类似于管道,但是它可以用于任何两个进程之间的通信,命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建;
  • (3) 信号(signal):信号是比较复杂的通信方式,用于通知接收进程有某种事情发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持UNIX早期信号语义函数signal外,还支持语义符合POSIX.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD即能实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数的功能);
  • (4) 内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它;
  • (5) 消息队列(message queue):消息队列是消息的连接表,包括POSIX消息对和System V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能成该无格式字节流以及缓冲区大小受限等缺点;
  • (6) 信号量(semaphore):信号量主要作为进程间以及同进程不同线程之间的同步手段;
  • (7) 共享内存 (shared memory):它使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。这是针对其他通信机制运行效率较低而设计的。它往往与其他通信机制,如信号量结合使用,以达到进程间的同步及互斥;
  • (8) 套接字(Socket):它是更为通用的进程间通信机制,可用于不同机器之间的进程间通信

select、poll、epoll之间的区别

select原理

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

  1. 单个进程可监视的fd数量被限制,即能监听端口的大小有限。这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.
  2. 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:
  3. 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。

poll原理

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:

  1. 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
  2. poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

epoll原理

epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无 论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者 遇到EAGAIN错误。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

epoll为什么要有EPOLLET触发模式?

如果采用EPOLLLT模式的话,系统中一旦有大量你不需要读写的就绪文件描述符,它们每次调用epoll_wait都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率.。而采用EPOLLET这种边沿触发模式的话,当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符

epoll的优点:

1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口); 2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数; 即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

3、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

select、poll、epoll 区别总结:

IO模式最大链接数时间复杂度消息传递事件驱动实现
select1024O(n)需要内核拷贝轮询
pollO(n)需要内核拷贝轮询
epollO(1)共享一块内存回调函数

水平触发和边缘触发的区别?

Level_triggered(水平触发):

当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!

Edge_triggered(边缘触发):

当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!

什么是硬链接和软链接?

1)硬链接

由于 Linux 下的文件是通过索引节点(inode)来识别文件,硬链接可以认为是一个指针,指向文件索引节点的指针,系统并不为它重新分配 inode 。每添加一个一个硬链接,文件的链接数就加 1 。

  • 1)不可以在不同文件系统的文件间建立链接;
  • 2)只有超级用户才可以为目录创建硬链接。

2)软链接

软链接克服了硬链接的不足,没有任何文件系统的限制,任何用户可以创建指向目录的符号链接。因而现在更为广泛使用,它具有更大的灵活性,甚至可以跨越不同机器、不同网络对文件进行链接。

  • 不足:因为链接文件包含有原文件的路径信息,所以当原文件从一个目录下移到其他目录中,再访问链接文件,系统就找不到了,而硬链接就没有这个缺陷,你想怎么移就怎么移;还有它要系统分配额外的空间用于建立新的索引节点和保存原文件的路径。

linux内存管理机制

  • 块式管理:
    • 把内存分为固定大小的块,每个块只包含一个进程。如果进程需要内存的话,分配他一块内存即可。
  • 页式管理:
    • 把内存分为大小相等的而且固定的一页一页形式,页相对较小(类似于mysql的叶子节点)。
    • 相对于块式管理粒度更小,提高了内存利用率,减少了内存碎片。
    • 页式管理通过页表对应逻辑地址和物理地址。
  • 段式管理:
    • 段式管理把内存分为一段一段的区域,大小不固定。
    • 每个段定义了一组逻辑信息,每个段构成独立的地址空间。
  • 段页式管理:
    • 分段和分页的结合,先分段,每个段中在进行分页。

页面置换算法

地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法

1、先进先出(FIFO)

  1. 原理:把内存中驻留时间最久的页面置换算法予以淘汰
  2. 特点
    1. 优点:先进先出算法实现简单,是最直观的一个算法
    2. 缺点:先进先出的性能最差,因为与通常页面的使用规则不符合,所以实际应用少

2、第二次机会置换算法(SC)

  1. 原理:寻找一个在最近的时钟间隔内没有被访问过的页面。如果所有的页面都被访问过了,该算法就简化为纯粹的FIFO算法
  2. 特点:

3、时钟置换算法

  1. 原理:把所有的页面都保存在一个类似钟面的环形链表中,一个表指针指向最老的页面。当发生缺页中断时,首先检查表指针指向的页面,如果它的R位是0就淘汰该页面,并把新的页面插入这个位置,然后把表指针前移一个位置。如果R位是1就清除R位并把表指针前移一个位置;重复这个过程一直到找到一个R位为0的页面为止。
  2. 特点:

4、最近最久未使用(LRU)

  1. 原理:选择最近且最久未被使用的页面进行淘汰
  2. 特点:
    1. 优点:由于考虑程序访问的时间局部性,一般能有较好的性能;实际应用多
    2. 缺点:实现需要较多的硬件支持,会增加硬件成本

5、最佳置换算法

  1. 原理:每次选择未来长时间不被访问的或者以后永不使用的页面进行淘汰
  2. 特点:
    1. 优点:最佳置换算法可以保证获得最低的缺页率
    2. 缺点:最佳置换算法是一种理想化算法,具有较好的性能,但是实际上很难实现。

6、最近未使用算法(NRU)

  1. 原理:随机地从类编号最小的非空类中挑选一个页面淘汰。在一个时间滴答中(大约20ms)淘汰一个没有被访问的已修改页面要比淘汰一个被频繁使用的“干净”页面好
  2. 特点:
    1. 优点是易于理解和能够有效地被实现
    2. 它的性能不是最好的。

7、最不常用置换算法(NFU)

  1. 原理:将每个页面与一个软件计数器相关联。计数器的初值为0。每次时钟中断时,由操作系统扫描内存中所有的页面,将每个页面的R位(它是0或1)加到它的计数器上。这个计数器大体上跟踪了各个页面被访问的频繁程度。发生缺页中断时,则置换计数器值最小的页面。
  2. 特点:

8、老化算法

  1. 原理:老化算法是对NFU算法的修改,其修改包括两个部分,首先,在R位被加进之前将计数器右移一位,其次,将R位加到计数器最左端的位而不是最右端的位。
  2. 特点:

9、工作集页面置换算法

10、工作集时钟页面置换算法

僵尸进程和孤儿进程

  1. 僵尸进程:一个父进程利用fork创建子进程,如果子进程退出,而父进程没有利用wait 或者 waitpid 来获取子进程的状态信息,那么子进程的状态描述符依然保存在系统中。

  2. 孤儿进程:一个父进程退出,它的一个或多个子进程还在运行,子进程将成为孤儿进程,孤儿进程将被init进程所收养;

  3. 两者区别

    1. 僵尸进程是父进程在,子进程退出;孤儿进程是父进程退出,子进程还在;
    2. 僵尸进程将会导致资源浪费,而孤儿进程则不会。

那么如何避免僵尸进程呢

  • (1)fork两次:原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程
  • (2)通过信号机制:子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程

free -h命令中buffers 和cached有什么不同

Buffer用于临时存储IO设备数据,不分读写。在Buffer中的数据必须进行下一步处理后才能清空回收内存空间。例如用于读取的Buffer,需要后续指令对读取到buffer中的数据进行处理,在处理完成前清空内存会出现读失败;用于写入的Buffer,则需要写入到IO设备,在写入完成前清空内存会出现数据丢失。严格意义上的Buffer,在后续处理完成后则应当回收内存。

Cache则是把暂时不用的数据存储在内存中,以加快下一次对这些数据的访问。理论上来说,Cache是随时可以清空的,代价是下一次访问相同数据的时候性能下降。但实际上Cache机制中往往存在某种形式的Buffer,并非所有用于Cache的内存都可以随时清空回收。