原创不易,如果需要转载请附上本连接
操作系统
1. 进程和线程的区别
进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程(thread) 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
【总结】
进程: 指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。
线程: 系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。
2. 进程间的通讯方式
- 匿名管道通信
匿名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 - 高级管道通信
高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。 - 有名管道通信
有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 - 消息队列通信
消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 - 信号量通信
信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 - 信号
信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。 - 共享内存通信
共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。 - 套接字通信
套接字( socket ) : 套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
进程间通信有哪几种方式?进程间通信的方法详解
进程间的五种通信方式介绍
3. 进程切换的上下文细节
进程上下文切换主要涉及到两部分主要过程:进程地址空间切换和处理器状态切换。地址空间切换主要是针对用户进程而言,而处理器状态切换对应于所有的调度单位。
- 进程地址空间切换
- 处理器状态(硬件上下文)切换
Linux进程上下文切换过程context_switch详解–Linux进程的管理与调度
进程的上下文切换
进程/线程上下文切换会用掉你多少CPU?
Linux内核调度分析(进程调度)
4. 线程切换的上下文细节
CPU执行线程的时候是通过时间分片的方式来轮流执行的,当某一个线程的时间片用完(到期),那么这个线程就会被中断,CPU不再执行当前线程,CPU会把使用权给其它线程来执行。如T1线程未执行结束,T2/T3线程插进来执行了,若干时间后T1又继续执行未执行完的部分,这种就造成了线程之间的来回切换。
一次上下文切换:CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换。当Context Switch发生时,需要由操作系统保持当前线程的状态,并恢复另一个线程的状态,状态包括程序计数器、虚拟机栈中每个栈帧的信息。
5. CPU 的最小调度单位
CPU调度的基本单位是是线程。线程是操作系统能够进行运算调度的最小单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
6. 多线程同步、多进程通信方式
各个线程可以访问进程中的公共变量,资源,所以使用多线程的过程中需要注意的问题是如何防止两个或两个以上的线程同时访问同一个数据,以免破坏数据的完整性。数据之间的相互制约包括:
1、直接制约关系,即一个线程的处理结果,为另一个线程的输入,因此线程之间直接制约着,这种关系可以称之为同步关系
2、间接制约关系,即两个线程需要访问同一资源,该资源在同一时刻只能被一个线程访问,这种关系称之为线程间对资源的互斥访问,某种意义上说互斥是一种制约关系更小的同步
线程间的同步方式有四种:
- 临界区
- 互斥量
- 信号量
- 事件
进程间通信方式:查看本文 【2. 进程间的通讯方式】
7. 操作系统的栈和堆的区别
堆和栈的主要区别:
- 管理方式不同
栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。 - 空间大小不同
栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,当申请的空间超过栈的剩余空间时,将提示溢出。因此,用户能从栈获得的空间较小。
堆是向高地址扩展的数据结构,是不连续的内存区域。因为系统是用链表来存储空闲内存地址的,且链表的遍历方向是由低地址向高地址。由此可见,堆获得的空间较灵活,也较大。栈中元素都是一一对应的,不会存在一个内存块从栈中间弹出的情况。 - 是否产生碎片
对于堆来讲,频繁的malloc/free(new/delete)势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低(虽然程序在退出后操作系统会对内存进行回收管理)。对于栈来讲,则不会存在这个问题。 - 增长方向不同
堆的增长方向是向上的,即向着内存地址增加的方向;栈的增长方向是向下的,即向着内存地址减小的方向。 - 分配方式不同
堆都是程序中由malloc()函数动态申请分配并由free()函数释放的;栈的分配和释放是由编译器完成的,栈的动态分配由alloca()函数完成,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行申请和释放的,无需手工实现。 - 分配效率不同
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行。堆则是C函数库提供的,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大的空间,如果没有足够大的空间(可能是由于内存碎片太多),就有需要操作系统来重新整理内存空间,这样就有机会分到足够大小的内存,然后返回。显然,堆的效率比栈要低得多。
8. 用户态和内核态的区别
内核态与用户态是操作系统的两种运行级别,当程序运行在3级特权级上时,就可以称之为运行在用户态。因为这是最低特权级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态;
当程序运行在0级特权级上时,就可以称之为运行在内核态。
运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态(比如操作硬件)。
这两种状态的主要区别是:
处于用户态执行时,进程所能访问的内存空间和对象受到限制,其所处于占有的处理器是可被抢占的。
处于内核态执行时,则能访问所有的内存空间和对象,且所占有的处理器是不允许被抢占的。
9. 用户态和内核态切换的代价
10. fork 的过程
基本过程为:在父进程中调用 fork (),系统会创建一个子进程。在父进程中,fork () 返回子进程的进程号(pid);在子进程中,fork () 返回的是 0;如果创建失败,返回 - 1。
#include < sys/types.h >
#include < stdio.h >
#include < unistd.h >
int main() {
int pid1=fork ();
printf(“**1**\n”);
int pid2=fork ();
printf(“**2**\n”);
if(pid1==0) {
int pid3=fork ();
printf(“**3**\n”);
}
else
printf(“**4**\n”);
return 0;
}
11. 内存置换算法
内存页面置换算法
内存的页面置换算法
操作系统原理(二)——内存管理之页面置换算法
12. 什么是虚拟内存,作用
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。
13. Select,Poll,Epoll的区别
select, poll是为了解決同时大量IO的情況(尤其网络服务器),但是随着连接数越多,性能越差
epoll是select和poll的改进方案,在 linux 上可以取代 select 和 poll,可以处理大量连接的性能问题
14. Epoll 的 LT 和 ET 的区别
相同点:都是通过epoll_wait从EPOLL等待队列读取激活事件
区别:
- LT模式读取激活事件后,如果还有未处理的数据。事件会重新放入EPOLL等待队列。
- ET模式读取激活事件,直接从EPOLL等待队列移除,不管是否有未处理的数据。
Linux下的I/O复用技术 — epoll如何使用(epoll_create、epoll_ctl、epoll_wait) 以及 LT/ET 使用过程解析
epoll的LT和ET
15. 什么情况下会发生缺页中断,具体流程
什么情况下会发生缺页中断?
- 当内存管理单元(MMU)中确实没有创建虚拟物理页映射关系,并且在该虚拟地址之后再没有当前进程的线性区(vma)的时候,可以肯定这是一个编码错误,这将杀掉该进程。
- 当MMU中确实没有创建虚拟页物理页映射关系,并且在该虚拟地址之后存在当前进程的线性区vma的时候,这很可能是缺页中断,并且可能是栈溢出导致的缺页中断。
- 当使用malloc/mmap等希望访问物理空间的库函数/系统调用后,由于linux并未真正给新创建的vma映射物理页,此时若先进行写操作,将和2产生缺页中断的情况一样;若先进行读操作虽然也会产生缺页异常,将被映射给默认的零页,等再进行写操作时,仍会产生缺页中断,这次必须分配1物理页了,进入写时复制的流程。
- 当使用fork等系统调用创建子进程时,子进程不论有无自己的vma,它的vma都有对于物理页的映射,但它们共同映射的这些物理页属性为只读,即linux并未给子进程真正分配物理页,当父子进程任何一方要写相应物理页时,导致缺页中断的写时复制。
16. 如何判断逻辑地址是否已经映射在物理地址上了
浅析逻辑地址与物理地址映射关系
Linux从逻辑地址到物理地址
17. 页表所在的位置
有一个叫CR3的寄存器,专门存放的是当前进程页表的真实物理地址。另外,CR0寄存器中的特别位置可以控制CPU是否进入实模式,以及是否在保护模式中禁用MMU(现代系统一般跑在保护模式下),从而可以直接访问内存真实物理地址。
18. 环形缓冲区的好处
缓冲区的好处,就是空间换时间和协调快慢线程。缓冲区可以用很多设计法,这里说一下环形缓冲区的几种设计方案,可以看成是几种环形缓冲区的模式。
设计环形缓冲区涉及以下几个点:
- 超出缓冲区大小的的索引如何处理
- 如何表示缓冲区满和缓冲区空
- 如何入队、出队
- 缓冲区中数据长度如何计算
环形缓冲区的设计及其在生产者消费者模式下的使用(并发有锁环形队列)
19. 动态链接和静态链接
一、分别编译与链接(Linking)
静态链接方式: 在程序执行之前完成所有的组装工作,生成一个可执行的目标文件(EXE文件)。
动态链接方式: 在程序已经为了执行被装入内存之后完成链接工作,并且在内存中一般只保留该编译单元的一份拷贝。
二、静态链接库与动态链接库
先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终的EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
采用动态链接库的优点:
- 更加节省内存;
- DLL文件与EXE文件独立,只要输出接口不变,更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性。
20. 怎么查看端口号占用情况
-
lsof -i
:端口号,用于查看某一端口的占用情况,比如查看8000端口使用情况
lsof -i:8000
-
netstat -tunlp |grep
端口号,用于查看指定的端口号的进程情况,如查看8000端口的情况
netstat -tunlp |grep 8000