Zookeeper

Zookeeper是一个协调软件服务,用于构建可靠的、分布式群组

  • 提供群组成员维护、领导人选举、工作流协同、分布式系统同步 、命名、配置信息维护等服务

  • 提供广义的分布式数据结构:锁、队列、屏障、锁存器

  • Zookeeper促进户端间的松耦合,提供最终一致的、类似传统 文件系统中文件、目录的Znode视图,提供基本操作,如:创建 、删除、检查Znode是否存在

  • 提供事件驱动模型,客户端能观察到Znode的变化

  • Zookeeper运行多个Zookeeper Ensemble以获得高可用性,每个 服务器上的Ensemble都持有分布式系统内存副本,为客户端读取 请求提供服务

zookeeper_structure

Flume

Flume是分布式日志收集系统,收集日志、事件等数据资源,并集中 存储

Flume组件、结构

  • 旧版本组件、结构 flume_struture_old_version

  • 新版本组件、结构:每个Flume整体称为Agent flume_dataflow

    • 两个版本的组件功能、数据流结构都有区别
    • 但是3大组件基本可以一一对应(功能略有差异)
  • Agent是一组独立的JVM守护进程,从客户端、其他Agent接收 数据、迅速传递给下个目的节点

  • 支持多路径流量、多管道接入流量、多管道接出流量、上下文 路由

Source(Agent)

采集数据,是Flume产生数据流的地方

  • 运行在数据发生器所在的服务器上,接收数据发生器接受数据, 将数据以event格式传递给一个或多个Channel

  • 支持多种数据接收方式

    • Avro Source:支持Avro RPC协议,内置支持
    • Thrift Source:支持Thrift协议
    • Exec Source:支持Unix标准输出
    • JMS Source:从JMS(消息、主题)读取数据
    • Spooling Directory Source:监控指定目录内数据变更
    • Twitter 1% firehose Source:通过API持续下载Twitter 数据
    • Netcat Source:监控端口,将流经端口的每个文本行数据 作为Event输入
    • Sequence Generator Source:序列生成器数据源
    • HTTP Source:基于POST、GET方式数据源,支持JSON、BLOB 格式
  • 收集数据模式

    • Push Source:外部系统主动将数据推送到Flume中,如 RPC、syslog
    • Polling Source:Flume主动从外部系统获取数据,如 text、exec

Channel (Collector)

暂时的存储容器,缓存接收到的event格式的数据,直到被sink消费

  • 在source、sink间起桥梁作用

  • Channel基于事务传递Event,保证数据在收发时的一致性

  • Channel可以和任意数量source、sink连接

  • 主要Channel类型有

    • JDBC channel:数据持久化在数据库中,内置支持Derby
    • File Channel:数据存储在磁盘文件中
    • Memory Channel:数据存储在内存中
    • Spillable Meemory Channel:优先存在内存中,内存队列 满则持久到磁盘中
    • Custom Channel:自定义Channel实现

Sink(Storage Tier)

将从Channel接收数据存储到集中存储器中(HDFS、HBase)

Flume Event

Flume事件是内部数据传输最小基本单元、事务处理基本单位

  • 由一个装载数据的byte array、可选header构成
    • 数据对Flume是不透明的
    • header是容纳键值对字符串的无需集合,键在集合内唯一
    • header可以在上下文路由中使用扩展,如:数据清洗
  • Event将传输数据进行封装

Flume架构特性(旧版)

Reliablity

Flume提供了3种数据可靠性选项

  • End-to-End:使用磁盘日志、接受端Ack的方式,保证Flume接收 数据最终到导致目的地

  • Store on Failure:目的地不可用时,将数据保存在本地硬盘, 但进程如果出问题,可能丢失部分数据(发送后目的地不可用)

  • Best Effort:不做任何QoS保证

Scalability

易扩展性

  • Flume三大组件都是可伸缩的
  • Flume对事件的处理不需要带状态,Scalability容易实现

Avaliablity

高可用性:Flume引入Zookeeper用于保存配置数据

  • Zookeeper本身可以保证配置数据一致性、高可用
  • 在配置数据发生变化时,Zookeeper通知Flume Master节点 Flume Master节点通过gossip协议同步数据

flume_distributed_deployment

Manageablity

易管理性

  • 多个Master,保证可以管理大量节点

Extensibility

可开发性:可以基于Java为Flume添加各种新功能

  • 实现Source子类,自定义数据接入方式
  • 实现Sink子类,将数据写入特定目标
  • 实现SinkDecorator子类,对数据进行一定的预处理

适合场景

  • 高效率的从多个网站服务器收集日志信息存储在HDFS上
  • 将从多个服务器获取的数据迅速移交给Hadoop
  • 可以收集社交网络站点事件数据,如:facebook、amazon

Kafka

分布式、分区的、可复制的Message System(提交日志服务), 得益于特有的设计,Kafka具有高性能、高可扩展的特点

  • 完全分布式系统,易于横向扩展、处理极大规模数据
  • 同时为发布、订阅提供极高吞吐能力
  • 支持多订阅,出现失败状态时,可以自动平衡消费者
  • 将消息持久化到磁盘,保证消息系统可靠性,可用于消息 批量消费应用(ETL系统)、实时应用

Kafka组件

kafka_structure

Topic

话题:特定类型的消息流

  • 话题是消息的分类机制

    • 消息产生器向Kafka发布消息必须指定话题
  • Kafka安照Topic维护接收到的消息

    • 话题被划分为一系列分区
    • Kafka集群为每个Topic维护一个分区日志文件存储消息

消息是字节的Payload(有效载荷)

Producer

生产者:向Kafka发布消息的进程

  • 生产者需要指定消息分配至哪个分区
    • 采用Round-Robin方式方便均衡负载
    • 根据应用的语义要求,设置专用Partition Function进行 消息分区

Broker

代理:AMQP客户端,保存已经发布消息的服务器进程

AMQP:the Advanced Message Queuing Protocal,标准开放 的应用层消息中间件协议。AMQP定义了通过网络发送的字节流 的数据格式,兼容性非常好,任何实现AMQP协议的程序可以和 兼容AMQP协议兼容的其他应用程序交互,容易做到跨语言、 跨平台。

  • 一组代理服务器构成Kafka集群

  • Kafka代理是无状态的,消费者需要自行维护已消费状态信息

    • 因此Kafka无法知晓信息是否已经被消费、应该删除,因此 代理使用简单的、基于时间的Serice Level Agreement应用 于保留策略,消息在代理中超过一定时间自动删除

    • 这种设计允许消费者可以重复消费已消费数据

      • 虽然违反队列常见约定
      • 但是实际应用中很多消费者有这种特征
  • 消息代理将紧密耦合的系统设计解耦,可以对未及时处理的消息 进行缓存

    • 提高了吞吐能力
    • 提供了分区、复制、容错支持
  • Kafka代理通过Zookeeper与其他Kafka代理协同 kafka_with_zookeeper

    • 系统中新增代理或代理故障失效时,Zookeeper通知生产者 、消费者
    • 生产者、消费者据此开始同其他代理协同工作

Consumer

消费者:向Kafka subscribe话题,以处理Kafka消息的进程

  • 消费者可以订阅一个或多个话题,从代理拉取数据,消费已经 发布的消息

  • 消费者获取消息系统一般采用两种模型

    • Queuing:队列模型,一组消费者从一个服务器读取信息, 每个消息仅可被其中一个消费者消费

    • Publish Subscribe:发布订阅模型,消息被广播给所有 消费者

  • Kafka采用一种抽象方法:消费者组Consumer Group提供对上述 两种消息系统模型的支持 kafka_comsumer_group

    • 给每个消费者打上属于某个消费者组的标签(这里组只是 表示同组内消费者只能有一个消费信息)

    • 每个发布到话题的消息分发给消费者组的其中一个消费者

    • 一般情况下每个话题下有多个消费者组,每个组中有多个 消费者实例,以达到扩展处理能力、容错

    • 极端情况:如果所有消费者实例都隶属于同一个消费者组, Kafka工作模式类似于队列模型;所有消费者实例隶属于 不同的消费者组,Kafka工作模式类似于发布-订阅模型

消息分区、存储、分发

分区日志

每个分区是有序的不可更改可在末尾不断追加的 消息序列

分区优势

  • 允许Kafka处理超过一台服务器容量的日志规模

  • 分区作为并行处理基本单元,允许Kafka进行并行处理

  • 通过保证每个分区仅仅由一个消费者消费,可以保证同一 分区内消息消费的有序

    • 由于可以设置很多分区,仍然可以保证在不同消费者之间 实现负载均衡
    • 分区内外保证消息有序、数据分区处理对大部分实际应用 已经足够

分区管理

每个分区由单独的(一组)服务器处理,负责该分区数据管理、消息 请求,支持多个副本以支持容错

  • 每个分区中有一台服务器作为leader、若干服务器作为follower

  • 领导者负责分区读、写请求,跟随者以被动的方式领导者数据 进行复制

  • 领导者失败,则追随者之一在Zookeeper协调下成为新领导者

  • 为保证负载均衡,每个服务器担任部分分区领导者、其他分区 追随者

存储布局

Kafka存储布局非常简单

kafka_storage

分区存储

  • 话题每个分区对应一个逻辑日志

  • 每个日志为相同的大小的一组分段文件

  • 生产者发布的消息被代理追加到对应分区最后一个段文件中

  • 发布消息数量达到设定值、经过一段时间后,段文件真正写入 磁盘,然后公开给消费者

Offset

分区中每个消息的Sequential ID Number(Offset),唯一标识 分区中消息,并没有明确的消息ID

  • 偏移量是增量的但不连续,下个消息ID通过在其偏移量加上 消息长度得到

  • 偏移量标识每个消费者目前处理到某分区消息队列的位置, 对分区消息队列处理依赖于其(消息通过日志偏移量公开)

  • 偏移量由消费者控制,所以消费者可以以任何顺序消费消息

    • 可以回推偏移量重复消费消息
    • 设计消费者仅仅查看分区末尾若干消息,不改变消息, 其他消费者可以正常的消费

从消息分区机制、消费者基于偏移量消费机制,可以看出Kafka消息 消费机制不会对集群、其他消费者造成影响

适合场景

  • Messaging:消息传递,作为传递消息队列(ActiveMQ、 RabbitMQ等)替代品,提供高吞吐能力、高容错、低延迟

  • Website Activity Tracking:网站活动跟踪,要求系统必须 快速处理产生消息

  • Metric:度量,把分布式各个应用程序的运营数据集中,进行 汇总统计

  • Streaming Processing:流数据处理

  • Event Sourcing:事件溯源,把应用程序状态变化以时间顺序 存储,需要支持大量数据

  • Commit Log:日志提交,作为分布式系统提交日志的外部存储 服务

Storm

Storm是分布式、高容错的实时流数据处理的开源系统

  • Storm为流数据处理设计,具有很高的容错性
  • Storm保证每个消息只能得到一次完整处理,任务失败时会负责 从消息源重试消息,从而支持可靠的消息处理
  • 可以通过实现Storm通讯协议,提供其他语言支持

Storm架构

storm_structure

  • 主节点的运行Nimbus守护进程

    • 分配代码
    • 布置任务
    • 故障检测
  • 工作节点运行Supervisor守护进程

    • 监听、开始、终止工作进程
  • Nimbus、Supervisor都是无状态的(不负责维护客户端两次调用 之间状态维护)

    • 这使得两者十分健壮
    • 两者之间的协调由Zookeeper完成
  • Storm在ZeorMQ内部传递消息

Nimbus

Supervisor

Worker

Storm编程模型

Stream

数据流:没有边界的tuple序列

  • 这些tuple以分布式的方式,并行的创建、处理

Topology

计算拓扑:实时计算应用程序处理逻辑封装成的Topology对象

  • 相当于Mapreduce作业,但是MapReduce作业最终会结束、而 Topology会一直运行直到被杀死
  • Topology由Spout、Bolt组成

Spout

消息源:消息tuple生产者

  • 消息源可以是可靠的、不可靠的
  • 可靠的消息源可在tuple没有被storm成功处理时,可以重新发送
  • 不可靠的消息源则在发送tuple之后彻底丢弃

Bolt

消息处理者:封装所有的消息处理逻辑

  • Bolt可以做很多事情,包括过滤、聚集
  • Bolt一般数据处理流程
    • 处理一个输入tuple,发送0个、多个tuple
    • 调用ack接口,通知storm子集已经处理过了

Task、Executor

Topology每个Spout、Bolt转换为若干个任务在整个集群里执行

  • 默认情况下,每个Task对应一个线程Executor,线程用于执行 task
  • 同一个Spout/Bolt里的Task共享一个物理线程

Stream Grouping

数据分发策略:定义Spout、Bolt间Tasks的数据分发

  • Shuffle Grouping:洗牌式分组,上游Spout数据流tuples随机 分发到下游Bolt的Task

  • Fields Grouping:按指定字段进行分组

  • All Grouping:Spout数据tuple分发给所有下Bolt

  • Global Grouping:Spout数据tuple分发给最小id的task

  • Non-Grouping:类似shuffle Grouping,把具有Non-Grouping 设置Bolt推到其订阅的上游Spout、Bolt

  • Direct Grouping:tuple生产者决定接收tuple下游bolt中的task

  • Local or Shuffle Grouping:如果目标bolt中由一个或多个 task工作在同一进程中,tuple分配给这些task,否则同洗牌式 分组

  • Partial Key Grouping:类似Fields Grouping,但是在下游 Bolt中做负载均衡,提高资源利用率

消息处理保证

Storm追踪由每个SpoutTuple产生的Tuple树

  • 每个从Spout发出tuple,可能会生成成千上万个tuple

    • 根据血缘关系形成一棵tuple树
    • 当tuple树中所有节点都被成功处理了,才说明tuple被完全 处理
  • 每个Topology都有一个消息超时设置,如果Storm在时间内无法 检验tuple树是否完全执行,该tuple标记为执行失败,之后重发

重发

Vimscripts 编程

变量

普通变量

创建变量、变量赋值都需要用到let关键字

1
2
3
4
let foo = "bar"
echo foo
let foo = "foo"
echo foo

数字

  • Number32位带符号整形(整形之间除法同c)

    • :echo 0xef:16进制
    • :echo 017:8进制(鉴于以下,不建议使用)
    • :echo 019:10进制(9不可能出现,vim自动处理)
  • Float

    • :echo 5.1e-3:科学计数法)
    • :echo 5.0e3:科学计数法中一定要有小数点

类型转换:Number和Float运算时会强制转换为Float

字符串

类型转换
  • +if这些“运算”中,vim会强制转换变量类型

    • 数字开头的字符串会转为相应Number(即使符合Float也 会舍弃小数点后)
    • 而非数字开头 则转换为0
  • 连接.

    • .连接时vim可以自动将Number转换为字符串然后连接
    • 但是对于 Float,vim不能自动转换
  • 转义\

    • 注意echom "foo\nbar"类似的输出时,echom不会像 echo一样输出两行,而是将换行输出为vim默认 (即使设置了listchars)的“换行符”
  • 字符串字面量''

    • 所见即所得(py中r’’),注意连续两个单引号表示单引号
  • 内建字符串函数

    • strlen(str)(len(str)效果对字符串同)
    • split(str, token=" ")
    • join([str], token=" ")
    • tolower(str)
    • toupper(str)
  • 字符串比较====?==#

    • ==:对字符串比较是否大小写敏感取决于设置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      set noignorecase
      if "foo"=="Foo"
      echo "bar"(不会输出)
      endif

      set ignorecase
      if "foo"=="Foo"
      echo "bar"(会输出)
      endif
    • ==?:对字符串比较大小写永远不敏感

    • ==#:对字符串比较大小写永远敏感

  • <>:同上,也有3种

集合类型变量

列表

vim列表特点

  • 有序、异质
  • 索引从0开始,可以使用负数索引,使用下标得到对应元素
  • 支持切割
    • 是闭区间(这个和python不同)
    • 可以负数区间切割
    • 可以忽略起始/结尾索引表示从0开始/末尾截至
    • 切割区间越界是安全的

      字符串可以像列表一样切割、索引,但是不可以使用负数 索引,却可以使用负数切割

  • +用于连接两个列表

列表内建函数

  • add(list, item):添加新元素
  • len(list):列表长度
  • get(list, index, default_val):获取列表元素,越界则 返回default_val
  • index(list, item):返回元素索引,不存在返回-1
  • join(list, token):将列表中元素转换为字符串后,使用 toke连接,缺省为<space>
  • reverse(list):反转列表

字典

字典特性

  • 值是异质的,键可以不是字符串,但是会被强制转换为字符串, 因此,在查找值时也可以使用非字符串dict[100],同样会被 强制转换为字符串dict["100"]之后查找

  • 支持属性.查找,甚至可以后接Number

  • 添加新元素就和普通赋值一样:let dict.100 = 100

  • 移除字典中的元素

    • remove(dict, index)
    • unlet dict.index/unlet dict[index] 移除不存在的元素事报错

允许定义时多一个,

内建函数
  • get(dict, index, default_val):同列表

  • has_key(dict, index):检查字典中是否有给定键,返回 1(真)或0(假)

  • item(dict):返回字典键值对,和字典一样无序

  • keys(dict):返回字典所有键

  • values(dict):返回字典所有值

作为变量的选项

  • bool选项输出0、1

    1
    2
    :set wrap
    :set nowrap
  • 键值选项

    1
    2
    :set textwidth=80
    :echo &textwidth
  • 本地选项(l:作用域下)

    1
    let &l:number=1
  • 选项变量还可以参与运算

    1
    2
    let &textwidth=100
    let &textwidht = &textwidth + 10

作为变量的寄存器

1
2
3
4
let @a = "hello"
echo @a
echo @"
echo @/

变量作用域

<char>:开头表示作用域变量

  • 变量默认为全局变量
  • b::当前缓冲区作用域变量
  • g::全局变量

语句

条件语句

vim中没有not关键字,可以使用!表示否定

  • !:否
  • ||:或
  • &&:与
1
2
3
4
5
6
7
8
if "1one"
echo "one"(会输出)
endif
if ! "one"
echo "one"(会输出)
else
echo "two"
endif

finish关键字

finally时结束整个vimscripts的运行

循环语句

for语句

1
2
3
4
5
let c = 0
for i in [1,2,3]
let c+=i
endfor
echom c

while语句

1
2
3
4
5
6
7
let c = 1
let total = 0
while c<=4
let total+=c
let c+=1
endwhile
echom total

函数

没有作用域限制的vimscripts函数必须以大写字母开头 (有作用域限制最好也是大写字母开头)

1
2
3
4
5
6
7
8
func Func(arg1,...)
echo "Func"
echo a:arg1(arg1)
echo a:0(额外(可变)参数数量)
echo a:1(第一个额外参数)
echo a:000(所有额外参数的list
return "Func"
endfunction

function后没有紧跟!时,函数已经被定义,将会给出 错误,而function!时会直接将原函数替换,除非原函数正在 执行,此时仍然报错

  • 调用方式

    • :call Func():call直接调用(return值会被直接丢弃)
    • :echo Func():表达式中调用
  • 函数结束时没有return,隐式返回0

  • 函数参数:最多20个

    • 参数全部位于a:参数作用域下

      • a:arg1:一般参数
      • a:0:额外(可变)参数数量
      • a:n:第n个额外参数
      • a:000:包含额外参数list
    • 参数不能重新赋值

vim中函数可以赋值给变量,同样的此时变量需要大写字母开头, 当然可以作为集合变量的元素,甚至也可以作为参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function! Reversed(l)
let nl = deepcopy(a:l)
call reverse(nl)
return nl
endfunction

let Myfunc = function("Reversed")

function! Mapped(func, l)
let nl = deepcopy(a:l)
call map(nl, string(a:func) . '(v:val)')
return nl
endfunction

call Mapped(function("Reversed"), [[3,2], [1,2]])

let funcs = [function("Reversed"), function("Mapped")]

更多函数参见functions.vim(如果完成了)

Execute、Normal

  • :execute:把字符串当作vimscript命令执行(命令行输入) :execute "echom 'hello, world'"<cr>

    大多数语言中应该避免使用”eval”之类构造可执行字符串,但是 vimscripts代码大部分只接受用户输入,安全不是严重问题, 使用:execute命令能够极大程度上简化命令

    :execute命令用于配置文件时,不要忽略结尾的<cr>, 表示“执行命令”

  • :normal:接受一串键值,并当作是normal模式接受按键

    • :normal后的按键会执行映射,:normal!忽略所有映射

    • :normal无法识别“”这样的特殊字符序列 :normal /foo<cr> 这样的命令并不会搜索,因为“没有按回车”

  • :execute和:normal结合使用,让:normal接受按下 “无法打印”字符(<cr><esc>等),execute能够接受 按键

    1
    2
    :execute "normal! gg/foo\<cr>dd"
    :execute "normal! mqA;\<esc>`q"

正则表达式

vim有四种不同的解析正则表达式的“模式”

  • 默认模式下\+表示“一个或多个之前字符”的正常+意义, 其他的符号如{,},*也都需要添加转义斜杠表示正常意义, 否则表示字符字面意

    • :execute "normal! gg/for .\\+ in .\\+:\<cr>: execute接受字符串,将\\转义,然后正则表达式解析, 查找pythonfor语句

    • :execute "normal! gg".'/for .\+ in .\+:'."\<cr>": 使用字符串字面量避免\\,但是注意此时\<cr>也不会 被转义为按下回车\n才是换行符),所以需要分开 书写、连接

  • \v模式下,vim使用“very magic”正常正则解析模式

    • :execute "normal! gg".'/\vfor .+ in .+:'."\<cr>"

Cmd Markdown Reference


我们理解您需要更便捷更高效的工具记录思想,整理笔记、知识,并将其中承载的价值传播给他人,Cmd Markdown 是我们给出的答案 —— 我们为记录思想和分享知识提供更专业的工具。 您可以使用 Cmd Markdown:

  • 整理知识,学习笔记
  • 发布日记,杂文,所见所想
  • 撰写发布技术文稿(代码支持)
  • 撰写发布学术论文(LaTeX 公式支持)

cmd-markdown-logo

除了您现在看到的这个 Cmd Markdown 在线版本,您还可以前往以下网址下载:

Windows/Mac/Linux 全平台客户端

请保留此份 Cmd Markdown 的欢迎稿兼使用说明,如需撰写新稿件,点击顶部工具栏右侧的 新文稿 或者使用快捷键 Ctrl+Alt+N


什么是 Markdown

Markdown 是一种方便记忆、书写的纯文本标记语言,用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档:譬如您正在阅读的这份文档。它使用简单的符号标记不同的标题,分割不同的段落,粗体 或者 斜体 某些文字,更棒的是,它还可以

1. 制作一份待办事宜 Todo 列表

  • [ ] 支持以 PDF 格式导出文稿
  • [ ] 改进 Cmd 渲染算法,使用局部渲染技术提高渲染效率
  • [x] 新增 Todo 列表功能
  • [x] 修复 LaTex 公式渲染问题
  • [x] 新增 LaTex 公式编号功能

2. 书写一个质能守恒公式LaTeX

3. 高亮一段代码code

1
2
3
4
5
6
7
@requires_authorization
class SomeClass:
pass

if __name__ == '__main__':
# A comment
print 'hello world'

4. 高效绘制 流程图

1
2
3
4
5
6
7
8
st=>start: Start
op=>operation: Your Operation
cond=>condition: Yes or No?
e=>end

st->op->cond
cond(yes)->e
cond(no)->op

5. 高效绘制 序列图

1
2
3
Alice->Bob: Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!

6. 高效绘制 甘特图

1
2
3
4
5
6
7
8
9
10
11
12
13
title 项目开发流程
section 项目确定
需求分析 :a1, 2016-06-22, 3d
可行性报告 :after a1, 5d
概念验证 : 5d
section 项目实施
概要设计 :2016-07-05 , 5d
详细设计 :2016-07-08, 10d
编码 :2016-07-15, 10d
测试 :2016-07-22, 5d
section 发布验收
发布: 2d
验收: 3d

7. 绘制表格

项目 价格 数量
计算机 $1600 5
手机 $12 12
管线 $1 234

8. 更详细语法说明

想要查看更详细的语法说明,可以参考我们准备的 Cmd Markdown 简明语法手册,进阶用户可以参考 Cmd Markdown 高阶语法手册 了解更多高级功能。

总而言之,不同于其它 所见即所得 的编辑器:你只需使用键盘专注于书写文本内容,就可以生成印刷级的排版格式,省却在键盘和工具栏之间来回切换,调整内容和格式的麻烦。Markdown 在流畅的书写和印刷级的阅读体验之间找到了平衡。 目前它已经成为世界上最大的技术分享网站 GitHub 和 技术问答网站 StackOverFlow 的御用书写格式。


什么是 Cmd Markdown

您可以使用很多工具书写 Markdown,但是 Cmd Markdown 是这个星球上我们已知的、最好的 Markdown 工具——没有之一 :)因为深信文字的力量,所以我们和你一样,对流畅书写,分享思想和知识,以及阅读体验有极致的追求,我们把对于这些诉求的回应整合在 Cmd Markdown,并且一次,两次,三次,乃至无数次地提升这个工具的体验,最终将它演化成一个 编辑/发布/阅读 Markdown 的在线平台——您可以在任何地方,任何系统/设备上管理这里的文字。

1. 实时同步预览

我们将 Cmd Markdown 的主界面一分为二,左边为编辑区,右边为预览区,在编辑区的操作会实时地渲染到预览区方便查看最终的版面效果,并且如果你在其中一个区拖动滚动条,我们有一个巧妙的算法把另一个区的滚动条同步到等价的位置,超酷!

2. 编辑工具栏

也许您还是一个 Markdown 语法的新手,在您完全熟悉它之前,我们在 编辑区 的顶部放置了一个如下图所示的工具栏,您可以使用鼠标在工具栏上调整格式,不过我们仍旧鼓励你使用键盘标记格式,提高书写的流畅度。

tool-editor

3. 编辑模式

完全心无旁骛的方式编辑文字:点击 编辑工具栏 最右侧的拉伸按钮或者按下 Ctrl + M,将 Cmd Markdown 切换到独立的编辑模式,这是一个极度简洁的写作环境,所有可能会引起分心的元素都已经被挪除,超清爽!

4. 实时的云端文稿

为了保障数据安全,Cmd Markdown 会将您每一次击键的内容保存至云端,同时在 编辑工具栏 的最右侧提示 已保存 的字样。无需担心浏览器崩溃,机器掉电或者地震,海啸——在编辑的过程中随时关闭浏览器或者机器,下一次回到 Cmd Markdown 的时候继续写作。

5. 离线模式

在网络环境不稳定的情况下记录文字一样很安全!在您写作的时候,如果电脑突然失去网络连接,Cmd Markdown 会智能切换至离线模式,将您后续键入的文字保存在本地,直到网络恢复再将他们传送至云端,即使在网络恢复前关闭浏览器或者电脑,一样没有问题,等到下次开启 Cmd Markdown 的时候,她会提醒您将离线保存的文字传送至云端。简而言之,我们尽最大的努力保障您文字的安全。

6. 管理工具栏

为了便于管理您的文稿,在 预览区 的顶部放置了如下所示的 管理工具栏

tool-manager

通过管理工具栏可以:

</i> 发布:将当前的文稿生成固定链接,在网络上发布,分享 新建:开始撰写一篇新的文稿 </i> 删除:删除当前的文稿 导出:将当前的文稿转化为 Markdown 文本或者 Html 格式,并导出到本地 </i> 列表:所有新增和过往的文稿都可以在这里查看、操作 模式:切换 普通/Vim/Emacs 编辑模式

7. 阅读工具栏

tool-manager

通过 预览区 右上角的 阅读工具栏,可以查看当前文稿的目录并增强阅读体验。

工具栏上的五个图标依次为:

</i> 目录:快速导航当前文稿的目录结构以跳转到感兴趣的段落 视图:互换左边编辑区和右边预览区的位置 </i> 主题:内置了黑白两种模式的主题,试试 黑色主题,超炫! 阅读:心无旁骛的阅读模式提供超一流的阅读体验 全屏:简洁,简洁,再简洁,一个完全沉浸式的写作和阅读环境

8. 阅读模式

阅读工具栏 点击 或者按下 Ctrl+Alt+M 随即进入独立的阅读模式界面,我们在版面渲染上的每一个细节:字体,字号,行间距,前背景色都倾注了大量的时间,努力提升阅读的体验和品质。

9. 标签、分类和搜索

在编辑区任意行首位置输入以下格式的文字可以标签当前文档:

标签: 未分类

标签以后的文稿在【文件列表】(Ctrl+Alt+F)里会按照标签分类,用户可以同时使用键盘或者鼠标浏览查看,或者在【文件列表】的搜索文本框内搜索标题关键字过滤文稿,如下图所示:

file-list

10. 文稿发布和分享

在您使用 Cmd Markdown 记录,创作,整理,阅读文稿的同时,我们不仅希望它是一个有力的工具,更希望您的思想和知识通过这个平台,连同优质的阅读体验,将他们分享给有相同志趣的人,进而鼓励更多的人来到这里记录分享他们的思想和知识,尝试点击 (Ctrl+Alt+P) 发布这份文档给好友吧!


再一次感谢您花费时间阅读这份欢迎稿,点击 (Ctrl+Alt+N) 开始撰写新的文稿吧!祝您在这里记录、阅读、分享愉快!

作者 @ghosert
2016 年 07月 07日

LaTeX. 支持 LaTeX 编辑显示支持,例如:$\sum_{i=1}^n a_i=0$, 访问 MathJax 参考更多使用方法。
code. 代码高亮功能支持包括 Java, Python, JavaScript 在内的,四十一种主流编程语言。

MarkDown Basics


[TOC]

标题

  • Setext形式: 以底线的形式
    • = 表示最高阶标题
    • - 表示次阶标题
  • Atx形式: 在行首插入1-6各#表示1-6阶标题

  • [TOC]可以生成标题链接目录(兼容性不好,不是标准实现)

段落

  • 类似html格式, md忽略文件中的换行符
  • 如果需要新开段落, 手动使用空行(可包含不可见<space> <tab>字符)

区块引用

  • 使用email形式的>标识
  • 可嵌套, 根据层次添加不同数量的>
  • 其中可使用其他形式的语法, 如: 标题, 列表, 代码区块
  • 也可以只给整个”段落”第一行加上>

修辞和强调

  • *_包裹输出为html标签<em>
  • **__包裹则是输出为html标签<strong>
  • ~~包裹输出<del>(删除线)
  • 符号两边如果有空格则会当作普通符号
  • 可以\*, \_转义的方式输出普通符号

列表

  • 无序列表: -, +, *都可以输出无序列表
  • 有序列表: 数字接半角句点.
  • 列表项目标记一般放在最左边, 也可以缩进, 最多3个空格, 项目 标记后面一定要<space><tab>
  • MD其实不关心有序列表中数字的顺序正确性, 只是单纯的输出为 <li>, 如果需要避免将1.类似输出为有序列表, 需要转义 1\.
  • 在列表项目增加空行, 会把列表内容用<p>包裹, 列表 项目可以包含多个段落, 此时列表内段落间也需要空行分隔, 且 项目下所有段落都需要缩进<tab>或4个<space> (这里的段落是指MD意义上的一段, 不是源文件的一段, 即缩进 也只是需要在MD意义上一段的第一行缩进, 当然所有行都缩进 比较美观)
  • 在列表项目内添加引用, >需要缩进, 而代码块本身就需要 缩进, 所以需要缩进2个<tab>或8个<space>

链接

  • 行内形式: 直接在链接内容之后用括号添加链接
       [link_content](link "title(optional)")
    
  • 参考形式: 为链接定义一个名称, 然后可以在其他地方给出地址
       [link_content][id]
       [id]: link "title(optional)"
       [id]: link 'title'
       [id]: link (title)
    

参考形式中

  • 三种定义title的方式都可以
  • id可以包括空格, 不区分大小写
  • 隐式链接标记功能可以省略id, 此时链接标记(id)视为等同 于链接内容

自动链接

  • <link>: 自动转换为链接, 内容就是链接地址
  • <email>: 自动转换为邮箱链接

图片

  • 行内形式: ![alt text](link "title(optional)")
  • 参考形式: ![alt text][id] [id]: link "title(optional)"

代码

  • 段落内代码: 使用`包裹回输出html标签<code>
  • 代码区块
    • 每行缩进<tab>或者4个<space>
    • 3个`包裹, 后面还可以接代码类型, 可能会有代码高亮
1
2
def fn():
print('hello markdown')
def fn():
    print('hello markdown')

表格

  • |: 纵向边界
  • -: 表头和内容边界, 可用于设置对齐
    • :----: 左对齐
    • -----: 默认左对齐
    • :---:: 居中对齐
    • ----:: 右对齐

特殊字符

对于html中特殊字符<(起始标签)和&(标记html实体)会自动 “智能”转义. 如果使用的&字符是html实体字符的一部分, 那么会 保留原状, 否则会转换为&amp

如: 输入©会被保留, 直接显示为copyright字符

分隔线

一行中3个以上的-, *, _可以建立分隔线, 符号之间可以有 空格, 不能有其他东西

其他

  • 注脚: 在注脚文字后接[^footer1]footer1, 然后在其他 地方[^footer1]: 注脚内容

VSCode基础

Settings

  • VSCode配置文件分为两种(其中项目会重复)

    • User Settings:用户对所有项目的配置
    • Workspace Settings:针对当前项目的设置
  • 配置方式分为两种完全相同

    • UI:提示丰富
    • JSON:配置快捷,JSON字典格式

Command Palette

Python

  • 选择默认python环境

Terminal

  • 选择默认terminal,可能的terminal包括cmd、powershell、 WSL(若启用Linux子系统)

Original Setting

Terminal

1
2
3
4
5
6
7
8
{
"terminal.integrated.shell.windows""/path/to/shell",
// 默认shell
"terminal.integrated.shellArgs.windows": [ ],
"terminal.integrated.shellArgs.linux": [ ],
"terminal.integrated.shellArgs.osx": [ ],
// VSCode创建terminal启动参数(3系统分别配置)
}
  • VSCode terminal分为很integrated、external

  • VSCode应该是兼容所有terminal shell,如

    • C:/Windows/System32/cmd.exe
    • C:/Windows/System32/powershell.exe
    • C:/Windows/System32/wsl.exe:WSL启用
    • “/path/to/git/bin/bash.exe”:Git中bash等
  • VSCode terminal虽然兼容多种shell,但只能创建默认shell

    • 需要多种shell只能切换默认shell再创建
    • python shell等shell是特殊shell,无法默认创建,必须要 在命令面板中创建(虽然在普通shell中打开python环境, 但是VSCode不认可)

Python

1
2
3
4
5
6
7
8
9
10
11
{
"python.condaPath": "/path/to/conda/Scripts",
// conda安装目录Scripts文件夹
"python.venvPath": "/path/to/conda/envs",
// 虚拟环境目录,VSCode会在其中查找虚拟环境,作为
// Command Palette中的备选项
"python.pythonPath": "/path/to/python.exe",
// 默认python解释器路径
"python.terminal.activateEnvironment": true,
// 创建python shell时,尝试中激活虚拟环境
}
  • python.terminal.activateEnviroment激活的虚拟环境由 python.pythonPath决定

    • VSCode会尝试执行python.pythonPath同级中 Scripts/activate.bat激活虚拟环境

    • 因此虚拟环境需要安装conda,否则没有 Scripts/ativate.bat无法正常激活默认虚拟环境

CPPC

配置文件

  • .vscode/c_cpp_properties.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "configurations": [
    {
    "name": "WSL",
    "includePath": [
    "${workspaceFolder}/**"
    ],
    "defines": [
    "LOCAL",
    "_DEBUG",
    "UNICODE",
    "_UNICODE"
    ],
    "compilerPath": "/usr/bin/gcc",
    "cStandard": "c11",
    "cppStandard": "c++14",
    "intelliSenseMode": "gcc-x64"
    }
    ],
    "version": 4
    }
    • C/C++项目基本配置
  • .vscode/tasks.json:利用VSCode的Tasks功能调用WSL的 GCC/G++编译器

    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    {
    // tasks.json
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format

    "version": "2.0.0",
    "tasks": [
    {
    "label": "Build",
    "command": "g++",
    "args": [
    "-g",
    "-Wall",
    "-std=c++14",
    "/mnt/c/Users/xyy15926/Code/cppc/${fileBasename}",
    "-o",
    "/mnt/c/Users/xyy15926/Code/cppc/a.out",
    "-D",
    "LOCAL"
    ],
    "problemMatcher": {
    "owner": "cpp",
    "fileLocation": [
    "relative",
    "${workspaceRoot}"
    ],
    "pattern": {
    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warining|error):\\s+(.*)$",
    "file": 1,
    "line": 2,
    "column": 3,
    "severity": 4,
    "message": 5
    }
    },
    "type": "shell",
    "group": {
    "kind": "build",
    "isDefault": true
    },
    "presentation": {
    "echo": true,
    "reveal": "silent",
    "focus": true,
    "panel": "shared"
    }
    },
    {
    "label": "Run",
    "command": "/mnt/c/Users/xyy15926/Code/cppc/a.out",
    "type": "shell",
    "dependsOn": "Build",
    "group": {
    "kind": "test",
    "isDefault": true
    },
    "presentation":{
    "echo": true,
    "reveal": "always",
    "focus": true,
    "panel": "shared",
    "showReuseMessage": true
    }
    }
    ]
    }
    • 这里为方便将运行程序任务同> Task: Run Test Task 任务关联,可以在命令面板执行此指令
  • .vscode/launch.json:gdb调试配置

    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
    35
    36
    37
    38
    39
    {
    // launch.json
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
    {
    "name": "(gdb) Bash on Windows Launch",
    "type": "cppdbg",
    "request": "launch",
    "program": "/mnt/c/Users/xyy15926/Code/cppc/a.out",
    "args": ["-f", "Threading"],
    "stopAtEntry": false,
    "cwd": "/mnt/c/Users/xyy15926/Code/cppc/",
    "environment": [],
    "externalConsole": true,
    "MIMode": "gdb",
    "pipeTransport": {
    "debuggerPath": "/usr/bin/gdb",
    "pipeProgram": "C:\\windows\\system32\\bash.exe",
    "pipeArgs": ["-c"],
    "pipeCwd": ""
    },
    "setupCommands": [
    {
    "description": "Enable pretty-printing for gdb",
    "text": "-enable-pretty-printing",
    "ignoreFailures": false
    }
    ],
    "sourceFileMap": {
    "/mnt/c": "c:\\",
    "/mnt/d": "d:\\"
    },
    "preLaunchTask": "Build"
    },
    ]
    }

Git

1
2
3
4
{
"git.ignore.MissingGitWarning": true,
"git.path": "/path/to/xxxgit.exe"
}
  • “git.path”既可以是windows下Git,也可以是“伪装”Git,使用 工具wslgit,让VSCode 直接使用WSL内的Git

KeyMapper

  • <c-s-\>`:新建默认terminal绘画
  • <c-s-p>:command palette

网络设置

VMware网络模式

虚拟网络组件

虚拟交换机

Virtual Network Editor中默认有三个虚拟交换机

  • VMnet0(Bridged):桥接模式下的虚拟交换机
  • VMnet1(Host-only);仅主机模式下的虚拟交换机
  • VMnet8(NAT):NAT模式下虚拟交换机

虚拟网卡

在宿主机上安装完成VMware后,在网络适配器中会安装有两块 虚拟网卡

  • VMware Network Adapter VMnet1:主机模式下虚拟网卡
  • VMware Network Adapter VMnet8:NAT模式下虚拟网卡

卸载虚拟网卡后,可以Virtual Network Editor->还原默认设置, 还原虚拟网卡

Bridged

桥接模式:将主机网卡同虚拟机网卡利用虚拟网桥进行通信

  • 网络结构

    • 在桥接作用下,类似从物理主机虚拟出一个交换机
    • 所有桥接设置的虚拟机都连接到这个交换机的接口上
    • 物理主机也同样连接在此交换机上
    • 虚拟机就像是外部局域网中的独立主机,同宿主机逻辑上 同等地位
  • 网络连通性

    • 所以桥接下的网卡与网卡的都是交换模式的, 可以相互访问,不受干扰
    • 虚拟机可以在外部网络上可见
  • 机器网络配置

    • 虚拟机IP地址需要和宿主机在同一网段(外网网段)
    • 需要手动配置虚拟机配置IP地址、子网掩码
    • 若需要联网,虚拟机Gateway、DNS需要和主机一致

vmware_bridged

适用场景

  • 利用VMware在局域网中新建虚拟服务器,位局域网提供网络服务
  • 网络环境ip资源充裕

网络设置

  • 宿主机网络连接

    • 网络连接属性中VMware Bridge Protocal是否勾选 (安装VMware后一般会默认勾选)
    • 确认宿主机:IP、Gateway、DNS
  • 虚拟机网络设置

    • 编辑虚拟机设置->网络适配器->Bridged/桥接模式
  • 虚拟机网卡配置:网卡配置文件方式参见config_file

    • IP地址要和宿主机IP在同一网段
    • Gateway默认网关要和宿主机一致

NAT

Network Address Translation:网络地址转换,借助 虚拟NAT设备虚拟DHCP服务器,使得虚拟机能够联网

  • 网络结构

    • 主机网卡与虚拟NAT设备相连
    • 虚拟NAT设备、虚拟DHCP服务器、虚拟机共同连接在虚拟 交换机VMnet8上
    • VMnet8通过虚拟网卡VMware Network Adapter VMnet8和 宿主机相连,宿主机成为双网卡主机,同时参与宿主网络 和虚拟网络
  • 网络连通性

    • 借助虚拟NAT设备连通宿主机网卡、虚拟机,实现了 虚拟机通过宿主机网卡联网,所以虚拟机在外部网络 不可见
    • VNAV8连通宿主机、虚拟机,使得宿主机可以和虚拟机连通 ,要注意此时宿主机双网卡,连通虚拟局域网的网卡是虚拟 网卡
    • 虚拟网卡只是起连通虚拟机、宿主机的作用,卸载不影响 虚拟机连通外网
  • 机器网络配置

    • 由于虚拟DHCP服务器的存在,虚拟机无需特殊配置,只需要 启用虚拟机网卡DHCP服务即可

vmware_nat

适用场景

  • 虚拟系统介入互联网简单,不需要其他配置,只需要宿主机能 访问网络即可
  • 网络ip资源紧缺,且需要虚拟机能够联网

网络配置

  • 宿主机服务设置

    • 检查宿主机VMware DHCP ServiceVMware NAT Service 服务是否启动(VMware安装一般默认启动此服务)
  • Virtual Network Eidtor配置

    • VMnet8虚拟交换机默认为NAT模式
    • 配置VMnet8->子网IPVMnet8->子网掩码
    • 配置VMnet8->NAT设置VMnet8->DHCP参数
  • 虚拟机网络配置

    • 编辑虚拟机设置->网络适配器->NAT模式
  • 虚拟机网卡配置

    • NAT虚拟网络有DHCP服务,只需要修改BOOTPRPOTO=dhcp, 使用DHCP协议即可
    • 也可以设置静态ip,但是需要注意要在DHCP范围内

Host-Only

仅主机模式:将虚拟机与外网隔开,使得虚拟机成为独立系统,只和 主机相互通讯

  • 网络结构:类似于NAT模式

    • NAT模式去除了虚拟NAT设备
    • 使用VMware Network Adapter VMnet1虚拟网卡连接宿主机 、VMnet1交换机
    • 虚拟局域网可以认为是单独从属当前宿主机的私有网络
  • 连通性

    • 虚拟机网络、真实网络相互隔离,两者不相互连通
    • 宿主机、虚拟机相互通过虚拟网卡VNAV1相互连接
    • 事实上,可以在宿主机中设置,通过将主机网卡共享给 虚拟网卡VNAV1实现虚拟机联网

vmware_host_only

适用场景

  • 特殊的网络调试环境下,要求真实环境、虚拟环境隔离开

网络设置

  • 虚拟机网络设置

    • 编辑虚拟机设置->网络适配器->Host-only/仅主机模式
  • Virtual Network Eidtor配置:类似于NAT模式,只是VMnet1 虚拟交换机才默认为Host-only模式

  • 虚拟机网卡配置:类似NAT模式

共享宿主机网卡

Host-only模式下下,虚拟局域网默认无法联网,需要将宿主机网卡 共享给给虚拟网卡VNAV8才能实现联网

  • 网络连接->属性->共享->选择VMAV1

  • 共享宿主机网卡可能会要强制修改虚拟网卡VMAV1的ip地址, 需要根据提示在虚拟网络编辑其中修改虚拟网络的DHCP参数 host_only_network_sharing

  • 然后修改虚拟网卡Gateway、DNS为VMAV1的ip地址

Windows Linux Subsystem

说明

WSL是一个兼容层,类似反过来的Wine,但更底层

  • Linux、Windows程序不兼容,是因为二者内核提供的接口不同

    • ls/dir命令
      • 在Linux下调用getdents内核调用
      • 在Windows下调用NtQueryDirectoryFile内核调用
  • WSL提供Linux内核接口,并转换为NT内核接口

    • 在WSL中执行ls仍然调用getdents
    • WSL收到请求,将系统调用转换为NT内核接口 NTQueryDirectoryFile
    • NT内核收到WSL请求,返回执行结果
    • WSL将结果包装后返回
  • 毕相较于真正Linux系统仍然有不足

    • Docker等涉及未实现的内核特性软件如法使用
    • Raw socket相关相关操作容易出错
    • I/O性能相对孱弱

Cygwin对比

wsl_architecture

  • Cygwin提供了完整的POSIX系统调用API(以运行库 Cygwin*.dll形式提供,但是仍然工作在User Mode

    • Cygwin将POSIX系统调用转换为Win32 API(因为其架设在 Win32子系统上),很多内核操作(如:fork)受限于 Win32实现

    • Linux应用程序必须链接到Cynwin*.dll,需要修改源码 重新编译后才能执行,这样应用程序才不会直接请求内核, 而是调用Cygwin运行库,且编译出的可执行文件为 Win32 PE格式封装,只能在Windows下执行

  • WSL中Linux应用程序进程被包裹在Pico Process中,其发出的 所有系统调用会被直接送往Kernel Mode中的 lxcore.syslxss.sys

    • WSL将POSIX系统调用转换为更底层的NP API调用(WSL和 Win32平行,直接架设在NT内核上)

    • 可以直接执行ELF格式封装的Linux可执行程序

启用

  • 控制面板 ->
  • 程序和功能 ->
  • 启用或关闭windows功能 ->
  • 适用于Linux的Windows子系统

其他

使用

进入WSL

除在图形界面中点击图标,以默认参数启动,还可以在terminal (cmd/powershell等)自定义进入WSL参数

  • wsl.exe:打开默认发行版中默认shell
  • distroname.exe:打开指定发行版中默认shell
  • bash.exe:打开默认发行版中bash shell
  • 这些应用程序默认在path中,可以直接执行

版本管理

  • wslconfig.exe可以用于管理多个子系统的发行版

WSL、Windows互操作

文件

  • Windows所有盘符挂载在WSL中/mnt目录下

  • WSL中所有数据存放在%HOME%/AppData/Local/Packages/{linux发行包名}/LocalState/rootfs

    • 不要在Windows下直接修改,造成权限错误

命令

  • 在cmd中直接调用WSL命令
    1
    2
    PS> wsl [-e] ls -al
    // wsl带参数执行
  • 在WSL中调用Windows命令行程序(在$PATH中)

    1
    2
    $ which ipconfig.exe
    $ ipconfig.exe
  • 在WSL中启动Windows应用(在$PATH中)

    1
    $ notepad.exe
  • 通过pipes通信

    1
    2
    $ cat foo.txt | clip.exe
    PS> ipconfig | wsl grep IPv4

端口、环境变量

  • WSL与Windows共享端口(NT内核?)
  • WSL继承Windows的部分环境变量,如:PATH

Terminal推荐

  • wsl-terminal: 专为WSL开发的终端模拟器,基于minttywslbridge,稳定 易用

  • ConEmu:老牌终端模拟器, 功能强大

  • Hyper:基于Electron的跨平台 终端模拟器

WSL-Terminal

  • WSL-Terminal中包含一些快捷工具

    • tools目录中包含一些脚本,可以通过wscripts.exe 执行修改注册列表,添加一些功能
      • 添加WSL中vim、emacs等打开到右键菜单
      • 添加在WSL中打开文件夹到右键菜单
    • run-wsl-file.exe可以用于在快捷执行wsl脚本,只需要 将其选择为文件打开方式
    • vim.exe可以用WSL中vim打开任何文件,当然一般是配合 tools/中脚本在右键注册后使用
  • 配置

    • 配置文件etc/wsl-terminal.conf
    • 主题文件etc/themes/
    • mintty配置文件etc/mintty

其他问题

文件权限问题

  • 在WSL中,windows实现了两种文件系统用于支持不同使用场景

VolFs

VolFs:着力于在windows文件系统上提供完整的linux文件系统特性 ,通过各种手段实现了对InodesDirectory EntriesFile ObjectsFile DescriptorsSpecial File Types 的支持

  • 为支持Inodes,VolFS会把文件权限等信息保存在 NTFS Extended Attributes

    • 在Windows中新建的文件缺少此扩展参数,有些编辑器也会 在保存文件是去掉这些附加参数
    • 所以不要在Windows中修改WSL文件,否则VolFs无法正确 获得文件metadata
  • WSL中/就是VolFs文件系统

DrvFs

DrvFs:着力提供于Windows系统的互操作性,从Windows的文件权限 (即文件->属性->安全选项卡中的权限)推断出文件对应Linux权限

  • 所有windows盘符挂在在WSL中/mnt是都使用DrvFs文件系统

  • 由于DrvFs文件权限继承机制很微妙,结果就是所有文件权限 都是0777

    • 所以ls结果都是绿色的
    • 早期DrvFs不支持metadata,在Build 17063之后支持 文件写入metadata,但是需要重新挂载磁盘
  • 可以通过设置DrvFs metadata设置默认文件权限

    1
    2
    3
    4
    5
    $ sudo umount /mnt/e
    $ sudo mount -t drvfs E: /mnt/e -o metadata
    // 此时虽然支持文件权限修改,但默认权限仍然是*0777*
    $ sudo mount -t drvfs E: /mnt/e -o metadata,uid=1000,gid=1000,umask=22,fmask=111
    // 此时磁盘中默认文件权限为*0644*
  • 更合适的方式是通过/etc/wsl.conf配置DrvFs自动挂载属性

AutoMatically Configuring WSL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 # `/etc/wsl.conf`
[automount]
# 是否自动挂载
enabled = true
# 是否处理`/etc/fstab`文件
mountFsTab = true
# 挂载路径
root = /mnt/
# DrvFs挂载选项,若需要针对不同drive配置,建议使用`/etc/fstab`
options = "metadata,umask=023,dmask=022,fmask=001"
[network]
generateHosts = true
generateResolvConf = true
[interop]
# 是否允许WSL载入windows进程
enabled = true
appendWindowsPath = true
  • 如果需要给不同盘符设置不同挂载参数,需要修改 /etc/fstab

    1
    E: /mnt/e drvfs rw,relatime,uid=1000,gid=1000,metadata,umask=22,fmask=111 0 0

GDB

概述

GDB是GNU发布的UNIX程序调试工具,可以帮助完成

  • 自定义运行程序
  • 让程序在指定断点处停止
  • 检查停止程序的内部状态
  • 动态改变程序执行环境

调试准备

  • gdb会根据文件名后缀确认调试程序的语言,设置gdb自身语言 环境,并随之改变语言环境

    • 如果程序由多种语言编译而成,gdb能根据不同语言自动 切换语言环境
  • 可以使用infoshowset命令查看设置当前语言环境

  • 可能会缺少glibc-debuginfo,无法显示全部调试信息

    • OpenSuse安装可能需要添加源

编译选项

  • 调试C++/C程序时,要求在编译时就把调试信息添加到可执行 文件中

    • 使用gcc/g++的-g参数添加源码信息
    • 否则调试时将看不见函数名、变量名,而是全部以运行时 内存地址代替
  • 关闭优化选项

    • 否则优化器会删改程序,使得某些变量不能访问、取值错误

启动GDB调试

  • gdb <exe>:直接gdb启动可执行文件<exe>
  • gdb <exe> core:用gdb同时调试可执行文件、core文件
  • gdb <exe> <PID>:给定进程PID,gdb自动attach该进程, 调试已经运行的程序
    • 也可不指定PID,直接关联源码,在gdb中使用attach命令 手动挂接,调试已运行程序

配置调试环境

源文件

  • gdb启动程序后,gdb会在PATH、当前目录中搜索程序的源文件 -directory/-d添加源文件搜索路径
  • 在gdb交互中使用dir[rectory] <dirname>指定源文件搜索 路径
  • gdb中使用list/l检查gdb是否能列出源码

程序运行环境

在gdb中run程序之前,可能需要设置、查看程序运行环境

  • 程序运行参数

    • set <args>:设置程序运行时参数
    • show <args>:查看已设置参数
  • 环境变量

    • path [<dir>]:查看、添加<dir>PATH
    • show paths:查看PATH
    • set environment var [=value]:设置环境变量
    • show environment var:查看环境变量
  • 工作目录

    • cd <dir>:等价于cd
    • pwd:等价于pwd
  • 输入输出

    • info terminal:显示程序所有终端模式
    • run > outfile:重定向程序输出
    • tty /dev/:指定输入、输出设备

参数

  • -s <file>/-symbols <file>:从指定文件读取符号表
  • -se <file>:从指定文件读取符号表信息,并用于可执行文件
  • -c <file>/-core <file>:调试core dump产生的core文件
  • -d <dir>/-directory <dir>:添加源文件搜索路径
    • 默认搜索路径是PATH

表达式

  • 表达式语法应该是当前所调试语言的语法

  • gdb另外还支持一些通用操作符

    • @:指定存储在堆上、动态分配内存大小的长度

      1
      2
      3
      4
      int *array = (int*)malloc(len * sizeof(int));
      # 源码
      > p *array@len
      # 打印数组`*array`
    • :::指定某个具体文件、函数中的变量

      • 常用于取出被隐藏的全局变量
    • [(type)]<addr>:内存地址addr处为一个type类型 变量

停止点

设置Breakpoint

break

break:在某位置设置断点

1
2
b[reak] [<probe_modifier>] [<location>] [thread <threadno>]
[if <condition>]
  • probo_modifier:命令处于probe point时需要
    • -probe:通用、自动推测探测类型
    • -probe-stapSystemTap探测
    • -probe-dtraceDTrace探测
  • location:设置断点位置
    • 缺省:当前栈帧中的执行位置
    • linespecs冒号分隔绝对位置参数(逐步精确)
      • 其中可以包括:文件名、函数名、标签名、行号
    • +-[n]标识相对当前行位置
    • *addr:在程序运行的内存地址addr
    • explicit:类似于linespecs,通过多个参数给出
  • threadno:设置断点的线程号
    • 缺省断点设置在所有线程上
    • gdb分配的线程编号,通过info threads查询
    • 多线程被gdb停止时,所有运行线程都被停止,方便 查看运行程序总体情况
  • if <condition>:满足条件condition时在断点处停止, 缺省立即停止程序
1
2
3
4
5
6
7
8
9
> break factorial.c:fact:the_top
# factorial.c文件、fact函数、the_top标签
> break +2
# 当前行之后两行
> break *main + 4
# `main`函数4B之后
> break -source factorial.c -function fact
-label the_top
# explicit方式,同linespecs
  • 标签:C++/C中配合goto使用(label_1:
  • <tab>:补全函数名称、函数原型(重载函数)
  • 若指定函数名称不唯一,gdb会列出函数原型供选择

tbreak

tbreak:设置一次性断点,生效后立刻被删除

rbreak

rbreak:对匹配正则表达式的行均设置断点

condition

condition:修改、设置断点n生效条件

1
2
> condition <bpnum> <condition>
# 设置断点`bpnum`生效条件为`condition`

ignore

ignore:设置忽略断点n次数

1
2
> ignore <bpnum> <count>`
# 忽略断点`bpnum` `count`次

设置Watchpoint

watch

watch:为某个表达式设置观察点

  • 观察某个表达式,其值发生变化时停止程序
1
2
> watch [-l|-location] <expr> [thread <threadno>]
[mask <maskvalue>]
  • -l:表示将expression视为地址,观察表达式指向的地址 中的值
  • expr:表达式、*开头表示的内地地址
  • threadno:同break
  • maskvalue:观察值mask,只观察部分bit值
1
2
3
4
> watch foo mask 0xffff00ff
# 观察`foo`
> watch *0xdeadbeef 0xffffff00
# 观察内存地址`0xdeadbeef`处的值
  • 取决于系统,观察点有可能以硬件、软件方式实现,大部分 PowerPC、X86支持硬件观察点
    • 软件观察点通过逐步测试变量实现,程序执行速度慢得多
    • 硬件观察点不会降低程序执行速度
  • mask参数需要架构支持

rwatch

rwatch:为某表达式设置读观察点

  • 当表达式值被读取时停止程序
1
2
> rwatch [-l|location] <expression> [thread <threadno>]
[mask <maskvalue>]

awatch

awatch:为某表达式设置写观察点

  • 当表达式值被修改时停止程序
1
2
> awatch [-l|-location] <expression> [thread <threadno>]
[mask <maskvalue>]

设置Catchpoint

catch

catch:设置捕捉点捕捉事件

1
> cat[ch] <event>
  • 通用事件
    • catch:捕获异常
    • exec:调用系统调用exec(仅HP-UX下有用)
    • fork:调用系统调用fork(仅HP-UX下有用)
    • load [<libname>]:载入共享库(仅HP-UX下有用)
    • unload [<libname>]:卸载共享库(仅HP-UX下有用)
    • throw:抛出异常
    • rethrow:再次抛出异常
    • vfork:调用系统调用vfork时(仅HP-UX下有用)
    • syscall:被系统调用
  • Ada
    • assert:捕获Ada断言失败
    • exception [arg]:捕获Ada和参数匹配的异常
    • handlers:捕获Ada异常
  • 捕捉点:捕捉程序运行时的一些事件,如:载入动态链接库、 异常事件<event>发生时停止程序

tcatch

tcatch:设置一次性捕捉点,程序停止后自动删除

信号处理

gdb可以在调试程序时处理任何信号,可以要求gdb在收到指定信号后 停止正在执行的程序以供调试

1
> handle <signal> <keywords>
  • signal:需要处理的信号
    • 信号可选使用SIG开头
    • 可以定义需要处理的信号范围SIGIO-SIGKILL
    • 可以使用all表示处理所有信号
  • keywords:被调试程序接收到信号后gdb的动作
    • nostop:不停止程序运行,打印消息告知收到信号
    • stop:停止程序运行
    • print:显示信息

      todo

    • noprint:不显示信息
    • noigore:gdb处理信号,交给被调试程序处理
    • nopass/ignore:gdb截留信号,不交给被调试程序处理

维护停止点

clear

clear:清除指定位置断点

1
> clear [<location>]
  • location:指定清除断点位置
    • 缺省:清除当前栈帧中正在执行行断点
    • 其余设置同break

commands

commands:设置断点生效时自动执行命令

  • 利于自动化调试
1
2
3
> commands [bpnum]
> ...command-list...
> end
  • bpnum:断点序号
    • 缺省:最近设置的断点
    • 5-7:指定断点区间

执行

文件

list

list:打印源代码

1
> l[ist] [<location>]
  • location:输入的源代码
    • 缺省/+:显示当前行后源代码
    • -:显示当前行前源代码
    • [[start], [end]]:显示范围内源代码(缺省表当前行)
    • 指定单行类似breaklocation参数
  • 一般默认显示10行,可以通过set listsize <count>设置

search/forward-search:从打印出最后行开始正则搜索源码

1
> search/forward-search <regexp>

reverse-search:从打印出的最后行反向正则搜索源码

1
> reverse-search <regexp>

directory

directory:指定源文件搜索路径

1
> dir[rectory] <dir>
  • dir:源文件路径
    • 可以指定多个搜索路径,linux下:分隔,windows下;
    • 缺省:清除自定义源文件搜索路径信息
  • show查看当前

继续执行

run

run:启动程序开始调试

1
> r[un] [<args>]
  • args
    • 缺省:上次runset args指定的参数
    • 参数中包含的*...将被用于执行的shell扩展 (需清除参数,使用set args置空)
  • 允许输入、输出重定向

continue

continue:继续执行直到之后断点、程序结束

1
> c[ontinue]/fg [<ignore-count>]
  • ignore-count:执行直到之后第ingore-count个断点 (忽略ignore-count-1个断点)
    • 缺省:1

step/next

step/next:单步/单行跟踪

1
> s[tep]/[n]ext [<count>]
  • count:执行代码行数
    • 缺省:1
  • 有函数调用,step进入函数(需要函数被编译有debug信息)

    next不进入函数调用,视为一代代码

stepi/nexti

stepi/nexti:单条intruction(机器指令、汇编语句)跟踪

1
> s[tep]i/n[ext]i [<count>]
  • count:执行指令条数
    • 缺省:1

finish

finish:运行直到当前栈帧/函数返回,并打印函数返回值、存入 值历史

return

return:强制函数忽未执行语句,并返回

1
> return [expr]
  • expr:返回的表达式值
    • 缺省:不返回值

util

until/u:运行程序直到退出循环体

jump

jump:修改程序执行顺序,跳转至程序其他执行处

1
> jump [<location>]
  • location:同break
  • jump不改变当前程序栈中内容,所以在函数间跳转时,函数 执行完毕返回时进行弹栈操作式必然发生错误、结果错误、 core dump,所以最好在同一个函数中跳转
  • 事实上,jump就是改变了寄存器中保存当前代码所在的内存 地址,所以可以通过set $pc更改跳转执行地址

call

call:强制调用函数,并打印函数返回值(void不显示)

1
> call <expr>
  • print也可以调用函数,但是如果函数返回voidprint 显示并存储如历史数据中

查看信息

1
> p[rint] [/<f>]<expr>[=value]
  • f:输出格式
    • x:16进制格式
    • d:10进制格式
    • u:16进制格式显示无符号整形
    • o:8进制格式
    • t:2进制
    • a:16进制
    • c:字符格式
    • f:浮点数格式
    • i:机制指令码
    • s
  • expr:输出表达式、gdb环境变量、寄存器值、函数
    • 输出表达式:gdb中可以随时查看以下3种变量值
      • 全局变量:所有文件可见
      • 静态全局变量:当前文件可见
      • 局部变量:当前scope可见
      • 局部变量会隐藏全局变量,查找被隐藏变量可以使用 ::指定
    • 编译程序时若开启优化选项,会删改程序,使得某些变量 不能访问
    • 输出环境变量、寄存器变量时,需要使用$前缀
    • 函数名称:强制调用函数,类似call
  • value:修改被调试程序运行时变量值
    • 缺省:打印变量值
    • =是C++/C语法,可以根据被调试程序改为相应程序赋值 语法
    • 可以通过set var实现(当变量名为gdb参数时,必须 使用set var
  • 每个print输出的表达式都会被gdb记录,gdb会以$1$2 等方式记录下来,可以使用此编号访问以前的表达式

examine

examine/x:查看内存地址中的值

1
> examine/x /[<n/f/u>] <addr>
  • 输出参数:可以三者同时使用
    • n:查看内存的长度(单元数目
    • f:展示格式,同print
      • u:内存单元长度
      • b:单字节
      • h:双字节
      • w:四字节,默认
      • g:八字节
  • addr:内存地址
1
2
> x/3uh 0x54320
# 从内存地址`0x54320`开始,16进制展示3个双字节单位

display

display:设置自动显示变量,程序停止时变量会自动显示

1
> display/[<fmt>] [<expr>] [<addr>]
  • fmt:显示格式
    • print
  • exprt:表达式
  • addr:内存地址
1
2
3
> display/i $pc
# `$pc`:gdb环境变量,表示指令地址
# 单步跟踪会在打印程序代码时,同时打印出机器指令

undisplay

undisplay:删除自动显示

1
> undisplay [<num>]
  • num:自动显示编号
    • info查看
    • 可以使用a-b表示范围

查看、设置GDB环境

info

停止点

  • locals:打印当前函数中所有局部变量名、值
  • args:打印当前函数参数名、值
  • b[reak][points] [n]:查看断点
  • watchpoints [n]:列出所有观察点
  • catch:打印当前函数中异常处理信息
  • line [<location>]:查看源代码在内存中地址
  • f[rame]:可以打印更详细当前栈帧信息
    • 大部分为运行时内存地址
  • display:查看display设置的自动显示信息

线程

  • threads查看在正在运行程序中的线程信息

信号

  • info signals/handle:查看被gdb检测的信号
  • frame:查看当前函数语言
  • source:查看当前文件程序语言

其他

  • terminal:显示程序所有终端模式
  • registers [reg]:查看寄存器情况
    • 缺省:除浮点寄存器外所有寄存器
    • 还可以通过print实现
  • all-registers:查看所有寄存器情况(包括浮点寄存器)

set

停止点

  • step-mode [on] [off]:开启/关闭step-mode模式
    • 程序不会因为没有debug信息而不停止,方便查看机器码

环境

  • language [lang]:设置当前语言环境
  • args [<args>]:设置被调试程序启动参数
  • environment var [=value]:设置环境变量
  • listsize <count>:设置最大打印源码行数
  • var <var=value>:修改被调试程序运行时变量值
    • 还可以通过print变量修改

print

  • address [on/off]:打开地址输出

    • 即程序显示函数信息时,显示函数地址
    • 默认打开
  • array [on/off]:打开数组显示

    • 打开数组显示后,每个函数占一行,否则以逗号分隔
    • 默认关闭
  • elements <num-of-elements>:设置数组显示最大长度

    • 0:不限制数组显示
  • null-stop [on/off]:打开选项后,显示字符串时遇到结束符 则停止显示

    • 默认关闭
  • pretty [on/off]:打开选项后,美化结构体输出

    • 打开选项后,结构体成员单行显示,否则逗号分隔
  • sevenbit-strings [on/off]:字符是否按照/nnn格式显示

    • 打开后字符串/字符按照/nnn显示

      todo

  • union [on/off]:显示结构体时是否显示其内联合体数据

    • 打开时联合体显示结构体各种值,否则显示...
  • object [on/off]:打开选项时,若指针对象指向其派生类, gdb自动按照虚方法调用的规则显示输出,否则gdb忽略虚函数表

    • 默认关闭
  • static-members [on/off]:是否对象中静态数据成员

    • 默认打开
  • vtbl [on/off]:选项打开,gdb将用比较规则的格式输出 虚函数表

    • 默认关闭

show

执行

  • args:查看被调试程序启动参数
  • paths:查看gdb中PATH
  • environtment [var]:查看环境变量
  • directories:显示源文件搜索路径
  • convenience:查看当前设置的所有环境变量
  • address:查看是否打开地址输出
  • array:查看是否打开数组显示
  • element:查看再打数组显示最大长度
  • pretty:查看是否美化结构体输出
  • sevenbit-strings [on/off]:查看字符显示是否打开
  • union:查看联合体数据输出方式
  • object:查看对象选项设置
  • static-members:查看静态数据成员选项设置
  • vtbl:查看虚函数显示格式选项设置

shell

shell:执行shell命令

1
> shell <shell-cmd>
  • cd:等同> shell cd
  • pwd
  • make <make-args>
  • Linux:使用环境变量SHELL/bin/sh执行命令
  • Windows:使用cmd.exe执行命令

path

path:添加路径至gdb中PATH(不修改外部PAHT

1
> path <dir>

GDB环境

环境变量

可以在gdb调试环境中自定义环境变量保存调试程序中需要的数据

1
2
3
4
5
6
7
8
> set $foo = *object_ptr
# 设置环境变量
> show convenience
# 查看当前设置的所有环境变量
> set $i=0
> print bar[$i++] -> contents
# 环境变量、程序变量交互使用
# 只需要回车重复上条命令,环境变量自动累加,逐个输出变量
  • 环境变量使用$开头(定义时也需要)
  • gdb会在首次使用时创建该变量,在以后使用直接对其赋值
  • 环境变量没有类型,可以定义任何类型,包括结构体、数组

寄存器

寄存器:存放了程序运行时数据

  • ip:程序当前运行指令地址
  • sp:程序当前堆栈地址
1
2
3
4
5
6
> info registers [<reg-name>]
# 输出寄存器值,缺省除浮点外所有
> info all-registers
# 输出所有寄存器值
> print $eip
# 输出寄存器`eip`值

其他

  • disassemble:查看程序当前执行的机器码
    • 此命令会dump当前内存中指令
  • si[gnal] <signal>:产生信号量发给被调试程序
    • signal:取值1-15,即Unix信号量
    • 此命令直接发送信号给被调试程序,而系统信号则是发送给 被调试程序,但由gdb截获

调试设置

delete

delete:删除断点(缺省)、自动输出表达式等

1
2
> delete [breakpoints] [bookmark] [checkpoints]
[display] [mem] [tracepoints] [tvariable] [num]
  • breakpoints:删除断点
  • bookmark:从书签中删除书签
  • checkpoints:删除检查点
  • display:取消程序停止时某些输出信息
  • mem:删除存储区
  • tracepoint:删除指定追踪点
  • tvariable:删除追踪变量

  • num

    • 缺省:删除所有断点/自动输出/书签等
    • 指定的序号
      • info查看
      • 可以使用a-b表示范围

disable

disable:禁用断点(缺省)、输出表达式等

1
2
3
> disable [breakpoints] [display] [frame-filter]
[mem] [pretty-printer] [probes] [type-printer]
[unwinder] [xmethod] [num]

breakpoints

禁用断点

1
> disable [breakpoints] [num]
  • 缺省:禁用所有断点
  • 仅指定的序号(info查看)

display

禁用程序停止时某些输出信息

frame-filter

禁用某些帧过滤器

mem

禁用存储区

pretty-printer

禁用某些打印美化

probes

禁用探测

type-printer

禁用某些类型打印

unwinder

禁用某些unwinder

xmethod

禁用某些xmethod

enable

enable:启用断点(缺省)、输出表达式等

1
2
3
> enable [breakpoints] [display] [frame-filter]
[mem] [pretty-printer] [probes] [type-printer]
[unwinder] [xmethod] [num]

breakpoints

启用断点

1
2
> enable [breakpoints] [num] [once] [delete]`
- `[delete]`:启用断点,生效后自动被删除
  • num:断点序号
    • 缺省:启用所有断点
  • once:启用断点一次
  • delete:启用生效后自动删除
  • count:启用断点count

display

启用程序停止时某些输出信息

frame-filter

启用某些帧过滤器

mem

启用存储区

pretty-printer

启用某些打印美化

probes

启用探测

type-printer

启用某些类型打印

unwinder

启用某些unwinder

xmethod

启用某些xmethod

backtrace

backtrace:打印函数栈

1
> backtrace/bt [-][<n>]
  • -:打印栈底信息
    • 缺省:打印栈顶信息
  • n:打印栈数量
    • 缺省:打印当前函数调用栈所有信息
  • 一般而言,程序停止时,最顶层栈就是当前函数栈

frame

frame:切换当前栈

1
> f[rame] [<n>]
  • n:切换到第n个栈帧
    • 缺省打印当前栈编号、断点信息(函数参数、行号等)

up

up:上移当前栈帧

1
> up [<n>]
  • n:上移n层栈帧
    • 缺省:上移1层

down

down:下移当前栈帧

1
> down [<n>]
  • n:下移n层栈帧
    • 缺省:下移1层

GDB命令大全

aliases

breakpoints

data

files

internals

obscure

running

stack

status

support

tracepoints

user-defined

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
$ gdb tst
# `gdb`启动可执行文件tst
(gdb) l
# `l`:list,列出源码
(gdb)
# 直接回车:重复上次命令
(gdb) break 16
# `break [n]`:在第`n`行设置断点
(gdb) break func
# `break func`:在函数`func`入口处设置断点
(gdb) info break
# `info break`:查看断点信息
(gdb) r
# `r`:run,执行程序,会自动在断点处停止
(gdb) n
# `n`:next,单条语句执行
(gdb) c
# `c`:continue,继续执行(下个断点、程序结束为止)
(gdb) p i
# `p i`:print,打印变量i的值
(gdb) bt
# `bt`:查看函数栈
(gdb) finish
# `finish`:退出**函数**
(gdb) 1
# `q`:quit,退出gdb