Linux
Linux指令
输出重定向
覆盖输出 会覆盖原文件内容
追加输出 不会覆盖原文件内容 在原内容末尾继续添加
管道
指令1 | 指令2 指令1的输出是指令2的输入
Vim三种模式
命令模式 编辑模式 末行模式 权限 chmod 数字形式 r:4 w:2x:1-:0 切换用户 su sudo
查看端口占用情况
netstat -t显示TCP连接信息 -u为UDP -l是仅显示监听状态的端口 ss 和netstat差不多
系统资源
df指令 df-h 查看文件系统磁盘使用情况统计 free -m 以mb为单位查看内存使用情况 ps 查看进程运作情况 ps-ef | grep 进程号 kill 杀死进程 9 强制删除 top 动态查看进程变化 suspend 终止进程
GCC
GCC是linux系统下最常用的C/C++的编译器 分布编译
预处理
gcc -E .c -o .i 得到预处理文件
编译
gcc -s . i 得到汇编文件
汇编
gcc -c .s 得到机器码文件
链接
gcc .o
同步,异步,阻塞,非阻塞
如果立刻去指向该函数,称为同步 如果没有去执行该函数,而是将执行此函数的时机安排在未来的某个时机,然后马上继续执行刚才的代码块,叫做异步 当执行函数时,直至获得完整的资源之前,都暂停执行当前的代码块,这称为阻塞 当执行此函数时,立即获得瞬时的结果,然后马上继续执行当前的代码块。如果获得的瞬时资源不是完整的资源,之后周期性发送类似的请求,直至获得完整的资源,这称为非阻塞。
软连接和硬链接
硬链接是多个目录项中的索引节点指向一个文件,指向同一个inode,但是indoe不可能跨文件系统,每个文件系统都有各自的indoe数据结构和列表,所以硬链接是不可以跨文件系统。 软连接相当于重新创建一个文件,这个文件有独立的inode,但是这个文件的内容是另外一个文件的路径,所以软连接是可以跨文件系统的,
静态库与动态库的区别
可执行文件的大小
静态库的大小比动态库的大小大的多 是因为静态库从二进制文件中拷贝了一份 而动态库只是复制了一些重定位和符号表信息
扩展性与兼容性
如果静态库中函数改变了 可执行文件必须重写编译 而动态库不用 直接编译即可
加载速度的话
静态库更快 他和可执行文件在一起 而动态库加载或运行时链接
GDB
break/b 数字或者函数 含义时在第几行设置断点 r 运行 n 下一条指令 会越过函数 s 下一条指令 会进入函数
Linux 系统编程
32位的CPU一次可以处理4个字节 64位CPU可以处理8个字节
内核
它是操作系统的核心,可以控制硬件 比如 cpu 内存 硬盘,内核作为引用连接硬件设备的桥梁,应用程序只需关心与内核交互,不用关心硬件的细节。
进程与线程的区别和联系
- 进程时操作系统进行资源分配调度的基本单元
- 线程相当于是进程的子任务 他是cpu分配和调度的基本单元
- 一个进程可以有多个线程 但线程只能属于一个进程
- 进程管理开销很大 这是因为他需要系统给他分配资源 销毁时也会需要回收 但线程管理开销很小
- 然后进程之间不会相互影响 如果一个进程内的线程崩溃 则该进程也崩溃
Linux查看动态日志
tail -f Linux理论上可以创建多少个进程 32768 pid
协程
协程也叫微线程,协程就是子程序在执行时终端并专区执行别的子程序,在适当的时候又返回来执行。 这种子程序间的跳转不是函数调用,也不是多线程执行,所以省去了线程切换的开销,效率很高。并且不需要多线程间锁机制,不会发生变量写冲突。
进程写文件过程中,写一半崩溃了,已写入的数据会丢失吗?
不会,page cache的本质是Linux内核管理的内存区域, page cache由多个page构成 page在操作系统了为4kb大小,
操作系统的内存管理
操作系统内存管理包括物理内存和虚拟内存管理 物理内存管理包括交换与覆盖,分页管理,分段管理。和段页式管理 虚拟内存管理包括虚拟内存的概念,页面置换算法,页面分配策略等 内存分段:程序是由若干个逻辑分段组成的,如可由代码分段,数据分段,栈断,堆段组成。不同的段是有不同属性的,所以就用分段的形式把这些段分离出来。
内存分页:分页就是把整个虚拟和物理内存空间切成一段段固定尺寸的大小,我们叫做页 通过页表来映射物理虚拟地址 而进程访问的虚拟地址在页表查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存,更新进程页表,最后返回用户空间,恢复进程的运行。
段页式内存管理
实现的方式:先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,在划分固定大小的页。 地址结构就由段号,段内页号和页内位移三部分组成。 要想得到物理地址 第一访问段表,得到页表起始地址 第二次访问页表,得到物理页号 第三次将物理页号与页内位移组合,得到物理地址。
死锁产生的必要条件
互斥,一个资源只能被一个进程使用 占有并请求 一个进程因请求资源而阻塞时 不可剥夺 进程已获得的资源,在未使用完之前,不能强行剥夺 循环等待 若进程之间形成一种头尾相接的循环等待资源关系。 产生死锁的原因主要是 因为系统资源不足 进程运行推进的顺序不合适 资源分配不当等 解决办法 重新启动:代价大,在此之前所有进程已完成的计算工作都没了。 终止进程,终于参与死锁的进程并回收它们所占资源。 一次性全部终止; 逐步终止 剥夺资源 剥夺死锁进程所占有的全部或者部分资源 进程回退 让参与死锁的进程回退到以前没有发生死锁的某个点处
写时拷贝
就是等到修改数据时才真正分配内存空间,这是对程序性能的优化,可以延迟甚至是避免内存拷贝,目的就是为了避免不必要的内存拷贝。也是用引用计数来实现的,会在开辟的空间中多维护四个字节的空间来存储引用计数,多开辟一个加1,释放减一,直到为0
什么时候用到多进程,什么时候用到多线程
- 频繁修改,需要频繁创建和销毁的优先使用多线程
- 计算量 需要大量计算优先用多线程 因为要小号大量CPU资源且切换频繁
- 相关性:任务相关性强的用多线程 反之多进程,因为线程之间的数据共享和同步比较简单
- 多分布 可能要扩展到多机分布的用多进程,多核分布的用多线程
同一个进程内的线程会共享什么资源
- 该进程的地址空间
- 全局变量
- 堆空间
- 线程的栈空间是自己独有的
原子操作
就是要么全部成功,要不都失败,不能出现中间状态 多线程是如何同步的
互斥锁,读写锁,自旋锁,条件变量
互斥锁
是一种阻塞锁,当某个线程持有锁,其他试图获取该锁的线程被阻塞。无法获取锁时,会被操作系统挂起并进入休眠状态,不再消耗CPU资源,操作系统会进行上下文切换,当线程在等待时会释放CPU资源。 使用场景是,锁被占用时间较长的情况,适合用于保护共享资源访问时间较长或线程竞争激烈的情况
自旋锁
自旋锁是一种非阻塞锁,当线程尝试获取一个被其他线程持有的自旋锁时,该线程会进入忙等待状态,而不是被挂起。所以他持续的消耗CPU资源,不会发生上下文切换。他通常有一个参数来限制最大尝试次数。 使用场景是,锁被占用时间较短的情况,用于保护共享资源访问时间较短且线程竞争不激励的情况
乐观锁和悲观锁
悲观锁就是,认为多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以在访问共享资源前,先要上锁 乐观锁就是,先修改完共享资源,在验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。
什么是上下文切换
是一种将CPU资源从一个进程分配给另一个进程的机制,并发,当一个进程在执行时,CPU的所有寄存器中的值,进程打开的文件,内存信息等。 进程的上下文切换不仅包含了虚拟内存,栈,全局变量等用户空间的资源,还包括了内核堆栈,寄存器等内核空间的资源。 进程切换: 当内核需要切换到另一个进程时,它需要保存当前进程的 所有状态, 即保存当前进程的上下文,以便在再次执行该进程时,能够必得到切换时的状态执行下去。
进程调度算法
非抢占式:当进程正在运行时,他就会一直运行,直到该进程完成或发生某个事件而被阻塞时,才会把CPU让给其他线程。 抢占式:进程在运行是,可以被打断,使其把CPU让给其他进程,抢占的原则有三种,时间片原则,优先权原则,短作业原则。
先来先服务调度算法
是一种非抢占的先来先服务算法 ,每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。 适用于CPU繁忙型作业的系统,而不适用于I/O繁忙型作业的系统。
最短作业优先调度算法
会优先选择运行时间最短的进程来运行,对长作业不利,有很多短作业,会给长作业排到很后面,导致不被运行。
高响应比优先调度算法
主要是权衡了短作业和长作业。 每次进行进程调度时,先计算响应比优先级,然后把响应比优先级最高的进程投入运行。
时间片轮转调度算法
每个进程被分配一个时间段,称为时间片,即允许该进程在该时间段中运行。 如果时间片用完,进程还在运行的化,那么会将此进程从CPU释放出来,并把CPU分配另外一个进程 如果进程在时间片结束之前阻塞或结束,直接切换CPU 时间片太短会导致CPU效率变慢 太长又会导致短作业进程响应时间变长。
最高优先级调度算法
从就绪队列中选择最高优先级的进程先运行,称为最高优先级调度算法。 进程的优先级可以分为,静态和动态优先级 静态:创建进程,已经确定优先级,不会改变 动态:根据进程的动态变化调整优先级 随着时间的推移郑家等待进程的优先级 非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,在选择优先级高的进程 抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行
多级反馈队列调度算法
多级:表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。 反馈:表示如果有新的进程加入优先级高的队列时,立刻停止当前正运行的进程,转而去运行优先级高的队列。
内存页面置换算法
当CPU访问的页面不在物理内存时,就会产生一个缺页中断,请求操作系统将所缺页调入到物理内存。 页面置换算法的功能是,当出现缺页异常,需要调入新页面而内存已满时,选择被置换的物理页面。
- 最佳页面置换算法 发生缺页时,选择最长时间没有被访问的页面进行置换
- 先进先出置换算法 选择在内存驻留时间很长的页面进行置换
- 最近最久未使用的置换算法 选择最长时间没有被访问的页面进行置换
最不常用算法
当发生缺页终端时,选择访问次数最少的那个页面,并将其淘汰 加个计数器,一个页面被访问时,加一 发生缺页中断时,淘汰最小计数器那个页面。
磁盘调度算法
先来先服务:
先到来的请求,先被服务
最短寻道时间优先:
优先选择从当前磁头位置所需寻道时间最短的请求
扫描算法:
磁头在一个方向上移动,访问所有未完成的请求,直到磁头到达该方向上的最后的磁道,才调换方向。
PCB
PCB就是进程控制块,是操作系统中的一种数据结构,用于表示进程的状态,操作系统通过PCB对进程进行管理。存放的是进程标识符,处理机状态,进程调度信息,进程控制信息。进程控制信息,是通过链表的方式进行组织,把具有相同状态的进程链在一起,组成各种队列。
虚拟内存
虚拟内存是一种管理内存的方式,一个进程通常会有4G的虚拟内存空间,它的地址都是逻辑地址,在每次访问的时候都需要映射成物理地址,操作系统用内存分段和内存分页来进行管理虚拟地址和物理地址的映射关系。
虚拟内存的实现方式
如果采用连续分配方式时,会使得一部分内存空间都处于暂时或永久的空闲状态,造成内存资源的严重浪费。因此虚拟内存需要建立在离散分配的内存管理方式的基础上,有三种方式。 请求分页存储管理 请求分段存储管理 请求段页式存储管理
操作系统中的时钟是什么
时钟也被称为定时器,防止一个进程长期占用CPU时间等其他功能。
Makefile
在makefile里,时间戳是指在进行编译时,通过比较文件的时间戳来确定是否需要重新生成目标文件。时间戳是实现增量编译的核心原理,它能够有效避免不必要的重复编译,提高编译效率
时间戳
时间戳是指文件最后的修改时间,可以使用stat命令来查看文件的时间戳信息,利用时间戳来判断文件是否发生了变化,从而确定是否需要重新编译相关的文件
孤儿进程
父进程先于子进程结束,则子进程成为孤儿进程 然后变为孤儿进程后会有一个专门回收的init作为他的父进程
僵尸进程
进程终止 但是父进程未回收子进程 子进程残留资源(PCB)存放在内核中 父进程没有调用wait或者waitpid, 可以杀死父进程 让init回收
进程间通信一共有6种方式
管道
匿名管道 pipe 只能单向通信 要想双方通信的话需要建立两个管道 必须有亲缘关系 所谓的管道,就是内核里面的一串缓冲区
命名管道 fifo
它可以在不相关的进程间也能相互通信。 管道的写入数据都是缓存在内核种,另一个进程读取数据时自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持lseek文件定位操作
不同设备及其间的进程通信
消息队列
消息队列就是一个保存在内核中的消息链表,是一系列保存在内核中消息的列表 ,具有特定的格式以及特定的消息类型 可以自定义优先级。 消息队列生命周期随内核,如果没有释放消息队列或者没有关闭操作系统。消息队列会一直存在,而前面提到的匿名管道的生命周期,是随着进程的创建而简历,随进程的结束而销毁。 消息队列不适合比较大数据的传输 消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销
共享内存
就是拿出一块虚拟地址空间来,映射到相同的物理内存中,没有用户态与内核态之间的消息拷贝过程。 这样这个进程写入的东西,另外一个进程立马就能看到,不需要拷贝来拷贝去,大大提高了进程间通信的速度。 但是如果多个进程同时修改同一个共享内存,很有可能冲突了。
信号量
信号量可以看做是一个计数器 ,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。他可以用来控制多个进程对资源共享的访问 信号量只有两种操作等待和方式 等待P 就是将其值减一如果减完之后信号量小于0表示资源已经被占用,进程需阻塞等待;发送 V就是将值加一或者将进程回复运行P操作就是在进入共享资源之前,V操作是用在离开共享资源之后
生产者-消费者问题
生产者生成数据后,放到一个缓冲区中 消费者从缓冲区取出数据处理 任何时刻,只能有一个生产者或者消费者可以访问缓冲区 线程同步—-建立线程执行的顺序
读写锁
读取资源时用读锁 修改资源时用写锁 工作原理 没有线程持有写锁时 所有线程都可以一起持有读锁 有线程持有写锁时 所有的读锁和写锁都会阻塞
条件变量
条件变量简单的说,就是休眠当前任务,然后当需要的条件满足的时候,再次唤醒休眠的任务。 通常来说,条件变量是和互斥锁一起使用的,也就是任务一因为在当前的任务中无法完成需要的条件,只能挂起当前任务,然后通过任务二来完成这个条件(通常就是操作线程之间共享的资源),当任务二完成条件后,会发送信号给休眠的任务一,休眠的任务一被唤醒然后向下执行。