操作系统-进程与线程

  • 进程基本概念;
  • 进程状态及状态转换;
  • 进程控制块的作用和内容;
  • 进程控制操作;
  • 进程地址空间与进程映像;
  • 为什么引入线程?线程的应用场景;
  • Web服务器的实现;
  • 线程概念、线程与进程的区别;
  • 线程实现的三种方式;
  • Pthreads线程库及应用;
  • 几个重要的概念:原语、可再入程序。

进程的基本概念

1
2
3
4
5
多道程序设计 : 允许多个程序同时进入内存并运行, 提高系统效率

并发环境和并发程序 :
并发环境: 一段时间间隔内, 一个处理器上有 多个程序 处于开始允许但尚未结束的状态
并发程序: 在并发环境中的程序
  • 进程的定义
1
2
3
4
5
6
具有独立功能的程序 关于 某个数据集合上的一次运行活动, 系统资源分配和调度的独立单位  (又称 任务)
程序的一次支持过程
正在运行程序的抽象
并发,抽象为一个CPU变成多个CPU
系统资源分配单位,内存,文件。。。独立地址空间
操作系统将 CPU 调度给需要的 进程

进程控制块 PCB (又称 进程描述符,进程属性)

1
2
3
4
5
6
7
8
9
10
操作系统 控制和管理进程的数据结构 (保存了操作系统管理进程需要的信息)
记录进程的各种属性,描述进程的动态变化过程
是操作系统感知进程存在的唯一标志 (一个进程一个PCB)
进程表: 所有进程的PCB集合 (操作系统最多有多少个进程是固定的)

PCB包含信息
进程的描述信息
进程的控制信息
进程所拥有的资源和使用情况
CPU现场信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
进程描述信息: 
进程标识符(process ID), 唯一标识,整数
进程名, 通常是可执行文件名,不唯一
用户标识符 (创建进程的用户信息)
进程组关系 (父子进程等)

进程控制信息:
当前状态
优先级
代码执行入口地址
程序的磁盘地址
运行的统计信息(执行时间, 页面调度)
进程间同步和通信
进程的队列指针
进程的消息队列指针

所拥有的资源和使用情况:
虚拟地址空间的状况
打开文件的列表

CPU现场信息: (进程不运行时,操作系统保存的硬件执行状态)
寄存器值(通用寄存器,程序计数器PC,程序状态字PSW, 栈指针)
指向该进程页表的指针

进程状态及状态转换

三种基本状态

  • 运行态 : 占有CPU, 在CPU上执行
  • 就绪态 : 具备运行条件,但是没有空闲CPU,暂时不能执行
  • 等待态 : 因等待某个时间而暂时不能运行(如:等待读盘) - 又称为阻塞态,封锁态,睡眠态

三状态模型

进程三状态模型和状态转换

其他状态

1
2
3
4
5
6
7
8
9
10
创建态: 
已经完成进程创建的必要工作: 分配PID,填写PCB
但尚未同意执行该进程(如资源不足)

终止态:
终止执行后,完成一些数据统计工作,资源回收工作

挂起态:
用于调节负载 (CPU忙不过来)
进程不占用内存空间, 其进程映像交换到磁盘上

五状态模型

进程五状态模型和状态转换

七状态模型

进程七状态模型和状态转换

linux状态转换示意图

linux状态转换示意图

进程队列

按进程所处状态不同对进程进行管理

1
2
3
为每一类进程建立一个或多个队列  (如处于等待态, 按等待事件不同分多个队列)
队列元素为 PCB
伴随进程状态的改变,PCB从一个队列进入另一个队列

进程控制

完成进程各状态之间的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
由特定功能的原语实现:  (原语: 原子操作,执行的程序不可分割,不可中断)
进程创建原语
进程撤销
进程阻塞,唤醒,挂起,激活
改变优先级
。。。

进程创建原语:
给新进程分配 唯一标识PID, 以及 进程控制块PCB
为进程分配 地址空间
初始化进程控制块 (设置默认值)
设置相应的队列指针 (插入进程队列,如就绪队列)

进程撤销原语: (结束进程)
收回进程所占有的资源 (关闭打开的文件,端口网络连接,回收分配的内存。。。)
撤销该进程的PCB
进程阻塞:
处于运行状态的进程,运行过程中期待某一事件发生而未发生时,进程自己执行阻塞原语,有运行态进入阻塞态

Unix常用进程控制操作 (都是系统调用)

1
2
3
4
fork():  通过 复制调用进程 来创建新的进程, 进程建立进程
exec(): 包括一系列系统调用,通过用新的程序代码覆盖原来的地址空间,实现进程执行代码的转换
wait(): 初级进程同步操作, 一个进程等待另一个进程结束
exit(): 终止一个进程的运行

一些进程的概念

1
2
3
4
5
6
7
8
进程不同角度的分类: 
系统进程 和 用户进程
前台进程 和 后台进程
CPU密集型进程 和 I/O密集型进程

进程的层次结构:
UNIX: init为根
Windows: 地位相等

进程地址空间

1
操作系统为 每个进程 分配 独立的相对进程地址空间

进程映像

对进程执行活动全过程的静态描述

1
2
3
4
5
6
7
8
9
10
由 进程地址空间内容,硬件寄存器内容, 该进程相关的内核数据结构,内核栈 组成

用户相关: 进程地址空间( 代码段,数据段,堆栈,共享库)

寄存器相关: 程序计数器,指令寄存器,程序状态寄存器,栈指针,通用寄存器 等的值

内核相关:
静态: PCB 及 各种资源数据结构
动态: 内核栈 (不同进程在进入内核后使用不同的内核栈)

上下文 (context)

1
2
3
4
5
6
将CPU硬件状态从一个进程换到另外一个进程的 过程称为 上下文切换

进程运行时: 硬件状态保存在CPU的寄存器中
进程不运行时: 这些寄存器的值保存在 进程控制块PCB中

操作系统运行新的进程时: 将PCB相关的值送到对应的寄存器中

线程

为什么在进程中引入线程

1
2
3
应用的需要  -  一个应用一个进程,但同时需要多个任务完成
开销的考虑 - 线程开销小,线程切换花费时间少, 线程之间共享内存和文件
性能的考虑

线程的基本概念

1
2
3
4
进程: 资源的拥有者

线程: CPU调度单位 (有了线程后继承了进程的基本属性)
在同一进程中增加了多个执行序列(线程)

线程的属性

1
2
3
4
5
6
7
8
标识符ID
状态及状态转换
不运行时需要保存的上下文(寄存器值)
有自己的栈和栈指针
共享进程的地址空间和相关资源
可以创建, 撤销 另外一个线程

程序开始以 一个单线程进程 运行

线程机制的实现

1
2
3
用户级线程
核心级线程
混合 - 以上两种方法结合

用户级线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实现: 
在用户空间 建立 线程库 : 提供一组管理线程的过程
是运行时系统: 完成线程的管理工作(操作,线程表)
内核管理的还是进程,不知道线程的存在
线程切换不需要 内核态特权

UNIX - 一般就是用户级线程

例子:
POSIX线程库 -- PTHREAD
POSIX:多线程编程接口, 以线程库方式提供给用户

优点:
线程切换快
调度算法是应用程序特定的
用户级线程可运行在任何操作系统上 (只需要实现线程库)
缺点:
内核只将处理器分配给进程, 同一进程的两个线程不能同时运行在两个处理器上
大多数系统调用是阻塞的,因此,由于内核阻塞进程,进程中所有线程也被阻塞

核心级线程

彻底改造操作系统

1
2
3
4
5
6
内核进行所有线程的管理, 向应用程序提供API接口
内核维护进程和线程的上下文
线程的切换需要内核支持
以线程为基础进行调度

例子: Windows

混合模型

1
2
3
4
5
6
线程的创建在 用户空间  (用线程库完成)
线程调度等在 核心态完成

实现: 多个用户级线程 多路复用 多个内核级线程

例子: Solaris 操作系统

总结

进程

1
2
3
4
5
6
并发性: 任何进程可以与其他进程 一起向前推荐
动态性: 动态产生,动态消亡。 在生命周期内在三种基本状态之间转换
独立性: 资源分配 的独立单位
交互性: 进程在执行过程中 与 其他进程产生直接或间接的关系
异步性: 每个进程以其 相对独立,不可预知的速度 向前推进
进程映像: 程序 + 数据 + 栈(用户栈,内核栈)+ PCB

线程

1
2
3
多线程的应用场景
线程基本概念,属性
线程的实现机制

可再入程序 (可重入程序) 的概念

1
2
3
4
5
指的是可以被多个 进程同时调用的程序,因此对这个程序有限制。 
具有固定性质:
它是纯代码的, 在执行过程中这个代码不会改变。
那么如果有改变,就需要调用它的进程提供不同的数据区。改变的内容放在数据区,而代码部分是不再改变的。
那么可再入程序实际上是我们大部分 进程和线程都必须是可再入程序才能去运行

操作系统-中断异常机制

操作系统运行环境与运行机制

1
2
3
4
5
操作系统运行环境
CPU状态
中断/异常机制
运行机制
系统调用
1
2
3
4
5
6
7
8
中央处理器(CPU): 运算器,控制器,一系列寄存器 以及 高速缓存 构成
两类寄存器:
用户可见寄存器: 高级语言编译器分配使用,减少程序访问内存次数
控制和状态寄存器: 控制处理器的操作,操作系统代码使用
常见控制状态寄存器:
程序计数器(PC program counter):记录将要取出指令的地址
指令寄存器(IR instruction register): 记录最近取出的指令
程序状态字(PSW program status word) : 记录处理器的运行状态,如条件码,模式,控制位等信息

操作系统需求 – 保护

1
2
3
4
5
6
从特征考虑: 并发,共享
提出要求: 保护和控制

硬件提供基本运行机制:
处理器具有特权级别,能在不同的特权级别下运行不同的指令集合
硬件机制可将 OS 和 用户程序隔离

处理器状态(模式)

1
2
现代处理器,cpu状态分为 二,三,或四种
在 程序状态字寄存器中,专门设置一位,根据运行程序 对资源和指令的使用权限,设置不同的CPU状态

操作系统需要两种 CPU状态

1
2
3
4
5
内核态(kernel mode): 运行操作系统程序   
用户态(User mode): 运行用户程序

特权指令 : 只能操作系统使用
非特权指令 : 用户程序可以使用的指令 (操作系统也能使用)

例子: x86系列处理器

1
2
3
4
5
6
7
支持 4 个特权级别, 称为特权环: R0, R1, R2, R3
R0相当于 内核态
R3相当于 用户态
R1,R2 介于之间
不同级别能运行的指令集合不同

目前大部分基于x86的操作系统都只使用了 R0,R3 两个级别

CPU状态之间的转换

1
2
3
4
5
6
7
8
9
用户态 -》 内核态
唯一途径: 中断/异常/陷入机制

内核态 -》 用户态
设置程序状态字PSW

一条特殊的指令: 陷入指令 (又称 访管指令, 用户态访问管理态的指令)
提供给用户程序的接口,用户调用操作系统的服务
例如: int, trap, syscall, sysenter/sysexit

中断与异常机制

  • 介绍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
重要性: 好比汽车的发动机
可以说: 操作系统是 由 "中断驱动" 或 "事件驱动" 的
主要作用:
处理设备发来的中断请求
捕获用户程序提出的服务请求
防止用户程序执行过程中的破坏性活动
等等

概念:
CPU对系统发送的某个事件做成的一种反应 (事件的发送改变处理器的控制流)
CPU暂停正在执行的程序, 保留现场,去执行相应的事件处理程序,处理完成后返回断点继续执行

特点:
随机发送
自动处理
可恢复

中断的引入:
支持CPU和设备之间的并行操作
异常的引入:
CPU执行指令时本身出现的问题 (错误异常处理程序,系统调用)

事件:
中断(外中断):I/O中断,时钟中断,硬件故障 -- 外部事件
异常(内中断):系统调用,页故障,保护性异常,断点指令,程序性异常 -- 执行本身程序引发的
  • 工作原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
软硬件配合完成
硬件 -- 中断/异常的响应:
捕获中断源发出的 中断/异常请求,以一定方式响应,将处理器控制器交给特定的处理程序
软件 -- 中断/异常的处理程序:
识别 中断/异常类型 并完成相应处理

中断响应:
处理器中有 中断寄存器
响应过程: CPU执行每条指令后扫描中断寄存器,若有中断,将中断触发器内容编码进入PSW相应位(中断码),查中断向量表,执行中断处理程序

中断向量:
一个内存单元 - 存放 中断处理程序入口地址 和 程序运行所需的处理机状态字

中断处理程序:
设计操作系统时,为每一类中断编好处理程序,设置好中断向量表

linux中断向量表

  • x86处理器 的中断/异常
1
2
3
中断: 硬件引发
异常: 除零异常等, x86大概20种异常
系统调用: 用户态到内核态的唯一入口,是异常的一种
1
2
3
4
5
6
7
8
9
10
11
12
中断控制器(PIC 、 APIC) :将中断信号转换为 中断向量,引发CPU中断

实模式: 中断向量表
存放中断服务程序的入口地址

保护模式: 中断描述符表
采用 门(gate)描述符 数据结构 表示中断向量
门描述符类型:
任务门
中断门 (主要使用): 中断门后 系统自动禁止中断
陷阱门 (主要使用): 系统不禁止,可继续接收中断
调用门

中断/异常 的硬件处理过程

1
2
3
4
5
6
7
8
9
确定 中断向量i
通过IDTR寄存器,找到IDT中断描述符表, 查到第i项 中断描述符
通过GDTR寄存器,找到GDT段描述符表,根据中断描述符中的段描述符,从GDT中获取到 中断处理程序的 段基址
过程中需要多次特权级别检查

检查是否发送特权级别变化,是则堆栈切换 (用户态,内核态切换 - 使用相应权级的堆栈)
硬件压栈,保存上下文环境
如果是中断门, 则清IF位,禁止继续接收中断, 陷阱门则不管
根据 中断描述符的 段内偏移量 和 段描述符的段基址, 获取中断程序入口地址,进入执行

系统调用

1
2
3
4
5
6
7
8
9
10
用户在编程时可以调用的操作系统功能
CPU 从用户态 到 内核态的唯一入口

每种操作系统都提供几百种系统调用
进程控制
进程通信
文件使用
目录操作
设备管理
信息维护等等
  • 系统调用, 库函数, 内核函数 概念区分
1
2
3
4
内核函数 是 系统调用 的处理程序
库函数 是对 内核函数的分装 (如c库函数)

应用程序可以直接调用内核函数来进行系统调用, 但一般是操作 库函数 来进行
  • 系统调用机制的设计
1
2
3
4
5
6
7
8
9
10
中断/异常机制 -- 来支持 系统调用的实现
一条特殊指令, 陷入指令(又称访管指令) -- 引发异常,完成用户态到内核态的切换
系统调用号和参数 -- 每个系统调用事先给定编号(功能号)
系统调用表 -- 存放系统调用程序的入口地址


参数传递问题: 用户程序参数传递给内核
1. 陷入指令自带参数: 长度有限,参数有限
2. 通过寄存器传递(主要): 操作系统和用户程序都能访问的寄存器, 数量有限,参数有限
3. 内存中开辟专门的堆栈区
  • 系统调用执行过程
1
2
3
4
5
CPU 执行到特殊的陷入指令: 
中断/异常机制: 硬件保护现场; 查中断向量表, 最终把CPU控制权交给 系统调用总入口程序
系统调用总入口程序: 保存现场;参数保存进内核的堆栈,查系统调用表 把控制权转交给 相应系统调用的处理程序/内核函数
执行系统调用
恢复现场,返回用户程序
  • 基于x86内核的 linux系统调用实现
1
2
3
4
5
6
7
8
9
10
11
12
陷入指令 为128号:  
int $0x80
门描述符:
系统初始化时,对IDT表中的128号门初始化对应中断了
门描述符2,3字节标识 段选择符, 0,1,6,7字节为偏移量, 最终能对应到 system_call() 中断总入口
门类型: 15 陷阱门,支持调用过程仍允许接收中断
DPL特权级别: 3, 与用户级别相同, 允许用户进程使用该门描述符 (是用户发起的系统调用,所以必须是3)

执行 int $0x80 命令
特权级别的改变, 切换栈 用户栈 -》 内核栈。 CPU指向新的栈地址
信息压栈, 最后返回使用
找到 系统调用内核函数入口地址,执行

中断发送后操作系统的工作步骤

操作系统-简介

学习内容

  • 操作系统结构
  • 中断及系统调用
  • 内存管理
  • 进程及线程
  • 处理机调度
  • 同步互斥
  • 文件系统
  • I/O子系统

操作系统是什么?

是系统软件,一些程序模块的集合

以尽量 有效,合理 的方式,组织和管理计算机的软硬件资源
合理组织计算机的工作流程,控制程序的执行并并向用户提供各种服务功能
用户能够灵活方便的使用计算机,使整个计算机系统高效率运行

关键词:

有效: 系统资源,资源利用率
    CPU, I/O设备
合理: 软硬件资源的管理是否公平合理    
方便使用: 用户界面 , 编程接口

操作系统是资源的管理者:(如何管理?)

跟踪记录资源的使用情况
确定资源分配策略--算法 
   静态分配策略
   动态分配策略
实施资源的分配和回收
提高资源利用率
保护资源的使用
协调多个进程对资源请求的冲突

资源管理角度,操作系统的五大基本功能:

进程/线程管理 (CPU管理)
    进程线程状态, 控制,同步互斥,通信,调度, 。。。
存储管理
    分配/回收,地址转换,存储保护,内存扩充,。。。   
文件管理
    文件目录,文件操作,磁盘空间,文件存取控制, 。。。
设备管理
    设备驱动,分配回收,缓冲技术, 。。。
用户接口
    系统命令,编程接口

操作系统是 各种系统服务的提供者

从用户角度看
    操作系统为用户提供了一组功能强大,方便易用的命令或系统调用
典型的服务
    进程的创建,执行; 文件目录的操作; I/O设备的使用; 各类统计信息, 。。。

对机器硬件的扩展

操作系统的特征

  • 并发: 处理多个同时活动的能力
1
2
3
4
并发问题: 切换,保护,依赖活动间的同步 
单CPU:
宏观上: 程序同时执行
微观上: 任何时刻只有一个程序在真正执行
  • 共享:
1
2
3
4
5
6
  操作系统和多个用户程序共同使用计算机中的资源
操作系统对资源进行合理分配
资源在一个时间段内交替被多个进程使用

互斥共享 (打印机
同时共享 (可重入代码,磁盘文件
  • 虚拟
1
2
3
4
5
6
一个物理实体 映射为 若干对应的逻辑实体 -- 分时 或 分空间
虚拟是 操作系统 管理 系统资源的重要手段, 提高资源利用率

CPU -- 每个进程的 "虚处理器"
存储器 -- 每个进程都有独立的虚拟地址空间(代码 + 数据 + 堆栈)
显示设备 -- 多窗口 或 虚拟终端
  • 随机
1
2
3
操作系统必须随时对 以不可预测的次序 发生的事件进行响应并处理
进程运行速度不可预知
难以重新系统在某个时刻的状态

操作系统分类

  • 批处理
1
2
3
4
5
工作方式
1. 系统操作员收集作业,输入系统,系统形成自动转接的连续作业流
2. 启动操作系统,系统依次执行作业,返回作业结果
技术: SPOOLING
利用磁盘做缓冲,将输入,计算,输出 组成独立的任务流,使I/O 和 计算 真正并行
  • 分时操作系统
1
2
3
4
时间片
将CPU的时间划分成片段
操作系统以时间片为单位,轮流为终端用户服务,每次服务一个时间片
利用用户错觉,使用户感受不到计算机在服务他人
  • 实时操作系统
1
2
对外部请求在严格时间范围内做出响应
高可靠性
  • 个人计算机操作系统

  • 网络操作系统

1
2
3
基于计算机网络
功能: 网络管理,通信,安全,资源共享,网络应用
追求目标: 互相通信,资源共享
  • 分布式操作系统
1
2
3
分布式系统: 或以计算机网络为基础,或以多处理机为基础,分别在不同计算机上
分布式操作系统: 统一的,多个计算机协作完成一个任务。 自动实现全系统内的任务分配,调度,均衡工作负载
处理能力增强,速度更快,可靠性强,具有透明性
  • 嵌入式操作系统
1
2
嵌入式系统: 在各种设备,装置或系统中, 完成特定功能的软硬件系统
嵌入式操作系统: 运行在嵌入式系统环境中,对系统及其所控制的资源进行统一调度,指挥的系统软件

linux-software

linux 环境下各种软件的安装

centos 安装 v2ray 服务端 ( 个人VPN搭建)

1
2
3
4
yum update -y && yum install curl -y   
bash <(curl -s -L https://git.io/v2ray.sh) ## 安装脚本
v2ray url ## 生成 vmess链接, 导入客户端
systemctl stop firewalld ## 关闭防火墙

安装zsh 和 oh-my-zsh

1
2
3
4
5
6
7
yum install zsh 
## 从gitee上获取安装脚本
wget https://gitee.com/mirrors/oh-my-zsh/raw/master/tools/install.sh

## 编辑 install.sh, 修改以下两行
REPO=${REPO:-mirrors/oh-my-zsh}
REMOTE=${REMOTE:-https://gitee.com/${REPO}.git}

code-review

对旧代码进行review

  • 业务说明
    • (无业务不开发)
  • 设计思路
    • (一切实现的基础)
  • 代码层次结构
    • (逻辑清晰的前提)
  • 代码逻辑说明
    • (想法的具体实现)
  • 代码好的地方
    • (开发规范执行完好的地方)
  • 代码坏的地方
    • (开发规范执行不好的地方)
  • 代码实现经典之处
    • (牛逼大佬值得学习的地方)
  • 代码实现缺陷遗漏之处
    • (需要牛逼大佬完善的地方)

对新代码如何进行review

参考文章 <<如何在团队中做好Code Review>> , 基本全拷贝,去看这篇文章就可以了

好处

  • 互相学习,彼此成就

你有一个苹果,我有一个苹果,彼此交换一下,我们仍然是各有一个苹果;但你有一种思想,我有一种思想,彼此交换,我们就都有了两种思想,甚至更多。

  • 知识共享,自动互备

在大部分团队,尤其是微服务架构的团队。 通常是一个人员负责多个服务/项目, 如果没有code review, 项目中设计的架构知识,业务知识就只存在于项目过程中产出的说明文档了。

(像我们甚至文档都比较少的)很多设计内容基本只存在于开发人员脑子里。 时间久了,自己都会忘,别人要维护就更难了。

code review 的过程, 至少 Reviewer 必须阅读文档,看代码是否实现相同。 知识的传播性更好,基本不会只有一个人了解某个项目的情况了。

  • 统一风格, 提升质量

代码质量等级: 可以编译通过->可以正常运行->可以测试通过->容易阅读->容易维护 。 Code Review的代码最起码可以达到易阅读这个级别

要做到易阅读,不是只要有Code Review这个环节就可以了,还要有相关的规范,让大家按照同样的工程风格、编码风格去构建项目和编写代码。

统一风格一方面是让大家无论是维护项目还是阅读代码,不用互相适应各自的编码习惯,另外也是给Reviewer一个Code Review的基本依据。

发现Bug不是Code Review的必需品,而是附属品。至于那些低级的问题/bug交给代码扫描工具就可以了,这不是Code Review的职责。

推动code review落地执行

工具

gitlab, 每个项目不同角色, 在合并过程进行 code review

开发规范

  • 工程规范 (工程结构,分层方式,命名等)
  • 命令规范 (接口,类,方法名,变量名)
  • 代码格式 (括号,空格,换行,缩进)
  • 注释规范 (规定必要的注释)
  • 日志规范 (合理的记录必要的日志)
  • 各种推荐和不推荐的代码示例

规范学习网址:

  1. Go Code Review Comments(Go官方编程规范翻译)
  2. Uber 开源的《Go 语言编码规范
  3. Go 最佳实践: 编写可维护 Go 代码

指定流程规范

  • 确定code review 的实施环节

CodeReview建议是放在代码提交测试前,也就是开发人员完成代码开发及自测后将代码提交到测试分支时进行Code Review。毕竟,如果测试通过后再进行Code
Review,如果需要代码变更,势必会增加测试的工作量,甚至影响项目进度。亦或是顶着项目上线的压力,干脆“以后再说”了

以一般的git 工作流程来说, 就是 功能分支 feature 合并到 开发者分支 develop的时候进行 code review

  • 指定角色行为规范

    行为规范

    规范的目的:

    1. 控制提交Code Review的代码的粒度
    2. 控制单次Code Review的时间
    3. 提升Commit/MergeRequest描述的质量,减少沟通成本

通过细粒度高频次的方式尽可能利用工程师碎片化的时间进行Code Review,一定程度上保证Code Review的效率。

分享与统计

对code review 的过程及结果进行检验

  • 定期分享

我们期望CodeReview可以让工程师之间互相学习的,那么对于一次Code Review通常只有参与的2-3个工程师有互相学习的机会,那么在这个过程中学到的知识,定期的分享出来,既可以加强知识的流动,又可以检查大家究竟有没有在Code
Review过程中学习到知识,或者有没有认真的进行Code Review

至于分享的内容,可以是开发规范中的范例代码,也可以是规范中的正例代码,也可以是针对某个功能实现的最佳算法/最佳实践,也可以是Code Review过程中的争议代码,也可以是自己踩过的坑。

  • 数据统计

为了在一定程度上保证Code Review的效率,我们在规范里是要求参与的工程师:

1
2
3
1.Developer控制提交Code Review的粒度,或者控制每个Commit的粒度
2.Developer要准确清晰的描述所提交的代码
3.Reviewer&Approver要在规定时间内完成Code Review

这些情况纯粹靠人工是无法检验的,还是需要有一定的数据统计。

1
2
如果用Gerrit,可以查询Gerrit的数据库,里面会有Code Review的信息,
如果用GitLab,可以通过WebHook或者restful API获取Code Review信息

我们可以做成报表,来展示Code Review的情况:

1
2
3
4
5
1.每人每周Code Review所消耗的时间
2.每人每周被Code Review所消耗的平均时间
3.超过规定时间的Code Review情况
4.代码提交描述字数过少的情况
5.等等(根据自己的需要来)

保证code review质量的关键

  • 工程师 对研发规范的认真学习
  • 资深工程师的认真对待

hexo_blog

使用 hexo 搭建博客

  • 使用命令
1
2
3
4
hexo init   # 空目录初始化
hexo g # 生成
hexo s # 本地跑服务
hexo d # deploy 部署, 上传到 git

插件安装

1
2
3
4
5
6
7

## 安装 图片 插件
npm install https://github.com/CodeFalling/hexo-asset-image --save
## 同时修改配置: post_asset_folder: true

## git部署插件安装, 不然会报git的错误
npm install hexo-deployer-git --save
1
2
3
git page 域名保持不懂

在 source 下创建 CNAME 文件, 放入 域名