Shell脚本基础
基本结构
1 | #!/bin/sh |
变量
脚本所有变量都为字符串
声明变量
1 | nodeList= |
使用变量
1 | cmd=$cmdBuild |
截断
1 | $ list="app _install util _install scripts logs" |
数组
声明数组
1 | defaultNodeList=`ls -d */ | awk '{gsub(/\//,""); print $0}'` |
增加元素
1 | # 数组尾部增加元素 |
删除元素
方法1,字符串截断,列表不能有重复元素1
2delim="_install"
defaultNodeList="${defaultNodeList%%${delim}*} ${defaultNodeList##*${delim}}"
方法2,关键字替换,可以替换所有元素
方法3,重新创建新数组,将指定元素丢弃
判断
单个条件1
2
3
4
5
6
7if [ -z "$1" ]; then
echo "Update cmd is empty, skip it."
elif [ -z "$2" ]; then
echo "Directory($s) is runtime, skip it."
else
echo "All is ok."
fi
多个条件1
2
3if [ ! -z "$nodeList" ] && [ "$cmd" = "$cmdBuild" ]; then
echo "Update cmd is empty, skip it."
fi
switch型1
2
3
4
5
6
7
8case "$1" in
-n) updateNodeList $2; shift; shift; ;;
--start) shift; updateCmd $cmdStart; ;;
--stop) shift; updateCmd $cmdStop; ;;
-b|--build) shift; updateCmd $cmdBuild; ;;
-h|--help) usage; ;;
*) error "Unknown option($1)"; usage; ;;
esac
循环
while型1
2
3
4while [ -n "$1" ]; do
echo "$1"
shift
done
for each型1
2
3for node in $nodeList; do
echo "node: $node"
done
函数
声明函数
1 | info() |
输入参数:$1~$9 分别代表参数1~9. $0为执行脚本文件路径
返回参数:return [RET],值为0-255;不写值为默认0;
调用函数
直接调用1
$ info "Exec cmd $cmd." "All is ok"
获取返回值判断,类似三元表达式1
2
3
4
5
6# 单步处理可以直接写
testPid $pid && echo "$pid is ok" || echo "$pid is failed"
# 多步处理需要增加括号
testPid $pid && info "$node($pid) start done." ||
(error "$node($pid) start failed." && echo `tail -n 5 $pwdPath/$node/${node}_out.log` && exit 255)
常用命令
readlink
返回文件的绝对路径1
2$ readlink -f go.mod
/root/project/softarch/go.mod
dirname
返回文件或文件夹的上级路径1
2$ dirname ~/project/softarch
/root/project
echo
显示字符串1
2echo "hello world"
echo "hello world\n你好,世界\n"
控制显示颜色1
2
3
4
5
6# 绿色字体
echo "\033[32m[INFO] hello world\033[0m"
# 黄色字体
echo "\033[33m[WARN] hello world\033[0m"
# 红色字体
echo "\033[31m[ERROR] hello world\033[0m"
nohup
脱离终端启动程序
启动程序时,获取程序运行pid1
nohup ./$node >$pwdPath/$node/${node}_out.log 2>&1 & echo $!>/tmp/.${node}.pid
组合命令
显示当前路径下所有文件夹名称
1 | $ ls -d */ | awk '{gsub(/\//,""); print $0}' |
源码书籍推荐
Rust
《Linux内核0.11完全注释》0.11
Bochs
qemu+gdb
网上修改版本:https://github.com/yuan-xy/Linux-0.11
ubuntu16.04.71
2
3
4
5
6
7
8# 带UI启动qemu
qemu-system-x86_64 -m 16M -boot a -fda Image -hda $(HDA_IMG) -s -S
# 其他终端启动gdb
gdb tool/system
target remote :1234
# VSCode ssh远程连接后启动调试
launch.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
"version": "0.2.0",
"configurations": [
{
"name": "gdb-0.11",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/Linux-0.11-master/tools/system",
"miDebuggerServerAddress": "127.0.0.1:1234",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/Linux-0.11-master",
"environment": [],
"externalConsole": false,
"MIMode": "gdb"
}
]
}
vscode打印内存地址数据,调试控制台1
2
3
4# 执行gdb命令x,打印0x0处内存,打印32字节
# xb,字节(默认)
# xw,4字节
-exec x/32xb 0x0
《Linux内核源代码情景分析》 2.4.0
《Linux Kernel Development》2.6
qemu+gdb
常用工具-Perf简介
Linux性能调试工具
简介
perf是Linux 2.6+内核中的一个工具,在内核源码包中的位置 tools/perf。
perf利用Linux的trace特性,可以用于实时跟踪,统计event计数(perf stat);或者使用采样(perf record),报告(perf report|script|annotate)的使用方式进行诊断。
perf命令行接口并不能利用所有的Linux trace特性,有些trace需要通过ftrace接口得到。
参考perf-tools。
安装
Ubuntu20.04直接安装1
apt-get install linux-tools-$(uname -r) linux-tools-generic
使用
运行程序后获取pid1
perf record -a -g -v -p <PID> sleep 30
等待30秒后会得到perf.data原始记录文件,直接查看1
perf report -g --tui
生成火焰图perf-main.svg1
2
3
4git clone https://github.com/brendangregg/FlameGraph
mv perf.data FlameGraph/
cd FlameGraph
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > perf-main.svg
perf-main.svg需要使用浏览器打开,即可方便查看
生成热力图类似1
2
3
4
5git clone https://github.com/brendangregg/HeatMap # or download it from github
mv perf.data HeatMap/
cd HeatMap
perf script | awk '{ gsub(/:/, "") } $5 ~ /issue/ { ts[$6, $10] = $4 } $5 ~ /complete/ { if (l = ts[$6, $9]) { printf "%.f %.f\n", $4 * 1000000, ($4 - l) * 1000000; ts[$6, $10] = 0 } }' > out.lat_us
./trace2heatmap.pl --unitstime=us --unitslat=us --maxlat=50000 out.lat_us > out.svg
编程语言-JavaScript学习笔记
入门基础
环境搭建
VSCode + 浏览器
必备插件
HTML CSS Support
官方推荐的插件,自动补全
open in browser
快速在浏览器中查看网页
error lens
报错显示
CSS Peek
追踪样式
Auto Rename Tag
成对修改标签。VS自带的重名名好像已经具备。
工程
常用资料
MDN
核心知识
JavaScript是HTML中的一个script标签,很多地方需要遵守HTML的规范。
结构
js代码存在多种方式:内部、外部、内联。
内部方式1
2
3
4
5
6<body>
<!-- 内部js -->
<script>
// 具体逻辑
</script>
</body>
HTML标签是按照顺序加载进入,一般推荐写在</body>标签之前,可以尽量保障所有标签都能访问到。
外部方式1
2
3
4<body>
<!-- 外部js -->
<script src="js文件所在位置"></script>
</body>
值得注意的是,body标签中引入script,会忽略script标签中的代码。
内联方式
这种方式在前端框架vue中使用比较多。1
2
3
4<body>
<!-- 内联js -->
<button onclick="alert('逗你玩~~~')">点击我月薪过万</button>
</body>
行结尾符
与C++类似使用英文;,但很多场合结尾符是可以省略的,推荐都省略掉。
常用输入输出
window下面的函数调用可以省略window。
| 函数 | 功能说明 |
|---|---|
| window.alert | 弹窗输出信息 |
| document.write | 文档输出信息,支持解析HTML |
| console.log | 控制台输出信息 |
| window.prompt | 弹窗输入信息 |
注释
与C++一行,单行注释使用//,多行注释使用/**/。1
2
3
4
5// 单行注释
/*
多行注释
*/
变量
命名规则
- 不能使用保留关键字
- 使用下划线、字母、数字、$,不能以数字开头
- 区分字母大小写
数字Number
js中数字不区分整型和小数1
2let n1 = 10
let n2 = 3.14
运算异常时,会返回NaN。
布尔Boolean
1 | let b1 = true |
字符串String
字面量支持反引号,可以将数字和字符串输出,类似格式化输出。1
2
3
4let s1 = 'hello'
let s2 = "world"
let s3 = `Pi is ${n2}`
let s4 = '3.14'
可以使用\转义符。
+可以连接两个字符串字面量或变量。
字符串转换数字
当使用+s4可以将字符串转换为数字。
使用函数parseInt或parseFloat。
数组Array
js中数组不要求每个元素要一致,类似其他语言的元组。
简单声明如下:1
2
3
4const arr1 = [1, 2, 3]
const arr2 = ['hello', 'world']
const arr3 = [1, 'hello']
const arr4 = new Array(1, 'hello')
常用数组方法
新增成员
push在数组尾部插入成员
unshift在数组头部插入成员
删除成员
pop删除数组尾部成员
shift删除数组头部成员
splice删除或替换数组指定区域的成员
修改成员
通过下标索引
访问成员
通过下标索引
对象
js中对象支持动态扩展成员及方法,所有对象都是Object。对象默认使用引用方式进行赋值,所以一般使用const进行声明即可。
简单声明1
2
3
4const obj1 = {
uname: 'Tik',
cnt: 0,
}
增加成员
对象.成员 = 值
删除成员
delete 对象.成员
修改成员
对象.成员 = 新值
访问成员
若成员名称符合语法
对象.成员
若名称不符合语法,比如:需要访问'number 1 - cnt'成员
对象[成员]
undefined
默认为赋值的变量都会返回undefined。运算结果一般都为undefined。
null
可以运算,结果与另一个算子相关。
内置Math对象
常用方法
| 方法 | 说明 |
|---|---|
| random | 随机值 |
| ceil | 向上取整 |
| floor | 向下取整 |
| max | 最大值 |
| min | 最小值 |
| pow | 幂运算 |
| abs | 绝对值 |
声明关键词
关键词const声明常量,声明后不能改变常量本身。js中常量本身存放的是对象或数组的地址,因此常量支持修改成员,例如:1
2const arr1 = []
arr1.push(1)
关键词let声明局部变量,同作用域不支持同名变量。
关键词var声明全局变量,无论在代码何处声明,其声明都在所有代码之前。但赋值操作还是保留原位置,推荐使用let替代声明变量。
typeof可以返回变量的类型。
运算符
算术运算符
与其他语言基本运算符一致。
特别的是对==和!=做了扩充,两等只判断值是否相等。===和!==不仅仅判断值是否相等,同时要判断类型是否相等。比如:1
2
3
4
5
6
7const str = '3.14'
const num = 3.14
console.log(`str == num : ${str == num}`)
console.log(`str === num : ${str === num}`)
// str == num : true
// str === num : false
运算优先级
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 1 | ( ) | |
| 2 | ++ -- ! | |
| 3 | * / % + - | +和-优先级较低 |
| 4 | > >= < <= | |
| 5 | == != === !== | |
| 6 | && || | ||优先级较低 |
| 7 | = | |
| 8 | , |
逻辑中断
原理是表达式逻辑判断时,遇到局部值时确定表达式返回值时会直接返回,此时局部值为表达式值。
或逻辑
A||B,A为真则为A,否则为B。适用于变量赋值A是表达式,B是默认值。
与逻辑
A&&B,A为假则为A,否则为B。
控制语句
与其他语言类似:if for while。
函数
普通函数
支持将声明放在作用域尾部。
声明方式
function 函数名(参数列表){函数逻辑}
调用方式
匿名函数
使用方式
作为函数参数
function (参数列表) {}
作为函数变量
let fun1 = function (参数列表) {}
调用方式
(function (参数列表) {})()
(function (参数列表) {}())
箭头函数Arrow function
与普通函数不同点是,箭头函数没有自己的this和arguments动态参数,只能使用剩余参数。
基本格式:()=>{}
省略规则
- 只有一个形参可以省略
(),x=>{} - 只有一行代码可以省略大括号,此时return需要省略
- 只有一行返回对象时需要加
(),=> ({uname:uname})
可变参数
arguments,函数属性,动态参数,是伪数组
...xxx,剩余参数,是真数组
DOM
文档对象模型(DOM,Document Object Model)是一组API,用于操作网页元素。在js中以树状结构组成,通过操作各对象(树节点)的属性和HTML结构。
document
document.documentElement HTML的根节点
基本操作
对象属性
元素内容顶部到它的视口可见内容的顶部的距离
scrollTop 顶部的距离
scrollLeft 左边的距离
元素布局尺寸,包含padding border
offsetWidth 宽度
offsetHeight 高度
元素布局位置,受父级元素定位影响
offsetLeft 元素左边相对父级元素的位置
offsetTop 元素顶部相对父级元素的位置
children 返回仅元素节点的伪数组
nextElementSibling 返回下一兄弟节点
previousElementSibling 返回上以兄弟节点
对象方法
getBoundingClientRect 返回元素的大小及其相对于视口的位置
parentNode 返回父级节点
childNodes 返回所有子节点,包括文本、注释
appendChild 在子节点列表增加节点
insertBefore 在子节点列表参考节点前插入节点
cloneNode 克隆对象,默认false不克隆子节点
removeChild 删除子节点
获取对象
获取第一个元素querySelector('css选择器')
返回所有匹配的元素querySelectorAll('css选择器')
css选择器简单回忆
- 选择指定类
class='active':.active - 选择特定标签
div:div - 选择特定子级标签
<div><p class='active'><i></i></p></div>:div i - 选择特定亲子级标签
<div><p class='active'></p></div>:div>.active - 选择特定序号子级标签
<div><p class='active'></p><p></p><p></p></div>:div p:nth-child(2)
修改对象
修改元素内容
innerText 不会解析子标签
innerHTML 会解析子标签
修改元素的对象属性
href 跳转地址
title 标题
src 图片源
修改元素样式
直接修改style属性。需要注意,使用下划线的要改为小驼峰方式,没有自动补全。
基本格式
对象.style.样式属性 = 值
优点
- 适合小范围样式调整
- 行内样式,权重很高
通过className操作css
基本格式
对象.className = '类名或列表(空格分隔)'
优点
- 直接替换旧值
:thumbsup:通过classList控制css
注意,类名不能加英文.
基本格式
- 追加,
对象.classList.add('类名') - 删除,
对象.classList.remove('类名') - 切换,
对象.classList.toggle('类名')
优点
- 小范围批量更改样式,旧样式不受影响
操作表单属性
基本格式
表单.value = 值
表单.type = 类型值
部分表单属性使用布尔值表示,例如:disabled checked selected
标签支持自定义属性,自定义属性在HTML中以data-自定义属性名格式最为名称。js需要获取自定义属性时,使用对象.dataset.自定义属性名
Date对象
新建
new Date()
new Date('2022-5-1 8:30')
方法
getFullYear() 获取年份
getMonth() 获取月份,0代表1月
getDate() 返回月份中的天
getDay() 获取星期,0代表星期天
getHours() 获取小时数
getMinutes() 获取分钟数
getSeconds() 获取秒数
获取毫秒时间戳
new Date().getTime()
+new Date()
Date.now()
Document对象
对象属性
document.documentElement.scrollTop = Y
对象方法
write 向文档中写入数据
createElement 创建特定元素节点
Console对象
console.log
事件
基本概念
简介
在系统内发生的动作或事情。要产生事件并完成处理,需要
- 事件源,即
dom对象 - 事件类型
- 事件处理函数
事件监听经历多个版本。
DOM L0
基本格式为:对象.on事件类型 = 处理函数
不支持对同一事件多次绑定。
:thumbsup:DOM L2
基本格式:对象.addEventLisener('事件类型', 处理函数)
支持对同一事件多次绑定,推荐使用。
事件流
指事件完整执行的流动路径。
addEventListener第三个参数为是否在捕获阶段触发,默认为false为冒泡阶段。
事件流主要两个阶段:
- 捕获阶段,Document->Element html->Element body->Element div
- 冒泡阶段,Element div->Element body->Element html->Document
阻止事件流(事件冒泡)
事件对象.stopPropagation()
事件委托
减少注册次数,提高程序性能。
利用冒泡的特点来完成。
通过父级元素来处理多个子级元素的事件。
基本操作
事件绑定
基本格式:对象.addEventLisener('事件类型', 处理函数)
其中,事件类型见下面的章节。
处理函数可以带参数,第一个参数为Event对象,一般参数命名为event或e。
Event对象重要属性
target实际出发事件的对象type事件类型clientX/Y光标相对浏览器的位置offsetX/Y光标相对DOM对象的位置key用户按下键盘值(‘Enter’ ‘a’ ‘A’)
普通处理函数中,this会指向target。
事件解绑
L0 对象.on事件类型 = null
L2 对象.removeEventListener(事件类型,回调函数,关键参数)
需要注意的是,L2绑定的方式不支持匿名解绑。
事件类型
鼠标事件
click鼠标点击:thumbsup:mouseenter鼠标进入,没有冒泡效果:thumbsup:mouseleave鼠标离开,没有冒泡效果:thumbsup:mouseover鼠标划过,有冒泡效果mouseout鼠标离开,有冒泡效果
焦点事件
focus获得焦点blur失去焦点
键盘事件
Keydown按键按下Keyup按键抬起
文本事件
input用户输入change内容发生变化
多媒体事件
timeupdate音视频当前播放位置变化loadeddata音视频当前播放帧加载完毕
页面事件
load等待特定资源加载完毕DOMContentLoaded等待HTML节点加载完成resize当页面尺寸发生变化时
触摸屏事件
touchstart触摸开始touchmove触摸后并发生移动touchend触摸结束
BOM
基本结构
window对象作为根节点,包含如下节点对象:
navigatorlocationdocumenthistoryscreen
Window对象
对象属性
window.scrollTo(X,Y)
定时器
时间到时触发回调函数,并自动开启下次定时
setInterval 开启定时器
clearInterval 关闭定时器
输入
弹出输入框
prompt(消息,默认值)
输出
弹出警告框
alert(消息)
定时器-延时函数
时间到后出发回调函数,不会自动开启下次定时
setTimeout设置定时器clearTimeout删除定时器
本地存储
数据存储在用户浏览器中。设置、读取方便、甚至页面刷新不丢失数据。容量较大,sessionStorage和localStorage大约可以存储5M大小的数据。
所有方法都支持KV方式存储。
localStorage
数据生命周期没有过期时间,直到用户主动清理。
实例方法
setItem 存储值
getItem 读取值
sessionStorage
用法同localStorage,生命周期与浏览器窗口一致。同一页面数据共享。
复杂数据类型处理
核心是将对象转换为json字符串
JSON.stringify 将对象序列化为json字符串
JSON.parse 将json字符串反序列化为对象
Location对象
对象属性
href 直接跳转到指定的网页 search 返回地址中?开始的参数 hash 返回#开始的哈希值
对象方法
reload 刷新页面
Navigator对象
对象属性
userAgent 检测浏览器信息,可以用于检测客户端平台
History对象
对象方法
back 网页后退 forward 网页前进 go 网页前进或后退
JS执行机制
最大特点为单线程。所有任务需要排队执行。任务可以分为两种:
- 同步任务。在主线程上执行,形成执行栈。
- 异步任务。
js的异步任务是通过回调实现的。
执行流程:
- 先执行执行栈的同步任务。
- 异步任务放入任务队列中。
- 执行栈中的所有同步任务执行完,会依次读取任务队列的异步任务,异步任务结束等待状态,进入执行栈,开始执行。
这种机制被称为事件循环(event loop)
特殊技巧
第三方推荐
swiper
触摸滑动插件swiper官网
正则表达式
语法
对象声明:const 变量名 = /表达式/
对象方法
test 返回是否匹配
exec 返回匹配的数组
字符串.replace 是字符串的方法,替换匹配文本
元字符
边界符:^ $
量词:* + ? {n} {n,} {m,n}
字符类:[abc] [a-z] [^abc] .
预定义:\d [0-9] \D [^0-9] \w [A-Za-z0-9_] \W [^A-Za-z0-9_] \s [\t\r\n\v\f] \S [^\t\r\n\v\f]
修饰符
使用格式:/表达式/修饰符
i 不区分大小写
g 全局匹配
参考资料
进阶用法
作用域
局部 外部无法访问 var声明的是全局变量
作用域链 优先在当前作用域查找 逐级查找父级作用域直到全局
GC
垃圾回收机制(GC,Garbage Collection)
计数方式 复杂对象都有计数,计数为0则会被回收 无法解决嵌套引用(循环引用) 标记清除法 “无法到达的对象”
闭包
一个函数对周围状态的引用捆绑在一起 简单理解 闭包=内层函数+外层函数的变量 可能会引起内存泄漏
展开符号
let arr = [1,2,3] console.log(…arr) 求最值 Math.max(…arr) Math.min(…arr) 合并数组 arr = […arr1, …arr2]
解构赋值
数组解构 目标是简洁语法和快速为变量赋值 将数组的单元值快速批量赋值给变量的简洁语法 const [max,min,avg]=[5,1,3] let [a, b] = [1, 2]; [b, a] = [a, b] 剩余参数 const [a,…b] = [1,2,3] 可以设置默认值 const [a=0,b=0]=[] 忽略部分值 const [a,,c] = [1,2,3] 多维数组 const [a,b,[c,d]]=[1,2,[3,4]] 对象解构 const { uname, age } = { uname: ‘pink’, age: 18 } 被赋值的变量与属性名称需要一致 重命名 const { uname: username, age: userage } = { uname: ‘pink9’, age: 19} 多级对象 const {name,family:{mother,father}}={name:‘佩奇’, family:{mother:‘猪妈妈’,father:‘猪爸爸’}}
拷贝
浅拷贝 深拷贝 递归实现 lodash/cloneDeep实现 JSON字符串转换
异常处理
抛异常 throw new Error(‘参数不能为空’) 异常捕获 try{}catch(err){} 异常时也会执行 finally{} 自动启动调试停止处 debugger
改变this
直接调用 fn.call(new_this, arg_list…) 直接调用,参数是数组 fn.apply(new_this [,arg_list…]) 先绑定返回新的函数 fn.bind(new_this)
防抖
原理 规定时间内,每次触发都会取消上次执行,只执行最后一次动作 场景 搜索框智能搜索 输入框输入检测 loadash提供防抖处理 使用setTimeout实现
节流-throttle
原理 规定时间内,每次触发都会检测上次执行是否完成,只有完成后才会执行新的动作 场景 高频事件 鼠标移动 页面尺寸缩放 滚动条变化 多媒体播放进度更新 loadash提供节流处理 使用setTimeout实现
面向对象
对象
创建 字面量 const obj = {name:’‘} const obj = new Object({name:’‘}) 构造函数 function Good(name){ this.name = name } const obj = new Good(’’) 属性 Good.count = 5 静态属性 方法 Good.print = function(){…} 静态方法
内置对象
Object Object.keys 返回对象所有属性键 Object.values 返回对象所有值 Object.assign 对象拷贝(与克隆不一样,没有的值不会覆盖) Array forEach 遍历数组 filter 过滤数组 map 迭代数组 reduce 累加器 其他常用 join find every some concat sort splice reverse findIndex Array.from 伪数组转为真数组 Number toFixed 设置保留小数位的长度
面向对象
简介 每个构造函数有prototype原型对象 prototype默认会有constructor属性,指向构造函数 所有对象实例都有一个属性__proto__指向prototype 浏览器中显示为[[Prototype]] 构造函数和原型对象中的this都指向实例化对象 手动赋值prototype Star.prototype={ constructor: Star, sing:… dance:… } 继承 原型继承 Man.prototype = new Person() Man.prototype.constructor = Man 原型链 原型对象会指向父级? 顶级对象是Object 当访问属性或方法时,优先查找本地,没有再到父级中查找,直到为null instanceof
常用工具-Valgrind简介
Linux内存泄漏工具
简介
详细信息可以查阅官方网站。
Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。
安装
ubuntu20.04直接安装,版本为3.15.01
apt-get install valgrind
使用
准备程序
需要使用-g将调试信息编译到程序中,以便Memcheck工具输出准确行数。优化参数最好是使用-O0或-O1,使用-O2及以上等级或存在错误上报的可能。
Memcheck运行程序
若程序正常运行为1
myprog arg1 arg2
则使用valgrind运行时1
valgrind --leak-check=all myprog arg1 arg2
Memcheck工具是默认项。--leak-check选项指定检查项细节。
程序会比正常运行慢一些,并会占用更多的内存。
常见错误检测
- 内存未释放(definitely/indirectly/possibly lost)
- 内存越界访问(Invalid read/write)
- 内存释放后读写(Invalid read/write)
- 内存错误释放(Mismatched free() / delete / delete [])
- 内存重复释放(Invalid free() / delete / delete[] / realloc())
常用工具-Git简介
流程介绍
基本原则
简单。不增加过多学习成本,开发测试快速迭代。
可追溯。每个现场环境运行的软件版本号都由主仓发布,主仓关键分支禁用代码强制提交。
简化流程
所有开发人员都从主仓的开发分支克隆(clone)代码。在本地修改代码,本地仓新增(add)提交。待所有代码调试完毕,准备推送(push)到主仓。在推送主仓前先获取(fetch)主仓最新提交记录。
完整流程
私有仓从主仓派生(fork),本地仓从私有仓克隆(clone)。
主仓所有开发人员只能发起合并请求(merge),禁止发起推送请求(push),经仓负责人检视后合入主仓。主仓只有开发(develop)分支接受私有仓代码合并请求,其他分支不接受私有仓代码合并请求。
版本管理
版本号规范
版本号基本结构:v主版本号.子版本号.修订版本号。版本号会以标签(TAG)的方式指向各分支的某次提交记录。
参考:产品中心发布的版本号命名规范
主版本号
- 大的代码重构导致此版本号变更;
- 主版本号变更可能导致向下兼容问题;
子版本号
- 有大的功能变动;
- 可能影响向下兼容,但是可以通过技术手段进行数据或者业务转换;
修订版本号
- 修改程序内部 bug,程序功能无变化;
- 绝对向下兼容;
分支策略
- develop(开发分支):主仓默认分支,日常开发活跃分支,一般包含未经严格测试的提交记录。
- v主版本号.子版本号.x(发布分支):是测试或现场环境运行的软件版本对应的代码。
以上两类分支会开启分支保护策略,禁止强制推送。
修订版本号为0的标签会指向开发分支和相应的发布分支,而修订版本号非0的标签只会指向相应的发布分支。
例如:版本号为v1.6.0的标签会同时指向开发分支和v1.6.x的发布分支。而版本号为v1.6.2的标签只会指向v1.6.x的发布分支。
提交记录
提交记录信息基本结构:提交类型[问题单号或任务编号]: 提交信息
提交类型
- feat(功能特性):主要是核心代码新增或修改功能特性,包括性能优化和代码重构;
- fix(修复问题):主要是修复核心代码中发现的问题;
- other(其他事务):主要是新增或修改非核心代码,比如构建脚本、代码版本号、新增环境等等。
提交信息
提交信息尽量简短(100字以内)介绍本次提交的目的
提交信息尽量携带问题单号或任务编号
单次提交实现单个特性或修复单个问题
标准提交示例1
2
3
4
5
6
7
8
9# 功能特性提交记录
【推荐】feat1549: 增加登录功能,并对用户权限进行校验
【不推荐】feat: 修改登录功能,忽略对管理员权限进行校验
# 修复问题提交记录
【推荐】fix8317: 修复校验用户权限时,并发访问公共队列造成崩溃的问题
【不推荐】fix: 修复校验管理员权限时,跳过校验普通用户权限的问题
# 其他事务提交记录
【推荐】other1349: 部署脚本打包增加检索服务
【不推荐】other: 部署版本信息修改为v1.8.5
提交策略
功能特性和其他事务的提交都只需要合并到开发分支即可;
修复问题的提交除了合并到对应的发布分支上,还需要调整后合并到开发分支中;
测试发布流程
对于分支的提测,若没有相应的发布分支,项目负责人需要在新建
对于发布分支的提测,项目负责人只需在发布分支上打v主版本号.子版本号.x的标签和记录Changelog。
常用命令
以下命令格式常用命令格式,详细可选参数可以查询Git手册获取。
约定
<XX>必填参数
[XX]可选参数
REPO_NAMEGit仓库名称,示例:origin
REPOGit仓库地址,示例:http://ubuntu.alfdxl.top/gitea-admin/git-template.git
BRANCH分支名称,示例:feat_adminuser
TAG标签名称,示例:v1.2.6
MESSAGE提交消息,示例:feat698: 新增创建管理员用户和查询用户信息功能
ID提交记录,示例:3110aa8502d24022cd08ba37d5dacd5160709214
PATH文件夹路径,示例:template
HEADGit保留字段,指向分支当前提交记录
clone
克隆一个Git仓库到本地,可以查看或修改此项目。基本格式:git clone <REPO> [PATH]
Git仓库地址支持HTTP和SSH协议。HTTP协议不会保存用户信息,在拷贝过程中也不会验证用户权限。但在推送本地信息时会要求用户进行验证,常常用于代码查看活动。SSH协议本身已经保存用户信息,推送本地信息时不会要求用户输入信息,常常用于代码开发活动。SSH协议需要提前配置,Gitlab的相关配置可以参考文章。
使用示例:1
2
3
4
5
6
7
8
9# 克隆代码到本地
$ git clone http://ubuntu.alfdxl.top/gitea-admin/git-template.git template
Cloning into 'template'...
remote: Enumerating objects: 61, done.
remote: Counting objects: 100% (61/61), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 61 (delta 9), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (61/61), 5.07 KiB | 2.53 MiB/s, done.
Resolving deltas: 100% (9/9), done.
remote
管理远程Git仓库,每个仓库都有名称,默认克隆的仓库名称都为origin。
查看远程仓详情:git remote -v
新增远程仓:git remote add <REPO_NAME> <REPO>
删除远程仓:git remote rm <REPO_NAME>
使用示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 查看远程仓
$ git remote -v
origin http://ubuntu.alfdxl.top/gitea-admin/git-template.git (fetch)
origin http://ubuntu.alfdxl.top/gitea-admin/git-template.git (push)
# 新增远程仓
$ git remote add ssh_remote ssh://git@ubuntu.alfdxl.top:222/gitea-admin/git-template.git
$ git remote -v
origin http://ubuntu.alfdxl.top/gitea-admin/git-template.git (fetch)
origin http://ubuntu.alfdxl.top/gitea-admin/git-template.git (push)
ssh_remote ssh://git@ubuntu.alfdxl.top:222/gitea-admin/git-template.git (fetch)
ssh_remote ssh://git@ubuntu.alfdxl.top:222/gitea-admin/git-template.git (push)
# 删除远程仓
$ git remote rm ssh_remote
$ git remote -v
origin http://ubuntu.alfdxl.top/gitea-admin/git-template.git (fetch)
origin http://ubuntu.alfdxl.top/gitea-admin/git-template.git (push)
checkout
分支切换和恢复工作区,基本格式:git checkout <BRANCH>
使用示例:1
2
3
4
5
6
7
8
9# 本地拉取远程指定分支代码
$ git checkout -b feat_adminuser origin/feat_adminuser
Branch 'feat_adminuser' set up to track remote branch 'feat_adminuser' from 'origin'.
Switched to a new branch 'feat_adminuser'
# 切换分支
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
branch
代码分支管理。
查看所有分支:git branch -a
删除分支:git branch -d <BRANCH>
使用示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14# 查看所有分支
$ git branch -a
* feat_adminuser
master
remotes/origin/HEAD -> origin/master
remotes/origin/feat_adminuser
remotes/origin/feat_build
remotes/origin/master
# 删除分支
$ git branch -d feat_adminuser
warning: deleting branch 'feat_adminuser' that has been merged to
'refs/remotes/origin/feat_adminuser', but not yet merged to HEAD.
Deleted branch feat_adminuser (was 3110aa8).
log
查看提交记录。基本格式:git log
使用示例: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
26
27
28
29
30
31# 查看提交记录
$ git log
commit 3110aa8502d24022cd08ba37d5dacd5160709214 (HEAD -> feat_adminuser, tag: v1.2.3, origin/feat_adminuser)
Author: 作者姓名 <作者邮箱>
Date: Tue May 10 11:41:36 2022 +0800
feat698: 新增创建管理员用户和查询用户信息功能;
commit 82f2ba7b82fa91335f3d226e3fee61cedea65945
Author: 作者姓名 <作者邮箱>
Date: Mon May 9 16:33:14 2022 +0800
fix696: 修复修改用户名都为常量的问题
commit bbb843b97d714df441275c488c8bd2f131efe42c (tag: v1.2.6, origin/master, origin/HEAD, master)
Author: zhangsan <zhangsan1598@gmail.com>
Date: Mon May 9 16:09:27 2022 +0800
feat1599: 新增修改用户名称的功能
commit 1ad9bce14ad8548c5b28bdd91398a3e4664909c5
Author: 作者姓名 <作者邮箱>
Date: Mon May 9 14:59:13 2022 +0800
feat1598: 新增创建用户的功能
commit 89b4f2cf3cd950686708d390126dec20da330257
Author: 作者姓名 <作者邮箱>
Date: Mon May 9 14:50:36 2022 +0800
other1598: 建立代码仓库
tag
标签管理。基本格式:git tag <TAG>
查看本地标签:git tag -l
新增本地标签:git tag <TAG>
删除本地标签:git tag -d <TAG>
使用示例:1
2
3
4
5
6
7
8
9
10
11# 查看本地标签
$ git tag -l
v1.2.3
v1.2.6
# 新增本地标签
$ git tag v1.2.3
# 删除本地标签
$ git tag -d v1.2.3
Deleted tag 'v1.2.3' (was 3110aa8)
add/rm
暂存区文件管理。暂存区的代码不会生成提交记录。
暂存区新增文件:git add <FILE>
暂存区删除文件:git rm <FILE>
使用示例:1
2
3
4
5
6
7
8# 暂存区新增文件
$ git add .
$ git add gw/*
$ git add gw/cmd/main.go
# 暂存区删除文件
$ git rm gw/internal/info/version.go
rm 'gw/internal/info/version.go'
commit
将暂存区的文件提交到本地仓。基本格式:git commit -m <MESSAGE>
使用示例:1
2
3
4
5# 提交到本地仓
$ git commit -m "fix866: 修复版本不匹配引起服务崩溃问题"
[master 3d4bf3c] fix866: 修复版本不匹配引起服务崩溃问题
3 files changed, 1 insertion(+), 6 deletions(-)
delete mode 100644 gw/internal/info/version.go
status
工作区状态查询。基本格式:git status
使用示例: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
26
27
28
29
30
31
32
33
34# 无任何修改或提交的工作区状态查询
$ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
# 有修改为提交的工作区状态查询
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: gw/cmd/main.go
deleted: gw/internal/info/version.go
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: go.mod
Untracked files:
(use "git add <file>..." to include in what will be committed)
ReadMe.md
# 有提交未推送的工作区状态查询
$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
reset
重置工作区指向的提交,即可以回退版本也可以撤销回退。
强制重置工作区:git reset --hard <ID>
工作区回退N次提交:git reset --hard HEAD~<N>
撤销工作区提交:git reset --soft <ID>
工作区撤销N次提交:git reset --soft HEAD~<N>
使用示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 强制重置工作区
$ git reset --hard 82f2ba7b82fa91335f3d226e3fee61cedea65945
HEAD is now at 82f2ba7 fix696: 修复修改用户名都为常量的问题
$ git reset --hard v1.2.3
HEAD is now at 3110aa8 feat698: 新增创建管理员用户和查询用户信息功能;
$ git reset --hard origin/master
HEAD is now at bbb843b feat1599: 新增修改用户名称的功能
# 工作区回退2次提交
$ git reset --hard HEAD~2
HEAD is now at 82f2ba7 fix696: 修复修改用户名都为常量的问题
# 撤销工作区提交
$ git reset --soft 82f2ba7b82fa91335f3d226e3fee61cedea65945
# 工作区撤销2次提交
$ git reset --soft HEAD~2
pull
获取远程仓最新提交记录并与本地记录进行合并。基本格式:git pull [REPO_NAME]
使用示例:1
2
3
4
5
6
7
8
9
10# 拉取代码
$ git pull
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), 435 bytes | 435.00 KiB/s, done.
From http://ubuntu.alfdxl.top/gitea-admin/git-template
fbd7e0a..01df67e feat_adminuser -> origin/feat_adminuser
Already up to date.
fetch
获取远程仓提交记录。基本格式:git fetch [REPO_NAME]
使用示例:1
2
3
4
5
6
7
8
9
10
11
12# 远程仓无变更
$ git fetch
# 远程仓有变更
$ git fetch
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), 433 bytes | 433.00 KiB/s, done.
From http://ubuntu.alfdxl.top/gitea-admin/git-template
3110aa8..fbd7e0a feat_adminuser -> origin/feat_adminuser
rebase
对本地分支进行变基操作。即缓存所有本地提交,将本地的基底与远程仓合并,再依次提交缓存的本地提交。与pull操作最大的不同是rebase操作可以保持提交记录的清晰度,参考文章。基本格式:git rebase [REPO_NAME]
使用示例:1
2
3
4
5
6
7
8
9
10
11
12# 变基操作
$ git pull
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), 435 bytes | 435.00 KiB/s, done.
From http://ubuntu.alfdxl.top/gitea-admin/git-template
fbd7e0a..01df67e feat_adminuser -> origin/feat_adminuser
Already up to date.
$ git rebase
Successfully rebased and updated refs/heads/feat_adminuser.
push
推送本地记录到远程仓。
推送提交记录:git push [REPO] [BRANCH]
推送标签记录:git push --tags [REPO] [BRANCH]
使用示例: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# 推送本地提交记录
$ git push
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 453 bytes | 453.00 KiB/s, done.
Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a new pull request for 'feat_adminuser':
remote: http://ubuntu.alfdxl.top/gitea-admin/git-template/compare/master...feat_adminuser
remote:
remote: . Processing 1 references
remote: Processed 1 references in total
To ssh://ubuntu.alfdxl.top:222/gitea-admin/git-template.git
3110aa8..fbd7e0a feat_adminuser -> feat_adminuser
# 推送标签记录
$ git push --tags
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote: .. Processing 2 references
remote: Processed 2 references in total
To http://ubuntu.alfdxl.top/gitea-admin/git-template.git
* [new tag] v1.2.3 -> v1.2.3
* [new tag] v1.2.6 -> v1.2.6
配置
配置用户名、邮箱1
2git config --global user.name "名字"
git config --global user.email "邮箱"
配置编辑器1
git config --global core.editor vim
配置日志显示格式1
2
3
4
5
6
7
8
9
10
11
12
13# 隐藏Merge --no-merges
# 过滤作者 --author='你的名字!自己修改!'
git config --global alias.lm "log --no-merges --color --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
# 显示效果
git config --global alias.lms "log --no-merges --color --stat --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
git config --global alias.ls "log --no-merges --color --graph --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
git config --global alias.lss "log --no-merges --color --stat --graph --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
git lm -n 5 | cat
git lm显示效果:
git lms显示效果:
git ls显示效果:
git lss显示效果:
Ubuntu20使用
网络配置
网络配置改用netplan,配置方式与以前版本不同。参考Canonical Netplan
其中enp0s3使用的是主机网络(用于主机中局域网络访问),enp0s8使用的是VirtualBox自带的NAT网络(用于访问互联网)。修改配置文件/etc/netplan/00-installer-config.yaml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28# This is the network config written by 'subiquity'
network:
renderer: networkd # NetworkManager
ethernets:
enp0s3:
dhcp4: no
dhcp6: no
addresses: [192.168.56.101/24]
# gateway4: 192.168.56.1
# optional: true
# nameservers:
# addresses: [192.168.56.1,8.8.8.8]
# routes:
# - to: default
# via: 192.168.56.1
# routes:
# - to: 192.168.56.0/24
# via: 192.168.56.1
# table: 102
# routing-policy:
# - from: 192.168.56.0/24
# table: 102
enp0s8:
dhcp4: yes
dhcp6: no
# nameservers:
# addresses: [10.0.3.1,8.8.8.8]
version: 2
从零实现Ada编译器-参考资料
官方语言参考手册:Ada Standard, Rationale and other Documents (ada-europe.org)
提供一个实现编译器简单思路:从零开始实现一个简单编译器 - 知乎 (zhihu.com)
参考各类资料:Commercial software solutions for Ada, C and C++ | AdaCore,BNF表示的Ada-2012语法:Syntax Summary (adacore.com)
考虑到实现所有Ada语法非常多,可以考虑只实现部分。