linux,操作系统开发相关知识总结

操作系统相关


根据进程在系统中访问资源的特点,进程在系统中访问运行分为2个级别:
1.用户态(User Mode),用户态运行的程序可以直接读取应用程序数据,权限较低。应用程序需要执行某些特殊权限的操作,读写磁盘、网络通信等,就需要向操作系统发起系统调用请求, 请求进入内核态。。
2.内核态(Kernel Mode),可以访问计算机的几乎所有资源,不受限制,权限很高。
操作系统接收到系统调用请求时,从用户态切换到内核态,执行相应的系统调用,将执行结果返回给进程。再从内核态切换到用户态。
进入内核态,需要花费很大的开销(上下文切换等),应该减少进入内核态,提高系统稳定性和性能。
划分用户态和内核态的必要性:
1.CPU有些指令很危险,比如特权指令,所有程序都能使用的话,就很非常危险,所以要限制只能内核态运行。
2.只有内核态的话,所有程序或进程就要共享系统资源,导致资源竞争和冲突。
在这里插入图片描述
系统调用:
操作系统提供给上层应用程序和编程人员使用的接口,类似于一组特殊函数,通过系统调用可以获得操作系统内核的服务(访问计算机系统资源)。
系统调用和库函数的关系:
系统调用运行在内核态,由操作系统内核提供。
库函数运行在用户态,由函数库或者用户自己提供。
系统调用过程: 必须要中断!!!
1.用户态的程序发起系统调用,系统调用中有一些特权指令(只能在内核态运行),用户态权限不足,(把系统调用名称、参数存放到寄存器),发送指令,引发系统中断;
2.发生中断后,当前CPU执行的程序会中断,根据中断指令,跳转到中断处理程序。
3.内核程序开始执行,处理系统调用。(从寄存器中读取系统调用名称、参数,然后执行);
4.内核处理完成后,根据系统调用上下文找到引发系统调用的进程,结果返回进程,(放到寄存器中),主动触发中断,再次触发中断,切换回用户态。从寄存器中拿到系统调用结果。继续执行。
在这里插入图片描述
中断和异常:
1.中断:来自于处理器外部。外围发送中断信号,CPU暂停执行,转去执行处理中断处理程序,用户态切换到内核态。

中断:(Interrupt Request): 计算机运行过程中,出现意外情况需要主机干预,机器自动停止正在运行的程序,并转入处理新情况的程序,处理完毕后返回原来被暂停的程序继续运行。
中断分为硬件中断 和 软件中断。

软件中断,一般是一个CPU指令,切换CPU到内核态,系统调用(System Call)需要通过软件中断来实现。
信号: 比如CPU中断时,操作系统发送给进程的各种信息,操作系统定义的一些标识信号。 整个操作系统有几十个信号吧。

2.异常:来自于执行当前指令的内部结果。CPU执行用户态下的程序时,发生了事先不可知的异常。触发当前运行进程切换到处理此异常的内核态程序。


CPU多级缓存:
局部性原理,时间局部性,空间局部性,所以使用缓存。减小CPU对内存和磁盘的查询,提高效率。

级别越小,越靠近CPU,速度越快,容量越小。
L1缓存:每个CPU的每个核上都有2个L1缓存,一个存Data,一个存指令。 (一般32K大小,速度最快)
L2缓存:每个CPU的每个核上,都有一个独立的L2缓存,(256K大小,比L1慢一些)
L3缓存:同一个CPU中的核共享L3缓存。(3M左右)

程序和数据,被加载到主内存;
指令和数据,会被加载到CPU的高速缓存中去;
CPU指令指令,会把结果写到高速缓存中去;
高速缓存中的数据,写入主内存。


进程(process),线程(thread),协程(CoRoutine)

1.进程(process): 计算机正在运行一个程序,一个应用实例,就是一个进程。比如,手机上运行的一个微信程序。
操作系统引入进程目的,使系统中多个程序可以并发执行,提高资源利用率和系统吞吐量。
操作系统进行资源分配和调度的一个独立的基本单位,进程都有自己的一个地址空间。
各个进程彼此互相独立。

进程5种基本状态:
(1)创建状态: 系统为进程分配PCB(进程控制块,描述进程的一个结构体), 进程所需资源还没分配,进程没有进入主内存,创建工作还没完成,进程不能被调度运行。 (fork函数创建进程的, 但是底层是glibc的execv)(系统根据程序和数据在初始时的大小来创建页表,为页表分配空间并初始化; 磁盘交换区分配空间swap:进程切换出时在磁盘上放置此进程的空间。)
linux进程空间布局:最底层到用户层。。
内核空间(kernel) -> 栈(函数,局部) -> MMS(Memory Mapping Segment, 内存映射段) -> 堆(动态申请) -> 未初始化数据段() -> 数据段 -> 代码段

(2)就绪状态: 系统分配完之后,获得许可,分配到除CPU以外的所有必要资源,等待分配CPU,进入就绪状态。
(3)执行状态: 进程获取到CPU,进程被拉起执行调度。(
重置MMU(Memory Management Unit,内存管理单元,管理虚拟内存,物理内存,内存映射);MMU读取指令和数据需要访问2次内存,性能较低。
刷新TLB(Translation Lookaside Buffer, 地址转换后的缓冲器),可看作页表的cache,存储当前最可能访问到的页表项,提高页表查询的性能。
将新进程的页表设置为当前页表,CR3控制寄存器为新进程页目录基地址;将进程的部分或全部页面装入到内存中,以减少缺页中断发生,如程序计数器PC-EIP寄存器所指页面会装入内存)

缺页中断:进程在执行中读写非法虚拟地址,造成缺页中断。
执行状态的进程:PCB中的信息不断变化。操作系统根据这些信息来管理调度进程。
(4) 阻塞状态: 正在执行的进程因为某些事件,而暂时无法继续执行时,放弃处理机而进入阻塞状态。(比如接收到一些IO请求)
(5) 终止状态: 进程自然执行结束或意外终止,不再继续执行,等待其它进程来收集。(释放该进程的页表、页面在硬盘占的空间,与其它进程共享的内存,最后一个使用的进程终止时,释放内存和磁盘上的页面)

(6)还有个 僵尸状态

进程表,也叫PCB(进程控制块)中包含哪些信息?
1.进程描述信息:进程名称、标识符等;
2.进程调度信息:阻塞原因、进程状态等,进程优先级,
3.进程对资源的需求情况:CPU,IO,内存等;
4.进程打开文件信息,(每个进程拥有一个文件描述表)
5.CPU状态信息。

进程间通信的方式!!!!!!
(Inter Process Communication, IPC), 程序员协调不同进程,使之能够在同一个操作系统里面同时运行,互相传递、交换信息。进程之间必须通信。
(1) 管道(pipe):半双工的通信方式,数据只能单向流动,无名管道:只能在亲缘关系中使用,常用于父子进程之间。有命名管道:允许无亲缘关系的进程间通信。(管道缺点:缓冲区大小受限、只能传递无格式的字节流)
(2)消息队列(queue):有消息的链表,消息队列标识符标识。(解决了管道的缺点。)
(3)共享内存(shared memory):!!最快的进程通信方式!!映射一段能被其它进程访问的内存,这段内存由一个进程创建,多个进程都可以访问。(往往和信号量配合使用。。)
(4)信号量(同步手段): 可以看做一组计数器,控制多个进程对共享资源的访问。常被用作一种锁进制,用作进程间或线程间同步的一种手段。
(4.1) 信号(siganl): 响应某些条件产生的一些事件,一个进程产生信号,另一个进程接收信号(系统内置了一些信号,SIG开头)
(5) 套接字(socket): 进程间通信,甚至可以用于不同机器之间。TCP/UDP通信。
信号量、 共享内存、 套接字(socket), 消息队列, 管道,

(6)共享内存:是目前最快的一种进程通信的方式。(向不同进程返回指向同一物理区域的地址指针)(双向通信,都可以写和拿数据)(生命周期和系统内核保持一致,任何进程都可以来读和写。。)
一个进程改变共享存储区的内容之后,其它进程立刻察觉到更改。
(共享内存没有任何进程同步 互斥机制, 所以要用信号量。)

进程调度算法 实现CPU利用率最大化。

  1. 先到先服务调度算法(FCFS,First Come, First Served) : 从就绪队列中选择一个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
    2. 短作业优先的调度算法(SJF,Shortest Job First) : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
    3.时间片轮转调度算法(RR,Round-Robin) : 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
    4.多级反馈队列调度算法(MFQ,Multi-level Feedback Queue) :前面介绍的几种进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成。因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。
    5.优先级调度算法(Priority) : 为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。

父进程,子进程。
父进程通过fork指令,可以创建其子进程,
fork:linux中,除了PID=0以外的进程,都是由其他进程使用系统调用fork创建的,调用fork创建新进程的进程叫做父进程,创建出来的进程叫做子进程。除了进程0,所有子进程都只有一个父进程,可以有多个子进程。
fork包含在unistd.h中,创建失败,父进程返回-1;创建成功,父进程返回子进程的pid>0, 子进程返回0.

子进程的结束,和父进程的运行是异步的过程,父进程无法知道子进程什么时候结束。父进程一般调用wait和waitpid系统调用取得子进程的终止状态。

僵尸进程, 孤儿进程, fork:

僵尸进程:对应进程的僵尸状态(Zombie, Z). 原因是子进程退出,父进程没有调用wait或waitpid获取子进程的状态信息(从而,内核就无法从内存中释放掉已结束的子进程的PCB,子进程的PCB就一直驻留在内存中) 僵尸进程会造成资源浪费。

linux查看僵尸进程:
zombie 值表示僵尸进程的数量,为 0 则代表没有僵尸进程

孤儿进程:父进程退出,它的一个或多个子进程还在运行。这些子进程将成为孤儿进程。孤儿进程将由进程号为1的进程init收养,并对它们完成状态收集工作(init进程循环wait回收资源)。(孤儿进程没什么危害,)

wait: 等待子进程中断或结束,父进程会暂停进程执行,直到有信号来到或子进程结束,子进程结束返回状态值。
vfork: 创建新进程,不复制父进程的空间,共享父进程的代码和数据,
父子进程资源共享关系: 子进程会复制父进程的几乎所有信息(用户空间所有数据(文件流缓冲区)、父进程内核PCB中绝大多数内容, 复制父进程的数据段、BSS段、代码段、堆空间、栈空间、文件描述符),
!!文件描述符关联的内核文件表项是共享的。父子对同一文件写入,不会覆盖,共享文件偏移。
对于变量,各自拥有副本,互不影响。

守护进程:
守护进程(daemon):生存周期很长的一种进程,仅在系统关闭时才终止(没有控制终端)。守护进程的名称通常以字母"d"结尾。如:syslogd(管理系统日志的守护进程) (一般网络服务都是以守护进程的形式在运行)

守护进程脱离终端的原因有哪些:(1)启动守护进程的终端在启动守护进程之后,还需要执行其他任务。。(2)终端上的一些键所产生的信号(比如中断信号),不应对以前从该中断启动的守护进程产生影响。
https://www.zhihu.com/question/38609004 (守护进程的详解)

守护进程 和 &后台运行程序的区别。。。
0.生命周期不一样,守护进程是开始运行到系统停止;&生命周期是父进程停止/退出,比如你退出终端。
1.守护进程已经完全脱离控制台了,后台程序并未完全脱离,终端未关闭之前还会往终端输出结果。
2.守护进程属于系统,与交互无关,sysadmin(系统管理员). 普通进程属于特定用户,使用nohup模拟守护进程。
3.守护进程的会话组、当前目录、文件描述符都是独立的,后台运行只是终端进行了一次fork,

查看守护进程: ps -axj,
普通进程达到守护进程的效果: nohup &

创建守护进程的过程:

  1. 调用fork创建子进程。父进程终止,让子进程在后台继续执行。
  2. 子进程调用setsid产生新会话期并失去控制终端调用setsid()使子进程进程成为新会话组长和新的进程组长,同时失去控制终端。
  3. 忽略SIGHUP信号。会话组长进程终止会向其他进程发该信号,造成其他进程终止。
  4. 调用fork再创建子进程。子进程终止,子子进程继续执行,由于子子进程不再是会话组长,从而禁止进程重新打开控制终端。
  5. 改变当前工作目录为根目录。一般将工作目录改变到根目录,这样进程的启动目录也可以被卸掉。
  6. 关闭打开的文件描述符,打开一个空设备,并复制到标准输出和标准错误上。 避免调用的一些库函数依然向屏幕输出信息。
  7. 重设文件创建掩码清除从父进程那里继承来的文件创建掩码,设为0。8. 用openlog函数建立与syslogd的连接。

进程组: 同一进程组中的各进程接收来自同一终端的各种信号
会话:(session)一个或多个进程组的集合,进程调用setsid函数创建一个会话。

进程间的制约关系:进程同步(进程合作 数据准备阻塞) 进程互斥(对临界区资源的操作)
进程互斥的方法:
(1)硬件方法,关闭CPU中断(顺序执行了); 屏蔽中断-执行临界区-打开中断(2)软件方法,信号量,PV操作(P表示占有,V表示释放),S表示资源个数,检测S是否小于0,对于P,小于0时P操作阻塞,大于0时P占有资源执行;对于V,小于0时唤醒等待使用S资源的其它进程。
生产者消费者问题(经典的进行同步问题,既有同步关系(只有生产了才能消费),也有互斥关系(临界区的访问),用到了信号量处理缓冲区)

有了进程,为什么还需要线程?
线程存在的必要性:
1.线程切换的成本低,进程切换是一个开销很大的操作;
2.线程更轻量,一个进程可以创建很多个线程;
3.多个线程可以并发处理不同任务,充分利用CPU资源;进程只能同一个干一件事。
4.同一进程中多线程共享内存和文件,无需通信无需调用内核,很方便。

2.线程(thread),轻量级进程,讲究轻量。多个线程可以在同一个进程中同时执行,多个线程间共享进程资源(内存空间、文件句柄、网络连接),进程里的不同子线程各自独立处理不同子任务。
CPU调度和分配的基本单位,线程没有自己的资源,和其它线程共享进程的资源。
同一进程中的多个线程可能会互相影响。
每个线程拥有的:自己的程序计数器,拥有自己的栈空间,拥有独立的执行序列。
线程:
线程共享:同一进程的多个线程共享进程的堆和方法区资源、常量,
共享 堆:堆是进程中最大的一块内存,所有对象分配内存都需要。
共享方法区: 存放新创建的类 对象 静态方法 常量。

线程独自拥有的:线程自己的本地方法栈、程序计数器和虚拟机栈。
程序计数器:1.改变程序计数器 来依次读取指令,实现代码的流程控制;2.多线程时,记录当前线程执行到哪,当前线程被切换回来时 能够从上次运行的位置继续执行。
线程自己的栈:局部变量 本地方法,保证不被其它线程影响。

协程(CoRoutine)
轻量级线程,微线程,协程是线程里不同的函数,这些函数之间可以相互快速切换
和用户态线程非常接近,不需要陷入内核,
协程之间的切换和过程由编程人员决定,用户态操作。适合I/O密集型任务,(因为协程不能很好的利用多核CPU)
——
开多线程 最好的期待就是跑满CPU, 充分利用资源 提高效率。。
———-
多线程:通过CPU给每个线程分配时间片 来实现。时间片一般非常短,CPU不停的切换线程执行,时间片一般几十毫秒,给人感觉是多个线程在同步执行。
任务从保存到加载的过程就是一次上下文切换,上下文切换影响多线程的执行速度。(并发)

进程切换 和 线程切换。(都通过操作系统内核来完成)
线程切换:单核cpu来说,cpu一个时刻只运行一个线程,运行一个线程过程中转去运行另一个线程,这就叫做线程上下文切换。(进程类似) 进程切换的消耗大于线程。
暂停线程a,保存a的状态,转去执行线程b,再切回来时a能继续执行。
线程切换:虚拟内存空间仍然是相同的,由调度器来完成。(1)虚拟内存映射 和 处理器状态切换到新线程, (2)前一个线程的寄存器、栈信息还有其它状态信息被保存,(3)新线程的栈信息和寄存器信息被恢复。
linux:一个task_struct结构体代表一个线程,用红黑树存所有可运行的线程,每次从红黑树中取一个执行

并发:多个任务交替进行,并行才是真正意义的同时进行。(单核CPU只能并发,不能并行。并行需要多核)

线程状态
1.初始状态start后,是可运行状态ready;
2.可运行ready状态获得CPU时间片后是running状态;
3.线程执行wait方法后,线程进入等待状态。等待状态的线程需要其它线程通知才能返回到运行状态(notify)这里可以引入超时机制,超时后自动回到运行状态.
4.多线程同步时,没有锁时会进入阻塞状态blocked.
5.运行完时终止状态。

sleep 和 wait的区别:都可以让线程进入等待状态。sleep没有释放锁,wait释放了锁。sleep可以自己唤醒线程,wait需要等其它线程来notify唤醒。sleep作用于thread,wait作用于整个对象。
sleep用的多。

多线程中阻塞问题
多线程访问临界区资源时,某一线程占用,其它线程等待挂起 不能工作。
解决方案:
1.减少锁的持有时间,
2.读写锁分离(读锁情况下,可以并发访问;写锁时按顺序访问)
3.减小锁的粒度(尽可能少的请求锁,比如对类的某个变量使用锁,而不是对整个类使用锁)

多线程中活跃性问题:
死锁: (线程因某些原因一直获取不到资源,程序一直无法执行),等待永远不会释放的资源,都处于等待状态。 原因优先级太低,或者资源一直没被释放。设置合理的优先级。
死锁如何产生
多个进程因竞争资源而造成的一种僵局(互相等待),无外力作用,这些进程都无法向前推进。
产生的4个必要条件:(1)(资源,不可剥夺)互斥,(2)持有并等待,因请求资源而阻塞,对已获得资源保持不放(3)不可剥夺,已获得的资源未使用完之前不可剥夺(4)环形等待(资源申请释放顺序不对,信号量使用不当,全部堵塞),首尾相接的循环等待关系。
避免死锁的方法(破坏死锁产生的必要条件):(1)按照特定的顺序(设置编号id)访问资源,按照编号顺序来。(2)设置锁的超时时间(访问时限放弃对锁的请求,并放弃自己的锁)。
(3)破坏持有并等待的条件,进程不可以持有资源时还去申请其它资源。申请新资源时释放原有资源。

饥饿和饿死:等待会被释放的资源,但是却不会分配给自己的资源,
相同点:都是资源竞争引起的。

活锁:过于谦让了,资源在线程之间一直跳来跳去。而导致某一线程无法高效的执行。一直在尝试+失败。不存在阻塞。问题: 活锁会耗尽CPU资源,解决方法:暂停重试。

使用多线程 会遇到的问题:
1.线程安全问题: 数据被其它线程修改,数据缺失 数据不一致。
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
需要线程同步(代码加锁)
线程同步的4种方式:互斥锁(pthread_mutex)、读写锁(pthread_rwlock_)、条件变量、信号量
(1)mutex(互斥锁):每个线程对共享资源进行操作时先尝试加锁,加锁成功后才能操作,操作结束后解锁;其它线程加锁失败则阻塞,睡眠等待,节省cpu资源。

自旋锁是互斥锁的一种实现方式(一般的互斥锁在等待期间放弃cpu,自旋锁一直占用着)
自旋锁应用场景:多核CPU场景下,大量线程只会短时间持有锁的时候。
自旋锁(spin lock): 当一个进程在获取锁的时候,如果锁已被其它进程获取,该进程就会循环等待,不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。(获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。)
自旋锁的优点: 不会上下文切换,不会阻塞,一直活跃状态,执行速度快。一直是用户态。
缺点:CPU消耗会比较高。

(2)rwlock(读写锁):将操作分为读和写,可以多个线程共同占用读模式,提高并行能力;适合读多写少的场景。写独占:写锁占用时,其它读写操作都会堵塞;读共享:其它写操作会堵塞,读成功。
(3)条件变量:条件变量本质上也是一个多线程间共享的全局变量,它的功能是阻塞线程,被阻塞的线程直到接收到“条件成立”的信号后才能继续执行。 (比如线程池+消息队列) 生产者生产数据时唤醒消费者去处理消费;队列满时通知生产者不能再生产了。
由上可知,线程池由3部分组成:
**(1) 任务队列(Task Queue):**存储需要处理的任务,由工作者线程来处理这些任务。
①通过线程池提供的API函数,将任务添加到任务队列或者从任务队列中删除任务。
②已处理的任务会从任务队列中删除。
③线程池的使用者,也就是往任务队列中添加任务的线程就是生产者线程。
(2) 工作者线程(任务队列任务的消费者,N个)
①线程池中维护了一定数量的工作者线程,他们的任务是不断的读取任务队列中的任务,并且取出执行。
②工作的线程相当于任务的消费者角色。
③如果任务队列为空,工作者线程会被阻塞。(使用条件变量/信号量阻塞)
④一旦任务队列有任务了,由生产者将任务队列解除,工作者线程开始工作。
(3) 管理者线程
①它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作者线程个数进行检测。
②当任务数量过多时,可以适当的创建一些新的工作线程。
②当任务过少时,可以适当的销毁一些工作的线程。

2.性能问题:线程创建到销毁会消耗大量内存,线程数量创建要合理。
解决方案:使用线程池(避免频繁创建线程和销毁线程)数量合理,线程都处于忙碌状态,提高执行效率。过多的任务可以拒绝。
线程池(thread pooling): 重复利用已创建的线程,来降低线程创建和销毁造成损耗;任务到达时,任务利用线程可以立即执行;方便管理线程。

通过锁机制,保证在多核多线程环境中,某一个时间点上,只能有一个线程进入临界区代码,保证临界区中操作数据的一致性。(锁可以看做一个整数,拥有2种状态:空闲状态和上锁状态,)
(临界区:用来访问临界资源的代码,临界资源:一个只允许一个进程独占访问的资源。进程管理中的概念。忙则等待,空闲让进,有限等待,让权等待(等待进程放弃CPU,让其他进程有机会得到CPU))


操作系统的内存管理
1.内存分配和回收
进程所需内存进行分配和释放,malloc、free,new,delete等,
内存碎片:内存申请和释放产生的。
1.内部内存碎片:分配内存比实际使用的要大。按2的n次方来分配。
2.外部内存碎片:空间太小,没法使用。比如:分段机制。
内存管理方式:
1.连续内存管理,分配连续内存空间,内存使用率会很低。
(1)Linux中使用的伙伴系统(Buddy System)算法:有效解决外部内存碎片的问题, 每次找到大小最合适的内存空间,2的n次方,过大的话会切分。会把相邻释放的空间合并成一个使用。减少内存碎片,提高利用率。
(2)内部内存碎片的问题,Linux 采用 SLAB 进行解决。

2.非连续内存管理:会灵活一些,内存分布零散。
(1)段式管理 : 以段(—段连续的物理内存)的形式管理/分配物理内存。应用程序的虚拟地址空间被分为大小不等的段,段是有实际意义的,每个段定义了一组逻辑信息,例如有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。
(2) 页式管理 : 把物理内存分为连续等长的物理页,应用程序的虚拟地址空间划也被分为连续等长的虚拟页 ,现代操作系统广泛使用的一种内存管理方式。
(3) 段页式管理机制 : 结合了段式管理和页式管理的一种内存管理机制,把物理内存先分成若干段,每个段又继续分成若干大小相等的页。


虚拟内存(Virtual Memory,VM), 简化内存管理。 搭建一个进程访问主存的桥梁。
内存管理非常重要的一个技术,逻辑存在的,是一个假想出来的内存空间,主要作用是作为进程访问主存(物理内存)的桥梁并简化内存管理。
在这里插入图片描述
虚拟内存的作用:
(1)隔离进程:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
(2)提高了物理内存利用率: 有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
(3)简化内存管理: 进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
(4)提高内存使用安全性: 控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
(5)提供更大的可使用内存空间 : 可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。

没有虚拟内存的话,会出现什么问题?
(1)用户程序直接访问任意物理内存,可能会不小心操作到系统运行必需的内存,进而造成操作系统崩溃,严重影响系统的安全。
(2)同时运行多个程序会崩溃。操作到同一个地址。
(3)程序运行中的很多数据指令都要写入物理内存,很多可能都不会用到,浪费资源空间。

2.地址转换
(1)物理地址(Physical Address) 是真正的物理内存中地址,更具体点来说是内存地址寄存器中的地址。
(2)程序中访问的内存地址不是物理地址,而是 虚拟地址(Virtual Address)

编程开发的时候实际就是在和虚拟地址打交道。

(1)虚拟地址空间: 是虚拟地址的集合,是虚拟内存的范围。每一个进程都有一个一致且私有的虚拟地址空间。
(2)物理地址空间: 是物理地址的集合,是物理内存的范围。

CPU 芯片中的一个重要组件 MMU(Memory Management Unit,内存管理单元) 将虚拟地址转换为物理地址,这个过程被称为 地址翻译/地址转换(Address Translation)
在这里插入图片描述通过 MMU 将虚拟地址转换为物理地址后,再通过总线传到物理内存设备,进而完成相应的物理内存读写请求。
MMU 将虚拟地址翻译为物理地址的主要机制有两种: 分段机制 和 分页机制 。

分段机制:
分段管理通过 段表(Segment Table) 映射虚拟地址和物理地址。
在这里插入图片描述

分段机制下的虚拟地址由两部分组成:段号 + 段内偏移量
段号 :标识着该虚拟地址属于整个虚拟地址空间中的哪一个段。
段内偏移量 :相对于该段起始地址的偏移量。

翻译过程如下:
(1)MMU 首先解析得到虚拟地址中的段号;
(2)通过段号去该应用程序的段表中取出对应的段信息(找到对应的段表项);
(3)从段信息中取出该段的起始地址(物理地址)加上虚拟地址中的段内偏移量得到最终的物理地址。

分页机制:
把主存(物理内存)分为连续等长的物理页,应用程序的虚拟地址空间划也被分为连续等长的虚拟页。现代操作系统广泛采用分页机制!!!!。

应用程序虚拟地址空间中的任意虚拟页可以被映射到物理内存中的任意物理页上,因此可以实现物理内存资源的离散分配。分页机制按照固定页大小分配物理内存,使得物理内存资源易于管理,可有效避免分段机制中外部内存碎片的问题

页表(Page Table) 映射虚拟地址和物理地址
每个应用程序都会有一个对应的页表。
虚拟地址: 页号 + 页内偏移量。
页号 : 通过虚拟页号可以从页表中取出对应的物理页号;
页内偏移量 : 物理页起始地址+页内偏移量=物理内存地址。

具体的地址翻译过程如下:
(1)MMU 首先解析得到虚拟地址中的虚拟页号;
(2)通过虚拟页号去该应用程序的页表中取出对应的物理页号(找到对应的页表项);
(3)用该物理页号对应的物理页起始地址(物理地址)加上虚拟地址中的页内偏移量得到最终的物理地址。

存在页缺失,虚拟页号不一定都能找到对应的物理页号。

多级页表: 时间换空间。 增加页表的查询次数,减少页表的占用空间。
申请的页表可能有很多都没用,白白浪费。
多级页表 ,多级页表对应多个页表,每个页表也前一个页表相关联。32 位系统一般为二级页表,64 位系统一般为四级页表。按需加载,节省空间占用 多了个页表页号。
多级页表保存的信息: 页表页号 + 页号 + 页内偏移量。

TLB: 提高页表的虚拟地址转换物理地址的速度。
(Translation Lookasjde Buffer,TLB,转址旁路缓存,也被称为快表)。
在这里插入图片描述
TLB 属于 (Memory Management Unit,内存管理单元) 内部的单元,本质上就是一块高速缓存(Cache),缓存了虚拟页号到物理页号的映射关系,你可以将其简单看作是存储着键(虚拟页号)值(物理页号)对的哈希表。

使用 TLB 之后的地址翻译流程是这样的:
(1)用虚拟地址中的虚拟页号作为 key 去 TLB 中查询;
(2)如果能查到对应的物理页的话,就不用再查询页表了,这种情况称为 TLB 命中(TLB hit)。
(3)如果不能查到对应的物理页的话,还是需要去查询主存中的页表,同时将页表中的该映射表项添加到 TLB 中,这种情况称为 TLB 未命中(TLB miss)。
(4)当 TLB 填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。

3.内存扩充

换页机制:
当物理内存不够用的时候,操作系统选择将一些物理页的内容放到磁盘上去,等要用到的时候再将它们读取到物理内存中。也就是说,换页机制利用磁盘这种较低廉的存储设备扩展的物理内存。
(时间换空间的策略。)

页面缺失:(Page Fault,又名硬错误、硬中断、分页错误、寻页缺失、缺页中断、页故障等)
软件试图访问已映射在虚拟地址空间中,但是目前并未被加载在物理内存中的一个分页时,由 MMU 所发出的中断。
页缺失有2种情况:
1.硬性页缺失:物理内存中没有对应的物理页。
解决方法:CPU 从已经打开的磁盘文件中读取相应的内容到物理内存,而后交由 MMU 建立相应的虚拟页和物理页的映射关系。
2.软性页缺失:物理内存中有对应的物理页,但虚拟页还未和物理页建立映射。
解决方法:会指示 MMU 建立相应的虚拟页和物理页的映射关系
以上访问的都是有效的物理内存。
无效缺页错误:应用程序访问的是无效的物理内存。

页面置换算法:(当发生硬性页面缺失时,物理内存中没有空闲物理页面可用,就必须将物理内存中的一个物理页淘汰出去,这样就可以腾出空间来加载新的页面了。)

用来选择淘汰哪个物理页面的规则(页面置换算法)。
一个好的页面置换算法应该是可以减少页缺失出现的次数。
(1)最佳页面置换算法(OPT,Optimal) 目前无法实现这个算法。
优先选择淘汰的页面是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现,只是理论最优的页面置换算法,可以作为衡量其他置换算法优劣的标准。

(2)先进先出页面置换算法(FIFO,First In First Out): 最简单的一种页面置换算法, 总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。该算法易于实现和理解,一般只需要通过一个 FIFO 队列即可需求。不过,它的性能并不是很好。

(3)最近最久未使用页面置换算法(LRU ,Least Recently Used) LRU 算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 T,当须淘汰一个页面时,选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰。LRU 算法是根据各页之前的访问情况来实现,因此是易于实现的。 (l实际引用比较多的算法!!) 最接近OPT,实际用的时候都会做一些改进。
(4)最少使用页面置换算法(LFU,Least Frequently Used) : 和 LRU 算法比较像,不过该置换算法选择的是之前一段时间内使用最少的页面作为淘汰页。
(5)时钟页面置换算法(Clock) :可以认为是一种最近未使用算法,即逐出的页面都是最近没有使用的那个。

FIFO页面置换算法性能不好的原因:
(1)经常访问或者需要长期存在的页面会被频繁调入调出 :较早调入的页往往是经常被访问或者需要长期存在的页,这些页会被反复调入和调出。
(2)存在 Belady 现象 :被置换的页面并不是进程不会访问的,有时就会出现分配的页面数增多但缺页率反而提高的异常现象。出现该异常的原因是因为 FIFO 算法只考虑了页面进入内存的顺序,而没有考虑页面访问的频率和紧迫性。

分页机制和分段机制的相同点和不同点:
相同点:
(1)都是非连续内存管理的方式。
(2)都采用了地址映射的方法,将虚拟地址映射到物理地址,以实现对内存的管理和保护。
不同点:
(1)分页机制以页面为单位进行内存管理,而分段机制以段为单位进行内存管理。页的大小是固定的,由操作系统决定,通常为 2 的幂次方。而段的大小不固定,取决于我们当前运行的程序。
(2)页是物理单位,即操作系统将物理内存划分成固定大小的页面,每个页面的大小通常是 2 的幂次方,例如 4KB、8KB 等等。而段则是逻辑单位,是为了满足程序对内存空间的逻辑需求而设计的,通常根据程序中数据和代码的逻辑结构来划分。
(3)分段机制容易出现外部内存碎片,即在段与段之间留下碎片空间(不足以映射给虚拟地址空间中的段)。分页机制解决了外部内存碎片的问题,但仍然可能会出现内部内存碎片。
(4) 分页机制采用了页表来完成虚拟地址到物理地址的映射,页表通过一级页表和二级页表来实现多级映射;而分段机制则采用了段表来完成虚拟地址到物理地址的映射,每个段表项中记录了该段的起始地址和长度信息。
(5) 分页机制对程序没有任何要求,程序只需要按照虚拟地址进行访问即可;而分段机制需要程序员将程序分为多个段,并且显式地使用段寄存器来访问不同的段。

局部性原理:
局部性原理是指在程序执行过程中**,数据和指令的访问存在一定的空间和时间上的局部性特点**。其中,
(1)时间局部性是指一个数据项或指令在一段时间内被反复使用的特点,
(2)空间局部性是指一个数据项或指令在一段时间内与其相邻的数据项或指令被反复使用的特点。
分页机制,利用局部性原理,采用缓存(时间局部性)和预取技术(用预取技术来预先将相邻的一些页读入内存缓存中,空间局部性),可以提高页面的命中率,从而提高内存访问效率

4.内存映射
5.内存优化
6.内存安全


文件系统
1.存储管理:文件数据存储到物理存储介质中,并且管理空间分配,以确保每个文件都有足够的空间存储,并避免文件之间发生冲突。
2.文件管理 :文件的创建、删除、移动、重命名、压缩、加密、共享等等。
3.目录管理 :目录的创建、删除、移动、重命名等等。
4.文件访问控制 :管理不同用户或进程对文件的访问权限,以确保用户只能访问其被授权访问的文件,以保证文件的安全性和保密性。

软连接和硬链接
文件链接是一个特殊的文件类型,在文件系统中指向另一个文件。
每个文件和目录都有一个唯一的索引节点号(inode),
1.硬链接,通过inode节点互为链接,两者地位相等,相等于文件备份,源头是同一个文件。删除一个,对另一个文件没有影响。
ln:创建硬链接。
硬链接不能跨越文件系统。

2.软链接:又叫符号链接(SymLink)
软连接指向文件路径。
ln -s src dst, 在dst建立指向src的文件。(ln指令为一个文件在另一个位置建立一个同步的链接) 就是实现软连接。
使用场景:在不同目录 用到相同文件时,使用ln指向。节省磁盘空间。 文件修改也方便(保持同步)
软连接可以跨越文件系统。

硬链接为什么不能跨越文件系统?
1.硬链接是通过 inode 节点号建立连接的,而硬链接和源文件共享相同的 inode 节点号。
2.然而,每个文件系统都有自己的独立 inode 表,且每个 inode 表只维护该文件系统内的 inode。如果在不同的文件系统之间创建硬链接,可能会导致 inode 节点号冲突的问题,即目标文件的 inode 节点号已经在该文件系统中被使用。

磁盘调度算法: (对磁盘访问请求进行排序和调度的算法,其目的是提高磁盘的访问效率。)

一次磁盘读写操作的时间由磁盘寻道/寻找时间、延迟时间和传输时间决定。磁盘调度算法可以通过改变到达磁盘请求的处理顺序,减少磁盘寻道时间和延迟时间。

1.先来先服务算法(First-Come First-Served,FCFS) : 按照请求到达磁盘调度器的顺序进行处理,先到达的请求的先被服务。FCFS 算法实现起来比较简单,不存在算法开销。不过,由于没有考虑磁头移动的路径和方向,平均寻道时间较长。同时,该算法容易出现饥饿问题,即一些后到的磁盘请求可能需要等待很长时间才能得到服务。
2.最短寻道时间优先算法(Shortest Seek Time First,SSTF) :也被称为最佳服务优先(Shortest Service Time First,SSTF)算法,优先选择距离当前磁头位置最近的请求进行服务。SSTF 算法能够最小化磁头的寻道时间,但容易出现饥饿问题,即磁头附近的请求不断被服务,远离磁头的请求长时间得不到响应。实际应用中,需要优化一下该算法的实现,避免出现饥饿问题。
3.扫描算法(SCAN) : 也被称为电梯(Elevator)算法,基本思想和电梯非常类似。磁头沿着一个方向扫描磁盘,如果经过的磁道有请求就处理,直到到达磁盘的边界,然后改变移动方向,依此往复。SCAN 算法能够保证所有的请求得到服务,解决了饥饿问题。但是,如果磁头从一个方向刚扫描完,请求才到的话。这个请求就需要等到磁头从相反方向过来之后才能得到处理。
4.循环扫描算法(Circular Scan,C-SCAN) : SCAN 算法的变体,只在磁盘的一侧进行扫描,并且只按照一个方向扫描,直到到达磁盘边界,然后回到磁盘起点,重新开始循环。
5.边扫描边观察算法(LOOK) : SCAN 算法中磁头到了磁盘的边界才改变移动方向,这样可能会做很多无用功,因为磁头移动方向上可能已经没有请求需要处理了。LOOK 算法对 SCAN 算法进行了改进,如果磁头移动方向上已经没有别的请求,就可以立即改变磁头移动方向,依此往复。也就是边扫描边观察指定方向上还有无请求,因此叫 LOOK。
6.均衡循环扫描算法(C-LOOK) : C-SCAN 只有到达磁盘边界时才能改变磁头移动方向,并且磁头返回时也需要返回到磁盘起点,这样可能会做很多无用功。C-LOOK 算法对 C-SCAN 算法进行了改进,如果磁头移动的方向上已经没有磁道访问请求了,就可以立即让磁头返回,并且磁头只需要返回到有磁道访问请求的位置即可。

提高文件系统性能的方法:
1.优化硬件 :使用高速硬件设备(如 SSD、NVMe)替代传统的机械硬盘,使用 RAID(Redundant Array of Inexpensive Disks)等技术提高磁盘性能。
2.选择合适的文件系统选型 :不同的文件系统具有不同的特性,对于不同的应用场景选择合适的文件系统可以提高系统性能。
3.运用缓存 :访问磁盘的效率比较低,可以运用缓存来减少磁盘的访问次数。不过,需要注意缓存命中率,缓存命中率过低的话,效果太差。
4.避免磁盘过度使用 :注意磁盘的使用率,避免将磁盘用满,尽量留一些剩余空间,以免对文件系统的性能产生负面影响。
5.对磁盘进行合理的分区 :合理的磁盘分区方案,能够使文件系统在不同的区域存储文件,从而减少文件碎片,提高文件读写性能。


网络管理

IO相关:
1.文件描述符:linux将所有设备都当做文件来处理,文件描述符来标识每个文件对象。打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
2.缓存IO:Linux会将IO的数据缓存到文件系统的页缓存中,然后再从缓冲区中拷贝到应用程序地址空间。
(1)等待数据准备。
(2)将数据从内核拷贝到缓冲区。

3.linux下 IO的多种模式:
(1) 阻塞IO(block IO), IO执行的两个阶段都会被阻塞。(用户进程一直等待。)
(2) 非阻塞IO(non-block IO), 用户进程需要不断的主动询问kernel数据准备好了没有。
(3) IO多路复用,本质都是同步IO,都需要在读写事件就绪后自己负责进行读写,这个读写过程其实是阻塞的。(又叫时间驱动IO, event driven IO 或者 select, poll, epoll), 单个process可以同时处理多个网络连接的IO. 相当于process 和 大量的IO事件或设备或流 之间,的 一个中间件(也就是select,poll, epoll),不断地轮询所负责的所有socket,某个socket有数据到达了,就通知用户进程。
select: 整个进程会被阻塞,kernel监视所有select绑定的socket,任意一个数据准备好了,select就返回。
多路复用的优势在于处理大量连接。。

select: 监视文件描述符,调用后select会阻塞,select返回后遍历文件描述符,找到就绪的描述符;(能监视的文件数量有最大限制1024)
poll:没有最大限制,数量增加后性能下降,返回后都需要轮询找到就绪的描述符。
epoll:也没有描述符限制,使用一个文件描述符管理多个描述符(底层采用链表、红黑树管理事件) 描述符越多,性能提升越明显
epoll只遍历触发io事件的就绪描述符

(4)异步IO,用户进程发起read操作后,立刻去做别的事,不会阻塞;对于kernel,收到read请求后,也会立刻返回,不会对用户进程产生阻塞。kernel等数据准备完成,将数据拷贝到用户内存;等一切完成后,kernel给用户发送一个信号,告诉用户read操作完成了。

服务器过载:访问量过大,超过服务器的处理能力。
1.采用轮询机制。
2.减少中断的频率,(批中断和暂时关闭中断。批中断可以在超载时有效的抑制活锁现象,但对服务器的性能没有什么根本性的改进;当系统出现接收活锁迹象时,可以采用暂时关闭中断的方法来缓和系统的负担,当系统缓存再次可用时可以再打开中断,但这种方法在接收缓存不够大的情况下会造成数据包丢失。)
3.减少上下文切换。(核心级数据流是将数据从源通过系统总线进行转发而不需要使数据经过应用程序进程,这个过程中因为数据在内存中,因此需要CPU操作数据。)
4.扩容。

linux,比windows安全:
linux开源,解决漏洞的人多,默认不是管理员权限。危害范围小。
windows用户量大,容易被攻击,不开源,默认管理员权限,一旦侵入,可以做很多事情。

volatile: 有什么用?? 原理?? 干啥的?
变量类型修饰符(类似于const), 告诉编译器,volatile修饰的变量不要执行优化,从而可以提供对特殊地址的稳定访问。多线程编程中常用。(每次都从变量的地址中重新读取(稳定访问),优化的做法是: 前后如果连续读取时,直接把上次读到的结果拿来用。。)
多线程,共享基本变量要用volatile修饰,就不会保存到寄存器内,而是放到一个共享空间中,每次都去访问这个变量的地址空间内容。
(加锁,保护临界区内的数据)
volatile使用场景: 1。多任务环境中,共享的标志要加volatile;

重排序:编译器和处理器为了优化程序性能,对指令序列进行重新排序,可能会改变程序结果。(为了提高编译器和处理器的并行度,对于单线程的程序没有影响) (就可以使用volatile来避免。。)

linux指令:
(1) ps (process status), 进程状态指令。列出系统中当前运行的进程(执行ps指令的那个时刻),是进程的快照。 (想实时显示,可以用top)
(可以查看进程执行的状态,是否结束,有没有僵死,占用资源的情况。。)
-A:显示所有进程,
-e(作用等同于-A)
e : 显示环境变量
-aux:显示包含其它使用者的进程,
u:指定用户的进程。
-H: 显示树状结构。
f:显示程序之间的关系。
pstree, 以树结构显示进程,
ps -ef,显示所有进程信息,连同命令行。
(2)kill
kill -9 , 从内核级别,强制杀死进程。
kill -15, 操作系统通知进程,主动关闭。

(3)lsof(list open files),列出当前系统打开文件的工具。linux系统中,任何事物都以文件形式存在,通过文件不仅可以访问常规数据,还可以访问网络连接和硬件。文件描述符,包含大量信息。

lsof filename, 显示打开指定文件的所有进程
lsof -i(网络连接相关)可以查看指定端口有哪些进程在用。

(3)linux修改权限, chmod

https://blog.csdn.net/runrun117/article/details/80291397 线程-和进程。。

(4) nestat: 查看各种网络相关信息,网络连接,路由表,接口状态, -a显示所有选项。 -t显示tcp,-u显示udp。

(5) 查看资源消耗。(一般就cpu 和 内存)
内存:free, 查看cpu:ps -aux, 查看总体的用top。

文件系统 底层原理:
https://blog.csdn.net/bv1315008634/article/details/53327002


https://www.douban.com/note/790232447/
进程资源共享:(进程间可以资源共享。)
比较典型的就是文件系统, 剪切板。
两个进程再怎么隔离,只要有共同的中间人,就可以通信。中间人就是共享资源。
共享内存、 管道、 消息队列,都属于资源共享的一种方式。


临界区: 进程中访问临界资源的那段程序,叫做临界区。(临界资源是一次仅允许一个进程使用的共享资源)。
进程间同步:并发进程,因直接制约互相发送消息进行互相合作、互相等待,各进程按一定速度执行的过程。
!!!!进程同步互斥的主要方法:
(1)信号量, (2)P,V原语, (3)生产者消费者模式


linux下用户模式和内核模式:
微软-dos系统在单一cpu模式下运行,
类unix的操作系统使用 双模式, 可以有效实现时间共享。cpu要么处于受信任的内核模式,要么处于受限制的用户模式。
内核本身处于内核模式,所有用户进程都运行在用户模式。 内核模式的代码可以无限制访问cpu指令集和全部IO和全部内存;
用户模式的进程要享有内核的特权,必须通过!!系统调用!!向设备驱动程序或其它内核模式的代码发出请求。

作为程序员。只需要用代码 关注内存申请和释放即可,(因为虚拟内存的存在)
操作系统为每个进程提供一个抽象的虚拟内存,属于进程自己的、私有的、地址连续的虚拟内存,
操作系统的页表,记录虚拟内存到物理内存的地址映射。

linux:物理内存不够用时,把一部分不使用的内存数据放到磁盘swap,腾出更多的可用内存空间。
swap占比过高,说明内存空间不足。

我们用的操作系统 都是 分时操作系统,一个时间段只能运行一个进程,然后切换,切换速度足够快时,看起来就像所有进程同时运行一样。(进程切换要保留一些必要信息,比如寄存器)

进程的并发运行,一个进程的指令和另一个进程的指令是交错执行的(通过处理器在进程间切换来实现的,操作系统实现的这种交错执行的机制叫做上下文切换,进程运行所需的所有状态信息就是上下文,例如pc和寄存器当前值,主存内容等)
操作系统决定把控制权从当前进程转移到新进程时,就会进行上下文切换,保存上下文,新进程从上次停止的点继续运行。

进程切换(要切换虚拟内存空间) 比 线程切换(共享虚拟内存空间)耗时。

多线程:cpu调度,每个时间片只有一个线程执行。(同一个进程中多个线程同时执行)

线程的出现: 为了把两个功能区分开, 1.资源所有权(进程), 2.执行和调度(线程)
多线程并发:提高硬件资源利用率,在系统层面做到并发,线程上下文切换效率比进程高很多,

进程为了隔离程序,保证数据和程序运行安全;线程为了并发高效。

适用场景:
对于一些要求同时进行,并且又要共享某些变量的并发操作,只能用多线程,不能用多进程。
一个进程有独立的地址空间,一个进程崩溃后,不会影响其它进程。进程切换,资源耗费巨大,效率极差。多进程更健壮。
一个线程死掉后,整个进程都要死掉。(一个线程,只是进程中一个不同的执行路径)
线程的划分尺度小于进程,多线程的程序并发性更高。
多线程共享内存,极大提高了程序运行效率。

????
线程同步、进程同步,多线程共享内存哪些东西??进程通信方式。。共享内存在哪(进程通信的一种方式。。)
资源共享: 堆、全局变量、地址空间、
线程独享:栈、寄存器、计数器

https://blog.csdn.net/weixin_41413441/article/details/80548683
linux线程互斥和同步:
互斥:无序的访问,唯一性和排他性;某一资源只允许一个访问者对其进行访问;
同步:在互斥基础之上,通过一些机制实现访问者对资源的有序访问。(同步是一种更复杂的互斥,在互斥基础之上,利用wait、notify,等待、通知机制实现同步。线程之间的一种制约关系,当他没有得到另一个线程的消息时,应等待,直到消息到达时才被唤醒)

互斥锁是在原子操作API上实现的信号量行为,互斥锁是信号量的特例(信号量初始值为1,即为互斥锁),
互斥锁的加锁和解锁,只能在同一个线程里对应使用,互斥锁只能用于线程的互斥(锁住一些资源,对临界区进行保护);
信号量可以由一个线程释放,另一个线程得到,所以信号量也可以用于线程的同步。
https://blog.csdn.net/m0_37962600/article/details/81448553(缺页中断原因、和处理措施)

协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制。异步机制,可以保留上一次调用时的状态,在进程和线程之上的抽象(一组代码逻辑),比如python。。 (一个线程可以有多个协程;一个进程也可以有多个协程,完全用户控制)

线程池:
创建一些线程的集合,这个集合叫做线程池,线程池拿到任务 查找空闲的线程执行,执行完继续空闲。等待下一个任务。 大大提升系统性能。
4种常见线程池:
1.可缓存 线程池
2. 周期性执行任务的线程池
3. 只有一条线程来执行任务,适合有顺序的任务的应用场景
4. 定长线程池,

构建项目用到了 fork:------------
C++的fork:复制一份主程序,主进程创建一个子进程,副本进程,但是pid不同。

————-
操作系统 锁的类型:
锁是 保证 多线程同步的一种方式。
互斥锁:任何时候只有一个线程在执行其中代码。(多个线程访问共享数据是串行的,所以互斥锁粉效率很低)(读写都是串行)

读写锁:读是并行的, 写是串行的(适合读操作比较频繁的场景)
两种方式:共享 和 独占,

信号量, 二元信号量,互斥锁是二元信号量取值0/1时的特例,
临界区,用户态的锁,更快,好像linux用不了。

epoll, select, poll。。。

http状态码。。

异步回调

线程同步, 信号量和互斥量的区别。。

多路复用。。

多个TCP连接,复用指复用一个或少量线程,很多网络I/O复用一个或少量的线程来处理这些连接。
都是异步的事件驱动的网络模型,

—————
TCP 流量控制 和 拥塞控制
1.流量控制
防止发送数据过快 ,接收方来不及接收。
TCP滑动窗口,就是进行流量控制的方式。接收方设置滑动窗口大小(单位字节),发送方不得超过该大小。(发送滑动窗口 ACK=1)

2.拥塞控制
对资源的需求 超过了可以供应的资源, 网络中很多资源不足,网络性能就会明显变差,
防止过多数据注入到网络中,防止网络过载。
拥塞控制是一个全局的工作,主机 交换机等,都要考虑。涉及整个网络性能。(前提要知道网络中流量分布情况)
常用方法:慢开始、 拥塞避免、 快重传、 快恢复。。

流量控制:只是点对点的,两端之间的问题,抑制发送端的发送速率。使接收端来得及接收。

1。epoll(linux)
任务队列中的任务,需要主动触发执行,

  1. iocp。(window)
    客户端发送过去到队列中,IOCP自动执行,通过一个接口获取完成事件通知即可(给子进程。。)

网络相关

!!!!!
socket函数, TCP和UDP发送同样的1M数据到接收端,有啥区别???

socket通信流程:
两个进程能够进行通信的方法:就是在网络中唯一标识一个进程。网络中唯一标识一个进程就是用 ip + 协议 + 端口,标识完两个进程就可以用socket通信了。

socket:套接字,是在应用层和传输层之间的一个抽象层,把tcp/ip复杂的操作流程 抽象成为几个简单的接口,供应用层调用,从而实现进程在网络中的通信。

TCP 字节流,(byte stream)
UDP datagram(数据报)

网络编程中,send函数怎么实现。。阻塞和非阻塞。。
https://www.cnblogs.com/inception6-lxc/p/9152691.html 网络每个层介绍。
TCP 滑动窗口机制, 拥塞机制, 流量控制。。???

阈值怎么设置。????

浏览器输入网址,访问服务器,用的啥协议,详细过程是啥????
1.域名解析协议DNS,将url解析成ip地址,(dns是属于udp协议)(先查dns缓存,缓存没有就请求dns服务器)
2.http协议,得到ip后,浏览器和服务器建立http连接,产生get报文请求,tcp协议处理(后面就一层层往深处去,服务器从底层一直往上解析)

FTP服务器???
底层tcp传输层建立连接, ftp加了一些功能:身份认证、浏览主机目录信息、维护多个tcp连接状态(文件传输结束时关闭,再次传输时重新建立)

ARP协议(地址解析协议),将ip地址转换为mac地址,
RARP协议(反ARP),mac地址转换为ip地址。

TCP的状态转换11种状态?有哪些?time_wait状态。???
(主要是 3次握手、4次挥手中的不同状态)

  1. 七层网络模型,
    从底层到上层: 物理层-数据链路层-网络层(ip协议)-传输层-应用层(会话层,表示层,应用层)。。

网络层(主机之间):
IP协议非常简单,仅仅提供不可靠、无连接的传送服务
1> 网络层负责对子网间的数据包进行路由选择。此外,网络层还可以实现拥塞控制、网际互连等功能;
2> 基本数据单位为IP数据报;
3> 包含的主要协议:
IP协议(Internet Protocol,因特网互联协议);
ICMP协议(Internet Control Message Protocol,因特网控制报文协议);
ARP协议(Address Resolution Protocol,地址解析协议)可看成是跨网络层和链路层的协议;
RARP协议(Reverse Address Resolution Protocol,逆地址解析协议)。
重要设备:路由器。。

传输层(具体的端口,具体应用):TCP、UDP
为应用进程之间提供端到端的逻辑通信。
1> 传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输以及端到端的差错控制和流量控制问题;
2> 包含的主要协议:TCP协议(Transmission Control Protocol,传输控制协议)、UDP协议(User Datagram Protocol,用户数据报协议);
3> 重要设备:网关。

网络层和传输层的区别:
网络层只是根据网络地址将源结点发出的数据包传送到目的结点,而传输层则负责将数据可靠地传送到相应的端口。

应用层:FTP、SMTP、DNS、HTTP、Telnet(远程网络访问协议)

会话层:管理主机之间的会话进程,即负责建立、管理、终止进程之间的会话。会话层还利用在数据中插入校验点来实现数据的同步。

会话层、表示层和应用层重点:
1> 数据传输基本单位为报文;
2> 包含的主要协议:FTP(文件传送协议)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议),POP3协议(邮局协议),HTTP协议(Hyper Text Transfer Protocol)。

  1. tcp, udp。。。ip。。

ip地址分类::
ip地址分网络号和主机号, ip地址由4段组成,每一段是一个字节(8位),最大值是255
网络号表示其属于互联网的哪一个网络,主机地址表示其属于该网络的哪一台主机。(主从关系)
ip共32位,4个字节,点分十进制记法。
A类地址:1个字节(8位,0+7位)表示网络号,网络号取值范围1~126,一般用于大型网络
后3个字节是主机号,(子网掩码255.0.0.0)
B类地址:2个字节(16位,10+14位)表示网络号
网络号取值为128~191,一般用于中等规模网络。子网掩码(255.255.0.0)

C类地址:3个字节(24位,110+21位)表示网络号,网络取值192~223,一般用于小型网络,子网掩码(255.255.255.0)

D类:以1110开始,用于组播(网络取值224~239,用于多路广播用户)
E特殊地址(用于科研),全1和全0保留不用。

子网掩码(subnet mask),又叫网络掩码、地址掩码、子网络遮罩,用来指明一个ip地址的哪些位标识的是主机所在的子网,哪些位标识的是主机的位掩码。
子网掩码必须结合ip地址一起使用。 作用是将某个ip地址划分为网络地址和主机地址2部分,屏蔽ip的一部分来区分网络标识和主机标识,并说明该ip在局域网还是在公网。
屏蔽ip的网络部分,A类默认子网掩码255.0.0.0

tcp udp区别

(位于传输层,只位于应用层之下,提供应用程序之间的通信,端口间, 1.格式化信息流;2.提供可靠传输,要可靠,必须接收端发回确认,丢失的话重新发送)
每个应用层协议 都有固定端口号, ip+端口号和一个可以固定到一个应用程序。(netstat -n查看端口号)

报文格式:报头header+数据
TCP格式:
报头 16位源端口号 + 16位目的端口号,
32位序号(标识TCP发送端向TCP接收端发送的数据字节流)
32位确认序号(SYN报文,ACK标志为0,没有确认序号)
首部长度, 标志位,
窗口大小(TCP流量控制,连接的每一端声明窗口大小,即接收缓冲区大小)
,校验和,(发送端计算存储,接收端验证)

UDP格式:
分为首部字段 和 数据字段
首部字段:占用8个字节,源端口、目的端口、长度和校验和。
首部和数据一起计算校验,
1.udp协议是无连接的,传输数据前不需要建立连接,当然也没有不需要释放连接。
2.尽最大努力交付,udp无法保证数据能准确交付到目的主机,也不需要对接收到的udp报文进行确认。不安全, 没有确认机制、没有重传机制。
2.5 没有发送缓冲区,没有重传机制,所以不需要发送缓冲区(拿到直接发送)
3.面向报文,udp将应用层传下来的数据不进行拆分合并,原封不动的发送过去。
4.没有拥塞控制,udp协议发送速率不受网络的拥塞影响。
5.udp比较小,包里面包的字段少。
6.支持1对1、1对多、多对1、多对多的交互通信。

基于UDP的协议:(适用场景,对数据安全要求不高,数据多为短消息,不需要拥塞,网络负担重,响应速度要求高。基于数据报)
(视频流、voip,传输允许丢包,效率高;dns也用的udp,简单的文件传输tftp)

TCP被应用更广泛,因为安全。可靠。
面向连接,可靠的数据流传输
TCP传输的是TCP报文,注重数据安全性
TCP面向字节流, 只是点对点连接(udp方式多种)
TCP用于网页 邮件等等。(http、ftp、telnet、

SYN:同步标志,同步序列编号栏,仅在三次握手时有效。
ACK:确认标志,确认编号栏,大多数情况是置位。

TCP三次握手(Three way handshake),建立TCP连接。建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。
socket编程中。三次握手由客户端执行connect来触发,
(1)第一次握手,client将SYN置为1,随机产生值seq,将该数据包发送给server,client变为SYN-SENT状态,等待server确认。
(2)第二次握手:server收到数据包后,由标志位SYN=1知道client请求建立连接,server将标志位SYN和ACK都置为1,ACK=J+1,随机产生seq,并将该数据包发送给client以确认连接请求,server进入SYN-RCVD状态。
(3)第三次握手:client收到确认后,检查ACK是否是J+1,ACK是否为1,如果正确将标志位ACK置为1,ack=K+1,并将数据包发送给server,server检查ack=K+1,ACK是否为1,如果正确则建立连接成功,client和server都是established状态,完成三次握手,随后client和server之间就可以传输数据了。

TCP四次挥手:(用来终止连接)断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。该过程由客户端或服务端任一方执行close来触发。两个方向上都必须发送FIN
FIN 结束标志。
(1)第一次挥手:client发送一个FIN,用来关闭client到server的数据传送,client进入FIN_WAIT_1状态;
(2)第二次挥手:server收到FIN后,发送一个ACK给client,

关闭连接为啥比建立连接多一次。。

TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。
两者的区别大致如下:
TCP面向连接,UDP面向非连接即发送数据前不需要建立链接
TCP提供可靠的服务(数据传输),UDP无法保证
TCP面向字节流,UDP面向报文。。
TCP数据传输慢,UDP数据传输快。。
TCP提供一种面向连接的、可靠的字节流服务
在一个TCP连接中,仅有两方进行彼此通信,因此广播和多播不能用于TCP
TCP使用校验和,确认和重传机制来保证可靠传输
TCP使用累积确认
TCP使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制。

TCP和UDP的应用场景

TCP:当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。
在日常生活中,常见使用TCP协议的应用如:浏览器,用的HTTP;FlashFXP,用的FTP;Outlook,用的POP、SMTP;Putty,用的Telnet、SSH;QQ文件传输
UDP:当强调传输性能而不是传输的完整性时, 要求网络通讯速度能尽量的快。如:QQ语音 QQ视频等。

bTCP对应的协议和UDP对应的协议

TCP对应的协议:
FTP:定义了文件传输协议,使用21端口。
Telnet:一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。
SMTP:邮件传送协议,用于发送邮件。服务器开放的是25号端口。
POP3:它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。
HTTP:是从Web服务器传输超文本到本地浏览器的传送协议,端口默认80。

UDP对应的协议:
DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。
SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。
TFTP(Trival File TransferProtocal),简单文件传输协议,该协议在熟知端口69上使用UDP服务。

为什么 TCP 叫数据流模式? UDP 叫数据报模式?

所谓的“流模式”,是指TCP发送端发送几次数据和接收端接收几次数据是没有必然联系的,比如你通过 TCP连接给另一端发送数据,你只调用了一次 write,发送了100个字节,但是对方可以分10次收完,每次10个字节;你也可以调用10次write,每次10个字节,但是对方可以一次就收完。
原因:这是因为TCP是面向连接的,一个 socket 中收到的数据都是由同一台主机发出,且有序地到达,所以每次读取多少数据都可以。
所谓的“数据报模式”,是指UDP发送端调用了几次 write,接收端必须用相同次数的 read 读完。UDP是基于报文的,在接收的时候,每次最多只能读取一个报文,报文和报文是不会合并的,如果缓冲区小于报文长度,则多出的部分会被丢弃。
原因:这是因为UDP是无连接的,只要知道接收端的 IP 和端口,任何主机都可以向接收端发送数据。 这时候,如果一次能读取超过一个报文的数据, 则会乱套。

TCP中的流量控制和拥塞控制

注:tcp协议如何保证传输的可靠性
流量控制主要针对的是端到端传输中控制流量大小并保证传输可靠性(未收到ack就不滑动)。流量控制往往是指点对点通信量的控制,所要做的是抑制发送端发送数据的速率。
拥塞控制主要是一个全局性过程,涉及到所有主机,路由器,以及与降低网络传输性能有关的所有因素。防止过多的数据注入到网络中。如果有发生丢包则通过拥塞控制减小窗口,确定出合适(慢启动 拥塞避免 快重传 快恢复)的拥塞窗口(增性加乘性减)。

三次握手,四次挥手,是否可以携带数据。。

核心思想:让双方都证实对方能发收。知道对方能收是因为收到对方的因为收到信息之后发的回应(ACK)。

redis 缓存。。

内存中缓存, 也可以持久化的 key-value的非关系型数据库。。
持久化:把内存数据写到磁盘中去,(rdb,aof)

redis单线程为什么快。。

Redis之所以执行速度很快,主要依赖于以下几个原因:

(一)纯内存操作,避免大量访问数据库,减少直接读取磁盘数据,redis将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度快;

(二)单线程操作,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

(三)采用了非阻塞I/O多路复用机制

异步队列, 缓存。。。

使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
缺点:
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

能不能生产一次消费多次呢?

使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。

缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。

如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期

3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

排序

(1)从平均时间性能而言,快速排序最佳,其所需时间是最省,但快速排序在最坏的情况下的时间性能不如堆排序和归并排序。
(2)当空间大资源足,要求时间效率时,可采用归并排序。
(3)堆排序虽然较之快排慢一些,但特别适合海量数据的排序。如在100万个数据里面找出前1000大的数据。可以用建立一个小顶堆存储1000元素。
(4)当序列中的记录基本有序或n值较小,插入排序是最佳的排序方法。
(5)基数排序最适用于基数很大但关键字较小的序列。
稳定性:
稳定算法:插入,冒泡,归并,基数,计数。
不稳定算法:快速(快),希尔(些),选择(选),堆排(堆)。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是课程列表: ├day01-01 系统介绍之缓冲区刷新.mp4 ├day01-02 系统介绍之man手册的使用.mp4 ├day02_文件查找规则.mp4 ├day03-01 调试输出点.mp4 ├day03-02 改变结构体对齐规则.mp4 ├day03-03 零长数组.mp4 ├day04-01 错误输出.mp4 ├day04-02 文件操作.mp4 ├day05-01 系统调用之文件操作.mp4 ├day05-02 C库函数对文件操作.mp4 ├day06-01 获取文件访问标识、文件加锁.mp4 ├day06-02 文件的访问权限、文件权限操作.mp4 ├day07-01链接文件操作.mp4 ├day07-02 目录操作.mp4 ├day08-01获取文件目录属性.mp4 ├day08-02 获取当前工作目录.mp4 ├day09-01 临时文件.mp4 ├day09-02 获取系统环境变量.mp4 ├day10_环境变量的增、删、改、查.mp4 ├day11-01 创建屏蔽字.mp4 ├day11-02 知识小结.mp4 ├day12-01 dup文件描述符复制.mp4 ├day12-02 dup2 文件描述符复制.mp4 ├day13_Mmap与文件关联映射.mp4 ├day14-01 匿名模式.mp4 ├day14-02 缓冲区(行缓冲 全缓冲 无缓冲).mp4 ├day14-03 获取进程id.mp4 ├day15-01 获取fork子父进程id.mp4 ├day15-02 子父进程 执行顺序 资源共享 资源回收.mp4 ├day16-01 ufork之子父进程 执行顺序 资源共享 资源回收.mp4 ├day16-02 孤儿进程.mp4 ├day16-04 守护进程讲解.mp4 ├day17-01 守护进程实现.mp4 ├day18-01 fork与sighal的组合(避免僵尸进程).mp4 ├day18-02 匿名管道之创建、缓冲区大小、阻塞模式.mp4 ├day18-03 知识总结.mp4 ├day19_匿名管道子父进程通信、有名管道创建、删除.mp4 ├day20_有名管道的特点、在子父进程及非子父进程间操作.mp4 ├day21-01 有名管道的创建、缓冲区大小、阻塞模式、信号.mp4 ├day21-02 有名管道进程间通信.mp4 ├day22_消息队列默认属性及改变方法.mp4 ├day23-01 消息队列、读写操作.mp4 ├day23-02 消息队列之mp-notify读操作.mp4 ├day23-03 消息队列之mp-tined-recv、mp-tined-sewd.mp4 ├day24-01 共享内存的读写操作.mp4 ├day24-02 共享内存与map的公用.mp4 ├day25-01 共享内存综合案例操作.mp4 ├day25-02 匿名信号量的讲解使用.mp4 ├day25-03 匿名信号量同步.mp4 ├day26-01巩固知识点回顾与总结.mp4 ├day26-02 匿名信号量的互斥.mp4 ├day26-03 线程id 线程比较.mp4 ├day27-01 线程的执行顺序 资源共享.mp4 ├day27-02 线程资源回收 线程变量创建 线程属性.mp4 ├day27-03 线程栈空间获取 线程中断.mp4 ├day28-01 线程及信号量组合同步、组合互斥.mp4

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值