markdown_mermaid笔记

mermaid 概述

  • 怎么使用Mermaid?

    • 使用特定的Mermaid渲染器;
    • 使用集成了Mermaid渲染功能的Markdown编辑器,如Typora。使用时,需要将代码块的语言选择为Mermaid。
    • 行首加%%代表注释
  • Mermaid能绘制哪些图?

    • 饼状图:使用pie关键字,具体用法后文将详细介绍
    • 流程图:使用graph关键字,具体用法后文将详细介绍
    • 序列图:使用sequenceDiagram关键字
    • 甘特图:使用gantt关键字
    • 类图:使用classDiagram关键字
    • 状态图:使用stateDiagram关键字
    • 用户旅程图:使用journey关键字
  • 在线渲染器:Online FlowChart & Diagrams Editor


饼状图(pie)

数值最多支持2位小数——数据会以百分比的形式展示

1
2
3
4
5
pie
title 为什么总是宅在家里?
"喜欢宅" : 15
"天气太热或太冷" : 20
"穷" : 500
1
2
3
4
5
pie
title 为什么总是宅在家里?
"喜欢宅" : 15
"天气太热或太冷" : 20
"穷" : 500

流程图(graph)

1
2
3
4
5
6
graph LR
A[Start] --> B{Is it?};
B -- Yes --> C[OK];
C --> D[Rethink];
D --> B;
B -- No ----> E[End];
1
2
3
4
5
6
graph LR
A[Start] --> B{Is it?};
B -- Yes --> C[OK];
C --> D[Rethink];
D --> B;
B -- No ----> E[End];

方向

用于开头,声明流程图的方向。

  • graphgraph TBgraph TD:从上往下
  • graph BT:从下往上
  • graph LR:从左往右
  • graph RL:从右往左

结点

  • 无名字的结点:直接写内容,此时结点边框为方形;节点内容不能有空格
  • 有名字的结点:节点名后书写内容,内容左右有特定符号,结点边框由符号决定;节点内容可以有空格
1
2
3
4
5
6
7
8
graph
默认方形
id1[方形]
id2(圆边矩形)
id3([体育场形])
id4[[子程序形]]
id5[(圆柱形)]
id6((圆形))
1
2
3
4
5
6
7
8
graph
默认方形
id1[方形]
id2(圆边矩形)
id3([体育场形])
id4[[子程序形]]
id5[(圆柱形)]
id6((圆形))

1
2
3
4
5
6
7
8
graph
id1{菱形}
id2{{六角形}}
id3[/平行四边形/]
id4[\反向平行四边形\]
id5[/梯形\]
id6[\反向梯形/]
id7>非对称形状]
1
2
3
4
5
6
7
8
graph
id1{菱形}
id2{{六角形}}
id3[/平行四边形/]
id4[\反向平行四边形\]
id5[/梯形\]
id6[\反向梯形/]
id7>非对称形状]

连线样式

1
2
graph LR
a-->b--文本1-->c-->|文本2|d
1
2
graph LR
a-->b--文本1-->c-->|文本2|d

1
2
graph LR
a==>b==文本==>c
1
2
graph LR
a==>b==文本==>c

1
2
graph LR
a-.->b-.文本.->c
1
2
graph LR
a-.->b-.文本.->c

1
2
3
4
5
6
7
8
graph LR
a---b
b--文本1---c
c---|文本2|d
d===e
e==文本3===f
f-.-g
g-.文本.-h
1
2
3
4
5
6
7
8
graph LR
a---b
b--文本1---c
c---|文本2|d
d===e
e==文本3===f
f-.-g
g-.文本.-h

  • 其他连线:需要将graph关键字改为flowchart,除了新增加的连线形式外,上面三种线的渲染效果也会不同 (好像网页显示不了<-->)
1
2
3
4
5
flowchart LR
A o--o B
B <--> C
C x--x D
旧连线 --文本--> 也会不同
1
2
3
4
5
6
flowchart LR
A o--o B
B <--> C
C x--x D

旧连线 --文本--> 也会不同

  • 延长连线:增加相应字符即可,如下图中的B到E,连线中增加了一个-。字符可多次添加。
1
2
3
4
5
6
graph LR
A[Start] --> B{Is it?};
B -->|Yes| C[OK];
C --> D[Rethink];
D --> B;
B --->|No| E[End];
1
2
3
4
5
6
graph LR
A[Start] --> B{Is it?};
B -->|Yes| C[OK];
C --> D[Rethink];
D --> B;
B --->|No| E[End];

连线形式

直链

1
2
graph LR
A -- text --> B -- text2 --> C
1
2
graph LR
A -- text --> B -- text2 --> C

多重链

可以使用&字符,或单个描述

1
2
3
4
5
6
7
graph
a --> b & c--> d
A & B--> C & D
X --> M
X --> N
Y --> M
Y --> N
1
2
3
4
5
6
7
graph
a --> b & c--> d
A & B--> C & D
X --> M
X --> N
Y --> M
Y --> N

特殊字符

如果文本中有字符可以用双引号:

1
2
flowchart LR
A["括号 (text)"]---B["中括号 [text]"]
1
2
flowchart LR
A["括号 (text)"]---B["中括号 [text]"]

如果文本中要使用双引号或者其它更多特殊字符,可以使用HTML 实体来表示:

1
2
flowchart LR
A["引号#quot;"]---B["美元符号#36;"]---C["笑脸#128512;"]
1
2
flowchart LR
A["引号#quot;"]---B["美元符号#36;"]---C["笑脸#128512;"]

  • 实例:朱元璋家谱简图,圆圈代表皇帝
1
2
3
4
5
6
7
8
9
10
graph LR
emperor((朱八八))-.子.->father(朱五四)-.子.->朱四九-.子.->朱百六


朱雄英--长子-->朱标--长子-->emperor
emperor2((朱允炆))--次子-->朱标
朱樉--次子-->emperor
朱棡--三子-->emperor
emperor3((朱棣))--四子-->emperor
emperor4((朱高炽))--长子-->emperor3

子图(subgraph)

  • 需要将graph关键字改为flowchart,在代码段的开始加入subgraph,尾部加入end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
flowchart TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
flowchart TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2

  • 注释:在行首加入%%即可。
1
2
3
4
5
6
graph LR
%%这是一条注释,在渲染图中不可见
A[Hard edge] -->|Link text| B(Round edge)
B --> C{Decision}
C -->|One| D[Result one]
C -->|Two| E[Result two]
1
2
3
4
5
6
graph LR
%%这是一条注释,在渲染图中不可见
A[Hard edge] -->|Link text| B(Round edge)
B --> C{Decision}
C -->|One| D[Result one]
C -->|Two| E[Result two]

序列图(sequenceDiagram)

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
sequenceDiagram
title: hello
autonumber
participant B as Browser
participant C as Controller
participant M as Model
participant V as View
note left of C: 左note
note right of C: 右note
note over B, C: 共note
B -> C: 实线
activate C
C ->> B: 箭头实线
deactivate C
%%加号等价于activate M,减号等价于deactivate M
C -->+ M: 虚线
M -x+ V: 打叉箭头实线
V --x- M: 打叉箭头虚线
M -->>- C: 箭头虚线
%% 循环
loop 每5分钟
%% Highlight background
rect rgba(0, 0, 255, 0.1)
V ->> V: 自环
end
end
loop 每5分钟
B-->>C:汇报
end
%% 可选条件
opt 心情好
M-->>V:唱歌
end
%% 条件判断
alt Controller没睡
B-->>C:吃宵夜吗
else Model没睡
B-->>M:吃宵夜吗
end
%% 并行发送
par Controller to Model
C-->>+M:hello?
and Controller to View
C-->>+V:hello?
end
%% 并行接收
par Model to Controller
M-->>-C:hi!
and Model to Controller
V-->>-C:hi!
end
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
sequenceDiagram
title: hello
autonumber
participant B as Browser
participant C as Controller
participant M as Model
participant V as View
note left of C: 左note
note right of C: 右note
note over B, C: 共note
B -> C: 实线
activate C
C ->> B: 箭头实线
deactivate C
%%加号等价于activate M,减号等价于deactivate M
C -->+ M: 虚线
M -x+ V: 打叉箭头实线
V --x- M: 打叉箭头虚线
M -->>- C: 箭头虚线
%% 循环
loop 每5分钟
%% Highlight background
rect rgba(0, 0, 255, 0.1)
V ->> V: 自环
end
end
loop 每5分钟
B-->>C:汇报
end
%% 可选条件
opt 心情好
M-->>V:唱歌
end
%% 条件判断
alt Controller没睡
B-->>C:吃宵夜吗
else Model没睡
B-->>M:吃宵夜吗
end
%% 并行发送
par Controller to Model
C-->>+M:hello?
and Controller to View
C-->>+V:hello?
end
%% 并行接收
par Model to Controller
M-->>-C:hi!
and Model to Controller
V-->>-C:hi!
end

1
2
3
4
5
sequenceDiagram
actor Z as 张三
actor L as 李四
Z ->> L: 实线箭头
Z -) L: 实线开放箭头
1
2
3
4
5
sequenceDiagram
actor Z as 张三
actor L as 李四
Z ->> L: 实线箭头
Z -) L: 实线开放箭头

甘特图(gantt)

1
2
3
4
5
6
7
8
9
gantt
title 研发周期
dateFormat YYYY-MM-DD
section 项目
A 项目: A1, 2022-01-01, 30d
B 项目: after A1 , 20d
section 子任务
子任务1: 2022-01-12 , 12d
子任务2: 24d
1
2
3
4
5
6
7
8
9
gantt
title 研发周期
dateFormat YYYY-MM-DD
section 项目
A 项目: A1, 2022-01-01, 30d
B 项目: after A1 , 20d
section 子任务
子任务1: 2022-01-12 , 12d
子任务2: 24d

类图(classDiagram)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}

状态图(stateDiagram)

1
2
3
4
5
6
7
8
9
10
11
stateDiagram-v2
state "状态1" as State1
State2 : 状态2
[*] --> State1
State1 --> State2: 过渡文本
State2 --> State1
State2 --> State3
state State3 {
[*] --> second
second --> [*]
}
1
2
3
4
5
6
7
8
9
10
11
12
stateDiagram-v2
state "状态1" as State1
State2 : 状态2
[*] --> State1

State1 --> State2: 过渡文本
State2 --> State1
State2 --> State3
state State3 {
[*] --> second
second --> [*]
}

用户旅程图(journey)

linux网络6_VLAN

VLAN

什么是 VLAN

VLAN ( Virtual Local Area Network )又称虚拟局域网,是指在交换局域网的基础上,采用网络管理软件构建的可跨越不同网段、不同网络的端到端的逻辑网络。一个 VLAN 组成 一个逻辑子网, 即一个逻辑广播域,它可以覆盖多个网络设备,允许处于不同地理位置的网 络用户加入到一个逻辑子网中。

使用 VLAN 优点

  1. 控制广播风暴

    一个 VLAN 就是一个逻辑广播域,通过对VLAN的创建,隔离了广播,缩小了广播范围,可以控制广播风暴的产生。

  2. 提高网络整体安全性

    通过路由访问列表和 MAC 地址分配等VLAN划分原则,可以控制用户访问权限和逻辑网段大小,将不同用户群划分在不同VLAN从而提高交换式网络的整体性能和安全性。

  3. 网络管理简单、直观

    对于交换式以太网, 如果对某些用户重新进行网段分配, 需要网络管理员对网络系统的 物理结构重新进行调整,甚至需要追加网络设备,增大网络管理的工作量。而对于采用VLAN 技术的网络来说,一个 VLAN 可以根据部门职能、对象组或者应用将不同地理位置的网络用 户划分为一个逻辑网段。在不改动网络物理连接的情况下可以任意地将工作站在工作组或子 网之间移动。利用虚拟网络技术,大大减轻了网络管理和维护工作的负担,降低了网络维护 费用。在一个交换网络中,VLAN 提供了网段和机构的弹性组合机制

物理网卡、子网卡、虚拟 VLAN 网卡的关系:

  1. 物理网卡:服务器上物理网络接口设备,也就是要配置 trunk 的具体接口。
  2. 子网卡:子网卡并不是网络接口设备,但是可以作为网络接口在系统中出现,如 eth0:1 、 eth1:2 这种网络接口。必须要依赖于物理网卡,可以与物理网卡同时在系统中存 在并使用不同的 IP 地址,而且也拥有它们自己的网络接口配置文件。但是所依赖的物理网 卡 down 掉时子网卡也不能工作。
  3. 虚拟 VLAN 网卡:虚拟 VLAN 网卡也不是物理网络接口设备,可以作为网络接口在系统 中出现,与子网卡不同的是,没有自己的配置文件。是通过将物理网加入不同的 VLAN 而生 成的 VLAN 虚拟网卡。如果将一个物理网卡添加到多个 VLAN 当中去的话,就会有多个 VLAN 虚拟网卡出现,相关的 VLAN 信息都是保存在/proc/net/vlan/config 这文件中的, 以 eth0.1、eth1.2 命名
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
67
68
69
70
71
72
73
74
75
76
[root@li]# modinfo 8021q # 查看是否有 vlan 模块
[root@li]# modprobe 8021q # 添加模块
[root@li]# vconfig add eth0 10 # 给eth0添加vlan id 10
[root@li]# vconfig add eth0 20 # 给eth0添加vlan id 20
[root@li]# vconfig set_flag eth0.10 1 1 # 设置VLAN的REORDER_HDR参数,默认就行了。
[root@li]# vconfig set_flag eth0.20 1 1 # 同上
[root@TSG-li]# cat /proc/net/vlan/eth0.10 # 查看eth0.10参数
eth0.10 VID: 10 REORDER_HDR: 1 dev->priv_flags: 81
total frames received 1
total bytes received 68
Broadcast/Multicast Rcvd 1
total frames transmitted 3
total bytes transmitted 270
total headroom inc 0
total encap on xmit 3
Device: eth0
INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0
EGRESS priority Mappings:

[root@li]# ifconfig eth0 0.0.0.0
[root@li]# ifconfig eth0.10 192.168.10.10 netmask 255.255.255.0 up
[root@li]# ifconfig eth0.20 192.168.20.10 netmask 255.255.255.0 up
[root@li]# ifconfig eth0.10
eth0.10 Link encap:Ethernet HWaddr 00:0C:29:27:D6:74
inet addr:192.168.10.10 Bcast:192.168.10.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe27:d674/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:68 (68.0 b) TX bytes:270 (270.0 b)

[root@li]# ifconfig eth0.20
eth0.20 Link encap:Ethernet HWaddr 00:0C:29:27:D6:74
inet addr:192.168.20.10 Bcast:192.168.20.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe27:d674/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:68 (68.0 b) TX bytes:176 (176.0 b)

[root@li]# brctl addbr br0 # 创建一个网桥
[root@li]# brctl show
[root@li]# brctl addif br0 eth0.10 # 把vlan eth0.10加到网桥上
[root@li]# brctl addif br0 eth0.20 # 把vlan eth0.20加到网桥上
[root@li]# brctl show

bridge name bridge id STP enabled interfaces
br0 8000.000c29f59487 no eth0.10,eth0.20

#STP enabled为no,可以用brctl stp <bridge> <state> 控制网桥是否加入STP树中,<state>可以是'on'或者'yes'表示加入stp树中,'off'表示关闭stp。

[root@li]# brctl stp br0 yes
[root@li]# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000c29f59487 yes eth0.10,eth0.20

[root@li]# brctl stp br0 off
[root@li]# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000c29f59487 no eth0.10,eth0.20

[root@li]# ifconfig br0 172.16.1.254/24 # 配置网桥br0的ip # ip a a 172.16.1.254/24 dev br0

[root@li]# ifconfig eth0.10 down
[root@li]# ip link set eth0.10 name eth10 # 改eth0.10网卡名称
[root@li]# ifconfig eth10 up
[root@li]# ifconfig eth10

[root@li]# brctl showmacs br0 #查看Mac地址和接口的对应关系,即MAC表,思科交换机设备叫CAM表

[root@li]# vconfig rem eth0.10 #删除vlan
[root@li]# vconfig rem eth0.20
[root@li]# ifconfig br0 down
[root@li]# brctl delbr br0 #删除网桥

brctl

有五台主机。其中一台主机装有linux ,安装了网桥模块,而且有四块物理网卡,分别连接同一网段的其他主机。我们希望其成为一个网桥,为其他四台主机(IP分别为192.168.1.2 ,192.168.1.3,192.168.1.4,192.168.1.5) 之间转发数据包。同时,为了方便管理,希望网桥能够有一个IP(192.168.1.1),那样管理员就可以在192.168.1.0/24网段内的主机上telnet到网桥,对其进行配置,实现远程管理。

1
brctl addbr br0 #建立一个逻辑网段,名称为br0

建立一个逻辑网段之后,我们还需要为这个网段分配特定的端口。在Linux中,一个端口实际上就是一个物理网卡。而每个物理网卡的名称则分别为eth0,eth1,eth2,eth3。我们需要把每个网卡一一和br0这个网段联系起来,作为br0中的一个端口。

1
2
3
4
brctl addif br0 eth0 #让eth0成为br0的一个端口
brctl addif br0 eth1 #同上
brctl addif br0 eth2 #同上
brctl addif br0 eth3 #同上

网桥的每个物理网卡作为一个端口,运行于混杂模式,而且是在链路层工作,所以就不需要IP了。

1
2
3
4
ifconfig eth0 0.0.0.0
ifconfig eth1 0.0.0.0
ifconfig eth2 0.0.0.0
ifconfig eth3 0.0.0.0
1
ifconfig br0 192.168.1.1 #给br0的虚拟网卡配置IP,那样就能远程管理网桥。

quilt笔记

quilt

使用例子

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# zh @ li in ~/li/tmp/hello [15:08:57]
$ ls
main.c

# zh @ li in ~/li/tmp/hello [15:08:58]
$ cat main.c
#include <stdio.h>
int main(int argv,char **argc) {
printf("Hello world\n");
return 0;
}

# zh @ li in ~/li/tmp/hello [15:09:04]
$ quilt new 0000-add_patched_first_line.patch # 创建一个patch
Patch patches/0000-add_patched_first_line.patch is now on top

# zh @ li in ~/li/tmp/hello [15:09:05]
$ tree
.
├── main.c
└── patches
└── series

1 directory, 2 files

# zh @ li in ~/li/tmp/hello [15:09:16]
$ quilt add main.c # 添加追踪文件
File main.c added to patch patches/0000-add_patched_first_line.patch

# zh @ li in ~/li/tmp/hello [15:09:18]
$ quilt edit main.c # 修改代码
File main.c is already in patch patches/0000-add_patched_first_line.patch

# zh @ li in ~/li/tmp/hello [15:09:52]
$ quilt diff # 查看代码改动内容
Index: hello/main.c
===================================================================
--- hello.orig/main.c
+++ hello/main.c
@@ -1,5 +1,6 @@
#include <stdio.h>
int main(int argv,char **argc) {
printf("Hello world\n");
+ printf("I am the first patched line\n");
return 0;
}

# zh @ li in ~/li/tmp/hello [15:10:03]
$ quilt refresh -pab # 把代码改动保存到patch文件
Refreshed patch patches/0000-add_patched_first_line.patch

# zh @ li in ~/li/tmp/hello [15:10:08]
$ tree
.
├── main.c
└── patches
├── 0000-add_patched_first_line.patch
└── series

1 directory, 3 files

# zh @ li in ~/li/tmp/hello [15:11:18]
$ quilt new 0001-add_patched_second_line.patch # 创建第2个patch(建议先用quilt push -a应用所有已存在的patch,然后再执行这个)
Patch patches/0001-add_patched_second_line.patch is now on top

# zh @ li in ~/li/tmp/hello [15:11:49]
$ quilt edit main.c
File main.c added to patch patches/0001-add_patched_second_line.patch

# zh @ li in ~/li/tmp/hello [15:11:56]
$ quilt diff
Index: hello/main.c
===================================================================
--- hello.orig/main.c
+++ hello/main.c
@@ -2,5 +2,6 @@
int main(int argv,char **argc) {
printf("Hello world\n");
printf("I am the first patched line\n");
+ printf("I am the second patched line\n");
return 0;
}

# zh @ li in ~/li/tmp/hello [15:12:07]
$ quilt refresh -pab
Refreshed patch patches/0001-add_patched_second_line.patch

# zh @ li in ~/li/tmp/hello [15:12:09]
$ tree
.
├── main.c
└── patches
├── 0000-add_patched_first_line.patch
├── 0001-add_patched_second_line.patch
└── series

1 directory, 4 files

# zh @ li in ~/li/tmp/hello [15:12:15]
$ quilt series # 查看全部patch
patches/0000-add_patched_first_line.patch
patches/0001-add_patched_second_line.patch

使用说明

quilt配置~/.quiltrc或者/etc/quilt.quiltrc,可以打开/usr/bin/quilt来看看

1
2
3
4
5
6
7
8
cat > ~/.quiltrc <<EOF
QUILT_DIFF_ARGS="--no-timestamps --no-index -p ab --color=auto"
QUILT_REFRESH_ARGS="--no-timestamps --no-index -p ab"
QUILT_SERIES_ARGS="--color=auto"
QUILT_PATCH_OPTS="--unified"
QUILT_DIFF_OPTS="-p"
EDITOR="vim"
EOF

前面几个都是对脚本里面使用diff,patch这些工具时,所使用的参数进行配置,EDITOR这项配置为自己习惯的编辑器

补丁名字以数字开头,然后通过-符号链接一个简短的描述,其中数字的大小就是最终打补丁的顺序

quilt files:查看被追踪的文件

quilt edit main.c:添加追踪并编辑,也可以quilt add main.c之后直接用其他编辑器编辑

quilt top:查看最近的补丁(最后一个)(初始为空)

quilt push:应用补丁(压栈)

1
2
3
quilt push -a #应用所有
quilt push 010-xxx.patch #应用从000->010所有的补丁
quilt push #应用单个补丁

quilt pop:取消应用(弹栈)(回滚)

1
2
3
quilt pop -a  #取消所有
quilt pop 002-yyy.patch #取消到指定指定补丁处(当前->002)
quilt pop #取消单个

quilt edit main.c:修改,开发

quilt diff:查看修改(当前补丁)

quilt refresh:保存为patch

quilt files:查看当前补丁中已修改文件

cmake笔记

cmake

project()

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
67
68
69
70
71
72
73
74
75
76
77
# zh @ li in ~/tmp/hello [22:45:26]
$ tree
.
├── CMakeLists.txt
└── subhello
└── CMakeLists.txt

1 directory, 2 files

# zh @ li in ~/tmp/hello [22:45:27]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(hello VERSION 0.1.2.3 DESCRIPTION "an hello program" LANGUAGES C HOMEPAGE_URL “https://www.hello.com”)

add_subdirectory(subhello)

# zh @ li in ~/tmp/hello [22:45:34]
$ cat subhello/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(subhello VERSION 3.2.1.0 DESCRIPTION "an subhello program" LANGUAGES C HOMEPAGE_URL “https://www.subhello.com”)

function(print)
foreach(var ${ARGN})
message("${var} = ${${var}}")
endforeach()
endfunction()


print(
PROJECT_NAME # PROJECT_NAME = subhello # 当前
CMAKE_PROJECT_NAME # CMAKE_PROJECT_NAME = hello # 顶层

hello_SOURCE_DIR # hello_SOURCE_DIR = /home/zh/tmp/hello # hello项目
subhello_SOURCE_DIR # subhello_SOURCE_DIR = /home/zh/tmp/hello/subhello # subhello项目
PROJECT_SOURCE_DIR # PROJECT_SOURCE_DIR = /home/zh/tmp/hello/subhello # 当前
CMAKE_SOURCE_DIR # CMAKE_SOURCE_DIR = /home/zh/tmp/hello # 顶层

hello_BINARY_DIR # hello_BINARY_DIR = /home/zh/tmp/hello/build # hello项目
subhello_BINARY_DIR # subhello_BINARY_DIR = /home/zh/tmp/hello/build/subhello # subhello项目
PROJECT_BINARY_DIR # PROJECT_BINARY_DIR = /home/zh/tmp/hello/build/subhello # 当前
CMAKE_BINARY_DIR # CMAKE_BINARY_DIR = /home/zh/tmp/hello/build # 顶层

hello_VERSION # hello_VERSION = 0.1.2.3
subhello_VERSION # subhello_VERSION = 3.2.1.0
PROJECT_VERSION # PROJECT_VERSION = 3.2.1.0
CMAKE_PROJECT_VERSION # CMAKE_PROJECT_VERSION = 0.1.2.3 #顶层

hello_VERSION_MAJOR # hello_VERSION_MAJOR = 0
subhello_VERSION_MAJOR # subhello_VERSION_MAJOR = 3
PROJECT_VERSION_MAJOR # PROJECT_VERSION_MAJOR = 3
CMAKE_PROJECT_VERSION_MAJOR # PROJECT_VERSION_MAJOR = 0 #顶层

hello_VERSION_MINOR # hello_VERSION_MINOR = 1
subhello_VERSION_MINOR # subhello_VERSION_MINOR = 2
PROJECT_VERSION_MINOR # PROJECT_VERSION_MINOR = 2
CMAKE_PROJECT_VERSION_MINOR # PROJECT_VERSION_MINOR = 1 #顶层

hello_VERSION_PATCH # hello_VERSION_PATCH = 2
subhello_VERSION_PATCH # subhello_VERSION_PATCH = 1
PROJECT_VERSION_PATCH # PROJECT_VERSION_PATCH = 1
CMAKE_PROJECT_VERSION_PATCH # PROJECT_VERSION_PATCH = 2 #顶层

hello_VERSION_TWEAK # hello_VERSION_TWEAK = 3
subhello_VERSION_TWEAK # subhello_VERSION_TWEAK = 0
PROJECT_VERSION_TWEAK # PROJECT_VERSION_TWEAK = 0
CMAKE_PROJECT_VERSION_TWEAK # PROJECT_VERSION_TWEAK = 3 #顶层

hello_DESCRIPTION # PROJECT_DESCRIPTION = an hello program
subhello_DESCRIPTION # PROJECT_DESCRIPTION = an subhello program
PROJECT_DESCRIPTION # PROJECT_DESCRIPTION = an subhello program
CMAKE_PROJECT_DESCRIPTION # CMAKE_PROJECT_DESCRIPTION = an hello program # 顶层

hello_HOMEPAGE_URL # hello_HOMEPAGE_URL = “https://www.hello.com”
subhello_HOMEPAGE_URL # subhello_HOMEPAGE_URL = “https://www.subhello.com”
PROJECT_HOMEPAGE_URL # subhello_HOMEPAGE_URL = “https://www.subhello.com”
CMAKE_PROJECT_HOMEPAGE_URL # hello_HOMEPAGE_URL = “https://www.hello.com” # 顶层
)

GNUInstallDirs

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
67
68
69
70
71
# zh @ li in ~/li/tmp/hello [11:19:03]
$ tree
.
└── CMakeLists.txt

0 directories, 1 file

# zh @ li in ~/li/tmp/hello [11:19:04]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(hello VERSION 0.1.2.3 DESCRIPTION "an hello program" LANGUAGES C)

function(print)
foreach(var ${ARGN})
message("${var} # ${var} = ${${var}}")
endforeach()
endfunction()

set(CMAKE_INSTALL_PREFIX "/usr") # 在include(GNUInstallDirs)前设置,否则就是默认/usr/local
include(GNUInstallDirs)
print(
CMAKE_INSTALL_PREFIX # CMAKE_INSTALL_PREFIX = /

CMAKE_INSTALL_BINDIR # CMAKE_INSTALL_BINDIR = usr/bin
CMAKE_INSTALL_FULL_BINDIR # CMAKE_INSTALL_FULL_BINDIR = /usr/bin

CMAKE_INSTALL_SBINDIR # CMAKE_INSTALL_SBINDIR = usr/sbin
CMAKE_INSTALL_FULL_SBINDIR # CMAKE_INSTALL_FULL_SBINDIR = /usr/sbin

CMAKE_INSTALL_LIBEXECDIR # CMAKE_INSTALL_LIBEXECDIR = usr/libexec
CMAKE_INSTALL_FULL_LIBEXECDIR # CMAKE_INSTALL_FULL_LIBEXECDIR = /usr/libexec

CMAKE_INSTALL_SYSCONFDIR # CMAKE_INSTALL_SYSCONFDIR = etc
CMAKE_INSTALL_FULL_SYSCONFDIR # CMAKE_INSTALL_FULL_SYSCONFDIR = /etc

CMAKE_INSTALL_SHAREDSTATEDIR # CMAKE_INSTALL_SHAREDSTATEDIR = usr/com
CMAKE_INSTALL_FULL_SHAREDSTATEDIR # CMAKE_INSTALL_FULL_SHAREDSTATEDIR = /usr/com

CMAKE_INSTALL_LOCALSTATEDIR # CMAKE_INSTALL_LOCALSTATEDIR = var
CMAKE_INSTALL_FULL_LOCALSTATEDIR # CMAKE_INSTALL_FULL_LOCALSTATEDIR = /var

CMAKE_INSTALL_RUNSTATEDIR # CMAKE_INSTALL_RUNSTATEDIR = var/run
CMAKE_INSTALL_FULL_RUNSTATEDIR # CMAKE_INSTALL_FULL_RUNSTATEDIR = /var/run

CMAKE_INSTALL_LIBDIR # CMAKE_INSTALL_LIBDIR = usr/lib
CMAKE_INSTALL_FULL_LIBDIR # CMAKE_INSTALL_FULL_LIBDIR = /usr/lib

CMAKE_INSTALL_INCLUDEDIR # CMAKE_INSTALL_INCLUDEDIR = usr/include
CMAKE_INSTALL_FULL_INCLUDEDIR # CMAKE_INSTALL_FULL_INCLUDEDIR = /usr/include

CMAKE_INSTALL_OLDINCLUDEDIR # CMAKE_INSTALL_OLDINCLUDEDIR = /usr/include
CMAKE_INSTALL_FULL_OLDINCLUDEDIR # CMAKE_INSTALL_FULL_OLDINCLUDEDIR = /usr/include

CMAKE_INSTALL_DATAROOTDIR # CMAKE_INSTALL_DATAROOTDIR = usr/share
CMAKE_INSTALL_FULL_DATAROOTDIR # CMAKE_INSTALL_FULL_DATAROOTDIR = /usr/share

CMAKE_INSTALL_DATADIR # CMAKE_INSTALL_DATADIR = usr/share
CMAKE_INSTALL_FULL_DATADIR # CMAKE_INSTALL_FULL_DATADIR = /usr/share

CMAKE_INSTALL_INFODIR # CMAKE_INSTALL_INFODIR = usr/share/info
CMAKE_INSTALL_FULL_INFODIR # CMAKE_INSTALL_FULL_INFODIR = /usr/share/info

CMAKE_INSTALL_LOCALEDIR # CMAKE_INSTALL_LOCALEDIR = usr/share/locale
CMAKE_INSTALL_FULL_LOCALEDIR # CMAKE_INSTALL_FULL_LOCALEDIR = /usr/share/locale

CMAKE_INSTALL_MANDIR # CMAKE_INSTALL_MANDIR = usr/share/man
CMAKE_INSTALL_FULL_MANDIR # CMAKE_INSTALL_FULL_MANDIR = /usr/share/man

CMAKE_INSTALL_DOCDIR # CMAKE_INSTALL_DOCDIR = usr/share/doc/hello
CMAKE_INSTALL_FULL_DOCDIR # CMAKE_INSTALL_FULL_DOCDIR = /usr/share/doc/hello
)

交叉编译

1
2
3
4
set(CMAKE_SYSTEM_NAME Linux)
set(TOOLCHAIN_PATH /OPT/gcc-arm-linux-gnueabi)
set(CMAKE_C_COMPILER ${tools}/bin/arm-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-linux-gnueabi-g++)

set(CMAKE_SYSTEM_NAME Linux)该指令必须存在,其目的是设置目标机使用的操作系统名称,支持Linux,QNX,WindowsCE,Android等。如果没有操作系统,那么就写 Generic。执行该指令后,cmake 变量——CMAKE_CROSSCOMPILING 会自动被设置为 TRUE,此时 cmake 就会“知道“现在执行的是交叉编译;

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
# zh @ li in ~/li/tmp/hello [19:44:35]
$ tree
.
├── cmake
│   └── toolchain_x86.cmake
├── CMakeLists.txt
└── src
└── main.c

2 directories, 3 files

# zh @ li in ~/li/tmp/hello [19:44:38]
$ cat cmake/toolchain_x86.cmake
set(CMAKE_SYSTEM_NAME Linux)

set(TOOLCHAIN_PATH /usr/bin)
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)

# zh @ li in ~/li/tmp/hello [19:44:41]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(hello VERSION 0.1.2.3 DESCRIPTION "an hello program" LANGUAGES C)

FILE(GLOB SRC_FILES "src/*.c")

add_executable(${PROJECT_NAME} ${SRC_FILES})

# zh @ li in ~/li/tmp/hello [19:44:43]
$ cat src/main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) { printf("hello world\n"); return 0; }

# zh @ li in ~/li/tmp/hello [19:44:46]
$ mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain_x86.cmake ..
-- The C compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/zh/li/tmp/hello/build

# zh @ li in ~/li/tmp/hello/build [19:44:53]
$ make
[ 50%] Building C object CMakeFiles/hello.dir/src/main.c.o
[100%] Linking C executable hello
[100%] Built target hello

注意:-DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain_x86.cmake 不能直接写入CMakeLists.txtinclude(toolchain_x86.cmake)

需要链接系统库或第三方库时,cmake 提供了FIND_PROGRAM()FIND_LIBRARY()FIND_FILE()FIND_PATH()FIND_PACKAGE() 来查找库路径或包。默认情况下,上述指令查找的是主机上的文件,其并不适用于目标机器。为此cmake提供了相应的变量:

set(CMAKE_FIND_ROOT_PATH path1 path2 path3 ...):设置其值为多个目录,这样 FIND_XXX() 就会从这些目录中查找。

跟随该变量的有下述 3 个变量,它们的值为 NEVERONLYBOTH

CMAKE_FIND_ROOT_PATH_MODE_PROGRAM若设为

  • NEVER,则FIND_PROGRAM() 不会在 CMAKE_FIND_ROOT_PATH 指定的目录中寻找
  • ONLY,则FIND_PROGRAM() 只会从 CMAKE_FIND_ROOT_PATH 指定的目录中寻找
  • BOTH,则FIND_PROGRAM() 会优先从 CMAKE_FIND_ROOT_PATH 指定的目录中寻找,再从默认的目录中寻找

FIND_PROGRAM() 通常用于寻找可执行程序,给后续的 EXECUTE_PROCESS()ADD_CUSTOM_COMMAND() 指令使用。并且,只有主机在生成编译文件时使用该可执行程序。因此通常设置 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

CMAKE_FIND_ROOT_PATH_MODE_LIBRARY:交叉编译只能使用 FIND_LIBRARY() 查找符合目标机器的库文件,因此设置set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

CMAKE_FIND_ROOT_PATH_MODE_INCLUDE:同上,设为 ONLY

编译选项

1
2
3
add_definitions(-std=c++11)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -Wl,-rpath=./:./lib") #-Wl,-rpath=./
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wl,-rpath=./:./lib") #-Wall
1
cmake -DCMAKE_PREFIX_PATH=/path/to -DCMAKE_LIBRARY_PATH=/path/to -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_C_FLAGS=-m32

find_xxx()

  1. set(CMAKE_PREFIX_PATH "/path/libs/xxx-install")之后,pkg_search_module()就能去/path/libs/xxx-install找到库的libxxx.pc文件
  2. set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/my/path/to/pkgconfig")之后,也能找找到libxxx.pc

pkg_search_module()

旧版本: pkg_search_module(PKG_XXX REQUIRED xxx)生成3个变量

  • PKG_XXX_INCLUDE_DIRS:编译时xxx库提供的头文件目录
  • PKG_XXX_LIBRARIES:只提供xxx库的名字,不提供路径
  • PKG_XXX_LDFLAGS:包含xxx的路径和库

你可以:target_link_libraries(my_proj ${PKG_XXX_LDFLAGS})

新版本: pkg_search_module(PKG_XXX REQUIRED IMPORTED_TARGET opencv)里面的IMPORTED_TARGET会创建一个供你直接在项目中使用的目标
你可以: target_link_libraries(my_proj PRIVATE PkgConfig::PKG_XXX)

iperf笔记

iperf

服务器

1
2
3
4
iperf3 -s  #-s 表示以服务器方式启动
#-B 表示监听指定 IP地址, 0.0.0.0 表示监听 本地IP 和 局域网(公网)IP
#-p 表示监听指定 端口,上述我们指定监听的端口号是 5207 ,没有该参数时,默认端口号是 5201
iperf3 -s -B 0.0.0.0 -p 5207

客户端

1
2
3
4
5
6
7
8
iperf3 -c 192.168.198.39    # -c 表示以客户端方式启动 iperf
iperf3 -c 192.168.198.39 -R # -R 用相反的模式运行,服务端发送,客户端接收
#-u 表示用 udp 连接来测速,默认是 tcp 连接测试,因为 tcp 要进行确认,所以不如 udp 测试的准确
#-b 目标带宽,UDP模式使用的带宽,单位b/s,0表示无限制,此选项与 -u 选项相关,默认值是1 Mb/s,1g 表示估计能跑 1Gbps
#-t 表示持续测试时间,10 表示测试 10s
#-i 表示多少秒输出一次测试结果,1 表示 1s 刷新一次
#-p 表示测试服务器端口,5207是测速服务器的端口
iperf3 -c 192.168.3.83 -b 1g -t 10 -i 1 -u -p 5207

vscose笔记

vscode配置

vscode插件vim:esc和caps没切换,文件-首选项-设置,搜Keyboard:Dispatch改为keyCode重启软件即可

vscode调试C/C++

安装以下工具(本地调试直接本地安装,若远程调试就ssh之后在远程安装)

  • vscode插件C/C++:C/C++ IntelliSense, debugging, and code browsing.
  • vscode插件Cmake Tools:Extended CMake support in Visual Studio Code

系统ubuntu:

1
sudo apt install -y build-essential gdb cmake

系统arch:

1
sudo pacman -S base-devel gdb cmake

本地单文件调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# zh @ li in ~/hello [20:14:03]
$ tree -a
└── hello.c

# zh @ li in ~/hello [20:15:03]
$ cat hello.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
for(int i = 0; i < 2; i++){
printf("hello world %d\n", i);
}
return 0;
}

运行-添加配置-C++(GDB/LLDB)-gcc(/usr/bin/gcc),自动生成 launch.json 和 tasks.json 并进入调试(此时就可以打断点重新调试,在断点时,在调试控制台可以敲指令修改变量值等等的)

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
67
68
69
70
71
72
73
74
75
76
77
# zh @ li in ~/li/tmp/hello [21:39:07]
$ tree -a
.
├── hello
├── hello.c
└── .vscode
├── launch.json #该文件用于调试
└── tasks.json #该文件用于编译

1 directory, 4 files

# zh @ li in ~/li/tmp/hello [21:43:09]
$ cat .vscode/tasks.json
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc 生成活动文件",
"command": "/usr/bin/gcc",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "调试器生成的任务。"
}
],
"version": "2.0.0"
}%
# zh @ li in ~/li/tmp/hello [21:46:58]
$ cat .vscode/launch.json
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gcc - 生成和调试活动文件",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}", //改为编译生成的可执行文件路径
"args": [], //想要传给可执行文件的参数
"stopAtEntry": false, //true表示在main第一行断点调试
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: gcc 生成活动文件",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}

本地调试cmake项目

ctrl+shift+p-CMake:QuickStart-GCC 11.2.0 x86_64-pc-linux-gnu Using compilers:C=/usr/bin/gcc,CXX=/usr/bin/g++-项目名hello-Executable

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
# zh @ li in ~/li/tmp/hello [22:15:38]
$ tree -a -I build
.
├── CMakeLists.txt
└── main.cpp

0 directories, 2 files

# zh @ li in ~/li/tmp/hello [22:16:16]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(hello VERSION 0.1.0)

include(CTest)
enable_testing()

add_executable(hello main.cpp)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

# zh @ li in ~/li/tmp/hello [22:16:23]
$ cat main.cpp
#include <iostream>

int main(int, char**) {
std::cout << "Hello, world!\n";
}

ctrl+shift+p-CMake:Select a kit可修改编译工具链(也可在状态栏点击修改或ctrl+shift+p-CMake:Edit User-Local Cmake Kits)

ctrl+shift+p-CMake:Select Variant可修改编译级别类型(也可在状态栏点击修改)

ctrl+shift+p-CMake:Configure配置项目

ctrl+shift+p-CMake:Build构建项目(也可在状态栏点击build)默认情况是构建所有目标,可以ctrl+shift+p-CMake:Set Build Target选择要构建的目标(也可以点状态栏)

设置断点过后,ctrl+shift+p-CMake:Debug即可开始调试(也可按F5)

远程调试

  1. ssh-copy-id usr@ipaddr
  2. vscode插件Remote-SSH:Open any folder on a remote machine using SSH and take advantage of VS Code’s full feature set.
  3. vscode通过ssh登陆到远程
  4. vscode搜索前面的插件,选择安装到远程
  5. 打开项目,调试方式跟前面的一样

php笔记

php小笔记

php在线工具

三目运算

1
2
3
4
5
10%2==0 ? echo '$a 是偶数' : echo '$a 是奇数';
10%2==0 ? "" : echo '$a 是奇数';
$a = 10; $b = 6; $c = 12;
$x = $a>$b ? ($a<$c ? $c-$a : $a-$c) : ($b<$c ? $c-$b : $b-$c);
echo '$x ='.$x; //$x =2

字符串

数组转换为字符串

1
2
3
4
$arr = array('Hello','World!','Beautiful','Day!');
echo implode(" ",$arr)."\n"; //Hello World! Beautiful Day!
echo join(" ",$arr)."\n"; //同上
echo implode($arr)."\n"; //!HelloWorld!BeautifulDay!
1
2
3
4
5
6
7
8
9
$arrs = array(
array('topicid' => 1),
array('topicid' => 2),
array('topicid' => 6)
);

$topicid = ''; //变量赋值为空
foreach($arrs as $key=>$vals){ $topicid.=$vals['topicid'].','; }
echo rtrim($topicid, ','); //1,2,6 使用 rtrim() 函数从字符串右端删除字符:

字符串切割为数组

1
2
3
4
5
$str = "hello,,world";
print_r(explode(",", $str)); //Array ( [0] => hello [1] => [2] => world)
//将字符串分割成数组,参数二将字符串从左向右每3个字符分割一次,最后的不够3个了 有几个算几个
print_r(str_split($str, 3)); //Array ( [0] => hel [1] => lo, [2] => ,wo [3] => rld)
print_r(preg_split("/,+/", $str)); //Array ( [0] => hello [1] => world)

插入元素到数组末尾

1
$a=array("red","green"); array_push($a,"blue","yellow"); print_r($a); // Array ( [0] => red [1] => green [2] => blue [3] => yellow)

数组相减

1
2
3
4
$a1 = [1,2,3,4,5];
$a2 = [2,4,6];
print_r(array_intersect($a1, $a2)); // Array ( [1] => 2 [3] => 4) //交集
print_r(array_diff($a1, array_intersect($a1, $a2))); // Array ( [0] => 1 [2] => 3 [4] => 5) //减去交集(差集)

遍历修改数组

1
2
3
4
$array= array(1,2,3,4,5,);
foreach ($array as &$value) { $value = $value*2; } //使用引用符号
unset($value); //遍历后value依然引用着最后元素,可用unset取消
print_r($array); //Array ( [0] => 2 [1] => 4 [2] => 6 [3] => 8 [4] => 10)

换行改为br换行

1
2
3
echo nl2br("One line.\nAnother line.");
//One line.<br />
//Another line.

执行shell命令

这三个函数在默认的情况下,都是被禁止了的,如果要使用这几个函数,就要先修改php的配置文件php.ini,查找关键字disable_functions,将这一项中的这几个函数名删除掉,然后注意重启apache。

system()

1
2
3
4
$last_line=system("ping 192.168.16.1 -c1", $status); //输出到当前位置,返回最后一行字符串
if($status){ echo "执行失败\n"; }
else { echo "执行成功\n"; }
echo "last_line:".$last_line;
1
2
3
4
5
6
7
8
PING 192.168.16.1 (192.168.16.1) 56(84) bytes of data.
64 bytes from 192.168.16.1: icmp_seq=1 ttl=63 time=2.37 ms

--- 192.168.16.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.372/2.372/2.372/0.000 ms
执行成功
last_line:rtt min/avg/max/mdev = 2.372/2.372/2.372/0.000 ms

exec()

1
2
3
4
$last_line=exec("ping 192.168.16.1 -c1", $result, $status); //输出到数组$result,返回最后一行字符串
if($status){ echo "执行失败\n";print_r($result); }
else { echo "执行成功\n";print_r($result); }
echo "last_line:".$last_line;
1
2
3
4
5
6
7
8
9
10
11
执行成功
Array
(
[0] => PING 192.168.16.1 (192.168.16.1) 56(84) bytes of data.
[1] => 64 bytes from 192.168.16.1: icmp_seq=1 ttl=63 time=2.54 ms
[2] =>
[3] => --- 192.168.16.1 ping statistics ---
[4] => 1 packets transmitted, 1 received, 0% packet loss, time 0ms
[5] => rtt min/avg/max/mdev = 2.536/2.536/2.536/0.000 ms
)
last_line:rtt min/avg/max/mdev = 2.536/2.536/2.536/0.000 ms

passthru()

passthru('ls',$res);输出到当前位置,命令执行结果存到$res,函数返回void

form checkbox

1
2
3
4
5
6
7
8
9
<!--a.html-->
<form action="checkbox-form.php" method="post">
<label>使能: <input type="checkbox" name="enable" value="enabled" /></label><br/>
<label><input type="checkbox" name="testarray[]" value="apple" />苹果</label><br/>
<label><input type="checkbox" name="testarray[]" value="banana" />香蕉</label><br/>
<label><input type="checkbox" name="testarray[]" value="cat" /></label><br/>
<label><input type="checkbox" name="testarray[]" value="dog" /></label><br/>
<input type="submit" name="提交" value="Submit" />
</form>
1
2
3
4
//checkbox-form.php
if(isset($_POST['enable']) && $_POST['enable'] == 'enabled') { echo "enabled<br/>"; }
else { echo "disabled<br/>"; }
if(isset($_POST['testarray'])){ var_dump($_POST['testarray']); if(empty($_POST['testarray'])) { echo("You select nothing."); } }
1
2
disabled
array(3) { [0]=> string(5) "apple" [1]=> string(6) "banana" [2]=> string(3) "cat" }

form 提交多维数组

二维

1
2
3
4
5
6
7
8
<tr>
<td><input name="params[0][a]" type="text" /></td>
<td><input name="params[0][b]" type="text" /></td>
</tr>
<tr>
<td><input name="params[1][a]" type="text" /></td>
<td><input name="params[1][b]" type="text" /></td>
</tr>
1
2
3
4
5
6
7
8
array(2) {
["params"]=> array(2) {
[0]=> array(2) {
["a"]=> string(1) "1" ["b"]=> string(1) "2" }
[1]=> array(2) {
["a"]=> string(1) "3" ["b"]=> string(1) "4" }
}
}

三维和关联数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<tr>
<td><input name="params[hello]" type="text" /></td>
<td><input name="params[world]" type="text" /></td>
</tr>
<tr>
<td><input name="params[0][0][a]" type="text" /></td>
<td><input name="params[0][0][b]" type="text" /></td>
</tr>
<tr>
<td><input name="params[0][1][a]" type="text" /></td>
<td><input name="params[0][1][b]" type="text" /></td>
</tr>
<tr>
<td><input name="params[1][0][a]" type="text" /></td>
<td><input name="params[1][0][b]" type="text" /></td>
</tr>
<tr>
<td><input name="params[1][1][a]" type="text" /></td>
<td><input name="params[1][1][b]" type="text" /></td>
</tr>
1
2
3
4
5
6
7
8
9
10
11
12
array(2) {
["params"]=> array(4) {
["hello"]=> string(1) "h"
["world"]=> string(1) "w"
[0]=> array(2) {
[0]=> array(2) { ["a"]=> string(1) "1" ["b"]=> string(1) "2" }
[1]=> array(2) { ["a"]=> string(1) "3" ["b"]=> string(1) "4" } }
[1]=> array(2) {
[0]=> array(2) { ["a"]=> string(1) "5" ["b"]=> string(1) "6" }
[1]=> array(2) { ["a"]=> string(1) "7" ["b"]=> string(1) "8" } }
}
}

注意:

  1. html代码中,提交name数组中的key不用加引号,加引号之后后端的键值中会存在引号
  2. 现在更流行的做法是ajax提交,用ajax提交的时候怎么获取表单中的值呢?通过jquery提供的serialize()方法可以很方便的将所有要提交的内容拼接成url字符串,然后通过get的方式就可以提交给后台了。

ajax提交多维数组

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
//var_dump($_POST);
<form id="myForm" method="post" onsubmit="return save()">
<label>Favorite Colors</label>
<input type="text" name="fav[color][]" required value="red"/>
<input type="text" name="fav[color][]" required value="green"/>
<input type="text" name="fav[color][]" required value="blue"/>

<label>Favorite Food</label>
<input type="text" name="fav[food][]" required value="bacon"/>
<input type="text" name="fav[food][]" required value="eggs"/>

<input type="submit" value="Submit"/>
</form>

<script type="text/javascript">
function save () {
// (A) GET FORM DATA
var form = document.getElementById("myForm"),
data = new FormData(form);

// (B) TO MANUALLY APPEND MORE DATA
data.append("address[]", "Foo Street 123");
data.append("address[]", "Hello World #234");

// (C) AJAX FETCH
fetch("<?php echo $_SERVER['PHP_SELF']; ?>", { method:"POST", body:data })
.then(res=>res.text()).then((response) => {
console.log(response);
});
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
array(3) {
["fav"]=> array(2) {
["color"]=> array(3) {
[0]=> string(3) "red" [1]=> string(5) "green" [2]=> string(4) "blue"
}
["food"]=> array(2) {
[0]=> string(5) "bacon" [1]=> string(4) "eggs"
}
}
["address"]=> array(2) {
[0]=> string(14) "Foo Street 123"
[1]=> string(16) "Hello World #234"
}
}

如果需要用json,那ajax代码改为

1
2
3
4
5
6
7
8
9
10
11
<script>
// (A) ARRAY TO STRING
var colors = ["Red", "Green", "Blue"];
colors = JSON.stringify(colors);
console.log(colors); // ["Red","Green","Blue"]

// (B) SEND AS FLAT STRING
var data = new FormData(form);
data.append("colors", colors);
fetch("SCRIPT", { method:"POST", body:data });
</script>

后端处理代码为:var_dump(json_decode($_POST["colors"]));

json_encode ‘/‘不转义,中文不转码

一般用json_encode($data,256)或json_encode($data,true) 来保证数据中的中文等特殊字符不被转码

如果数据中含有URL或是有转义字符(如斜杆/),这些字符将被转义,前面加上\,如:http://www.xxx.com/xxxx 将会被转义成http:\/\/www.xxx.com\/xxxx 。这种情况下,若接口方未对数据进行json_decode的话,这种URL就是不合法的,你直接在浏览器访问也会访问不到。

这就需要把数据转json时,既不被转义,中文也不转码,防范如下(JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE不要加引号):

json_encode($data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); // 斜杆(/)不转义,中文不转码

linux笔记

挂载USB/TF/SD卡是只读文件系统

fdisk -l 查看U盘格式,如果是ntfs就安装 ntfs-3g 再试

mount挂载ntf

1
mount -t nfs -o nolock -o tcp ${myip}:/home/xxx /mnt

解压zip出现乱码

方法1 使用unzip指定字符集

1
unzip -O CP936 xxx.zip #(用GBK, GB18030也可以)

方法2 修改环境变量参数

/etc/environment添加下面2行

1
2
UNZIP="-O CP936"
ZIPINFO="-O CP936"

方法3 使用jar解压zip包

1
jar xvf xxx.zip

ssh笔记

命令行传密码

1
sshpass -p ${PWD} ssh -o stricthostkeychecking=no root@192.168.1.1

报错处理

Unable to negotiate with 192.168.1.254 port 22: no matching host key type found. Their offer: ssh-rsa

原因为:没有找到相关的主机密钥类型(ssh-rsa)。

解决: ssh -o HostKeyAlgorithms=+ssh-rsa user@host 或者

1
2
3
$ cat .ssh/config
Host *
HostKeyAlgorithms +ssh-rsa

同理:

...Their offer: diffie-hellman-group1-sha1 解决: -o KexAlgorithms=+diffie-hellman-group1-sha1

...Their offer: ssh-dss 解决:-o HostKeyAlgorithms=+ssh-dss

...Their offer: 3des-cbc 解决:-o Ciphers=+3des-cbc

1
ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-dss -o Ciphers=+3des-cbc user@host

linux网络4_组播和ospfOverGre

拓扑

TOPO

组播源PC1

定时发送i am the xxth message to 239.255.255.249 到组播组239.255.255.249

配置RouterB

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
# root @ routerB in / [15:15:39]
$ iptables -t nat -D POSTROUTING 1 #关闭NAT,清空nat表

# root @ routerB in / [15:15:42]
$ iptables -t nat -nvL # 查看nat表已清空
Chain PREROUTING (policy ACCEPT 8 packets, 569 bytes)
pkts bytes target prot opt in out source destination

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

# root @ routerB in / [15:51:02]
$ ip link set eth0 multicast off # 关闭eth0的组播功能,确保组播走的是gre隧道

# root @ routerB in / [15:51:17]
$ ip link set eth1 multicast off # 关闭eth1的组播功能,确保组播走的是gre隧道

# root @ routerB in / [15:51:23]
$ ip addr show eth0 # 看到 <> 内已经没了MULTICAST
2: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:e2:69:24:0b:30 brd ff:ff:ff:ff:ff:ff
inet 20.1.1.2/24 brd 20.1.1.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::2e2:69ff:fe24:b30/64 scope link
valid_lft forever preferred_lft forever

# root @ routerB in / [16:18:55]
$ ip addr show eth1 # 看到 <> 内已经没了MULTICAST
3: eth1: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:e2:69:24:0b:31 brd ff:ff:ff:ff:ff:ff
inet 30.1.1.1/24 brd 30.1.1.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::2e2:69ff:fe24:b31/64 scope link
valid_lft forever preferred_lft forever

# root @ routerB in / [16:50:09]
$ cat /etc/quagga/ospfd.conf # 配置ospf
hostname Router_B
password testpassword
enable password testpassword
router ospf
ospf router-id 20.1.1.2
network 30.1.1.0/24 area 0
network 20.1.1.0/24 area 0
debug ospf event
log file /usr/local/etc/ospfd.log

# root @ routerB in / [16:50:14]
$ systemctl restart quagga.service # 重启quagg(ospf)

# root @ routerB in / [17:00:36]
$ ip r # 查看路由表
20.1.1.0/24 dev eth0 proto kernel scope link src 20.1.1.2
30.1.1.0/24 dev eth1 proto kernel scope link src 30.1.1.1

配置gre

1
2
3
4
5
6
#routerA
remoteip=30.1.1.2
localip=20.1.1.1
greip=10.10.10.1
grepeerip=10.10.10.2
grename=gre1
1
2
3
4
5
6
#routerC
remoteip=20.1.1.1
localip=30.1.1.2
greip=10.10.10.2
grepeerip=10.10.10.1
grename=gre1
1
2
3
4
5
6
7
8
9
#routerA and routerC
lsmod|grep ip_gre || modprobe ip_gre
#ping -c1 ${remoteip} || { echo "make sure the network is ok plz"; exit 1; }
ip addr show ${grename} && { echo "${grename} is already exist"; exit 1; }

ip tunnel add ${grename} mode gre remote ${remoteip} local ${localip} ikey 1 okey 1 ttl 255;
ip addr add ${greip} dev ${grename} peer ${grepeerip};
ip link set ${grename} multicast on up; #mtu 1400; # 注意一定要multicast on
ip addr show ${grename};

查看gre隧道接口

1
2
3
4
5
6
7
8
# root @ routerA in / [16:53:20]
$ ip addr show gre1
7: gre1@NONE: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc noqueue state UNKNOWN group default qlen 1
link/gre 20.1.1.1 peer 30.1.1.2
inet 10.10.10.1 peer 10.10.10.2/32 scope global gre1
valid_lft forever preferred_lft forever
inet6 fe80::200:5efe:1401:101/64 scope link
valid_lft forever preferred_lft forever
1
2
3
4
5
6
7
8
# root @ routerC in / [16:54:38]
$ ip addr show gre1
16: gre1@NONE: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc noqueue state UNKNOWN group default qlen 1
link/gre 30.1.1.2 peer 20.1.1.1
inet 10.10.10.2 peer 10.10.10.1/32 scope global gre1
valid_lft forever preferred_lft forever
inet6 fe80::200:5efe:1e01:102/64 scope link
valid_lft forever preferred_lft forever

测试隧道连接

routerA ping -I 10.10.10.1 10.10.10.2 -c2 通路
routerC ping -I 10.10.10.2 10.10.10.1 -c2 通路

配置router A和C的ospf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# root @ routerA in / [16:56:48]
$ cat /etc/quagga/ospfd.conf
hostname Router_A
password testpassword
enable password testpassword
router ospf
ospf router-id 10.10.10.1 # 这里用gre隧道的ip
network 10.1.1.0/24 area 0
network 10.10.10.0/24 area 0 # 使用隧道的网段
debug ospf event
log file /usr/local/etc/ospfd.log

# root @ routerA in / [17:05:03]
$ systemctl restart quagga.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# root @ routerC in / [17:06:07]
$ cat /etc/quagga/ospfd.conf
hostname Router_C
password testpassword
enable password testpassword
router ospf
ospf router-id 10.10.10.2 # 这里用gre隧道的ip
network 10.2.1.0/24 area 0
network 10.10.10.0/24 area 0 # 使用隧道的网段
debug ospf event
log file /usr/local/etc/ospfd.log

# root @ routerC in / [17:06:19]
$ systemctl restart quagga.service

查看ospf生成的路由

1
2
3
4
5
6
7
# root @ routerA in / [17:05:10]
$ ip r
default via 20.1.1.2 dev eth1
10.1.1.0/24 dev eth0 proto kernel scope link src 10.1.1.1
10.2.1.0/24 via 10.10.10.2 dev gre1 proto zebra metric 20 # 这条由zebra生成的就是
10.10.10.2 dev gre1 proto kernel scope link src 10.10.10.1
20.1.1.0/24 dev eth1 proto kernel scope link src 20.1.1.1
1
2
3
4
5
6
7
8
# root @ routerC in / [17:06:24]
$ ip r
default via 30.1.1.1 dev eth1
10.1.1.0/24 via 10.10.10.1 dev gre1 proto zebra metric 20 # 这条由zebra生成的就是
10.2.1.0/24 dev eth0 proto kernel scope link src 10.2.1.1
10.10.10.1 dev gre1 proto kernel scope link src 10.10.10.2
30.1.1.0/24 dev eth1 proto kernel scope link src 30.1.1.2
192.168.255.0/24 dev eth3 proto kernel scope link src 192.168.255.1
1
2
3
4
# root @ routerB in / [17:00:38]
$ ip r # routerB确实没有获取到gre相关路由
20.1.1.0/24 dev eth0 proto kernel scope link src 20.1.1.2
30.1.1.0/24 dev eth1 proto kernel scope link src 30.1.1.1

测试ospf路由通路

PC1 ping 10.2.1.2 通路
PC2 ping 10.1.1.2 通路

配置pimd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# root @ routerA in / [17:09:14]
$ diff -rauN /etc/pimd.conf.bak /etc/pimd.conf
--- /etc/pimd.conf.bak 2021-10-18 16:43:11.313255253 +0800
+++ /etc/pimd.conf 2021-10-19 15:41:13.738745717 +0800
@@ -132,7 +132,7 @@
#group-prefix 224.0.0.0 masklen 4

# Static rendez-vous point
-#rp-address 192.168.10.1 224.0.0.0/4 # 自选举方式
+rp-address 10.10.10.1 224.0.0.0/4 # 自选举太慢,手动指定汇聚点(这里用gre接口ip,也可用routerA的eth0的ip 10.1.1.1)

# Switch to shortest-path tree after first packet, but only after 100 sec.
spt-threshold packets 0 interval 100

# root @ routerA in / [17:20:01]
$ systemctl restart pimd.service
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
# root @ routerC in / [17:10:07]
$ diff -rauN /etc/pimd.conf.bak /etc/pimd.conf
--- /etc/pimd.conf.bak 2021-09-27 14:49:18.706881067 +0800
+++ /etc/pimd.conf 2021-10-19 15:40:35.242644448 +0800
@@ -112,7 +112,9 @@

# By default, all non-loopback multicast capable interfaces are enabled.
# If you want to use loopback, set the interface multicast flag on it.
-#phyint eth0 disable
+phyint eth3 disable # 关闭不想要进行组播的端口

# IGMP default query interval and querier timeout. The latter should
# per RFC always be (robustness * interval) + (query-response / 2), for
@@ -132,7 +134,7 @@
#group-prefix 224.0.0.0 masklen 4

# Static rendez-vous point
-#rp-address 192.168.10.1 224.0.0.0/4 # 自选举方式
+rp-address 10.10.10.1 224.0.0.0/4 # 自选举太慢,手动指定汇聚点(这里用gre接口ip,也可用routerA的eth0的ip 10.1.1.1)

# Switch to shortest-path tree after first packet, but only after 100 sec.
spt-threshold packets 0 interval 100

# root @ routerC in / [17:22:37]
$ systemctl restart pimd.service

查看组播路由

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
# root @ routerA in / [17:22:13]
$ pimd -r
Virtual Interface Table ======================================================
Vif Local Address Subnet Thresh Flags Neighbors
--- --------------- ------------------ ------ --------- -----------------
0 10.1.1.1 10.1.1/24 1 DR NO-NBR
1 20.1.1.1 20.1.1/24 1 DR NO-NBR
2 10.10.10.1 10.10.10.1/32 1 PIM 10.10.10.2
3 10.1.1.1 register_vif0 1

Vif SSM Group Sources

Multicast Routing Table ======================================================
----------------------------------- (*,G) ------------------------------------
Source Group RP Address Flags
--------------- --------------- --------------- ---------------------------
INADDR_ANY 239.255.255.249 10.10.10.1 WC RP
Joined oifs: ..j.
Pruned oifs: ....
Leaves oifs: ....
Asserted oifs: ....
Outgoing oifs: ..o.
Incoming : ...I

TIMERS: Entry JP RS Assert VIFS: 0 1 2 3
200 55 0 0 0 0 200 0
----------------------------------- (S,G) ------------------------------------
Source Group RP Address Flags
--------------- --------------- --------------- ---------------------------
10.1.1.2 239.255.255.249 10.10.10.1 SPT CACHE SG
Joined oifs: ..jj
Pruned oifs: ....
Leaves oifs: ....
Asserted oifs: ....
Outgoing oifs: ..oo
Incoming : I...

TIMERS: Entry JP RS Assert VIFS: 0 1 2 3
165 20 0 0 0 0 140 0
Source Group RP Address Flags
--------------- --------------- --------------- ---------------------------
10.10.10.1 239.255.255.249 10.10.10.1 RP SG
Joined oifs: ....
Pruned oifs: ..p.
Leaves oifs: ....
Asserted oifs: ....
Outgoing oifs: ....
Incoming : ...I

TIMERS: Entry JP RS Assert VIFS: 0 1 2 3
205 55 0 0 0 0 0 0
--------------------------------- (*,*,G) ------------------------------------
Number of Groups: 1
Number of Cache MIRRORs: 1
------------------------------------------------------------------------------


# root @ routerA in / [17:24:23]
$ ip mroute show # 看到PC1发的组播239.255.255.249数据从eth0进来,从gre1出去
(10.1.1.2, 239.255.255.249) Iif: eth0 Oifs: gre1 pimreg
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
# root @ routerC in / [17:26:09]
$ pimd -r
Virtual Interface Table ======================================================
Vif Local Address Subnet Thresh Flags Neighbors
--- --------------- ------------------ ------ --------- -----------------
0 10.2.1.1 10.2.1/24 1 DR NO-NBR
1 30.1.1.2 30.1.1/24 1 DR NO-NBR
2 192.168.255.1 192.168.255 1 DISABLED
3 10.10.10.2 10.10.10.2/32 1 DR PIM 10.10.10.1
4 10.2.1.1 register_vif0 1

Vif SSM Group Sources

Multicast Routing Table ======================================================
----------------------------------- (*,G) ------------------------------------
Source Group RP Address Flags
--------------- --------------- --------------- ---------------------------
INADDR_ANY 239.255.255.249 10.10.10.1 WC RP
Joined oifs: .....
Pruned oifs: .....
Leaves oifs: l....
Asserted oifs: .....
Outgoing oifs: o....
Incoming : ...I.

TIMERS: Entry JP RS Assert VIFS: 0 1 2 3 4
0 55 0 0 0 0 0 0 0
----------------------------------- (S,G) ------------------------------------
Source Group RP Address Flags
--------------- --------------- --------------- ---------------------------
10.10.10.1 239.255.255.249 10.10.10.1 SPT CACHE SG
Joined oifs: ....j
Pruned oifs: ....p
Leaves oifs: l....
Asserted oifs: .....
Outgoing oifs: o....
Incoming : ...I.

TIMERS: Entry JP RS Assert VIFS: 0 1 2 3 4
195 55 1 0 0 0 0 0 0
--------------------------------- (*,*,G) ------------------------------------
Number of Groups: 1
Number of Cache MIRRORs: 1
------------------------------------------------------------------------------


# root @ routerC in / [17:26:10]
$ ip mroute show # 看到组播239.255.255.249数据从gre1进来,从eth0出去发给PC2
(10.10.10.1, 239.255.255.249) Iif: gre1 Oifs: eth0 pimreg

接收端PC2

1
2
3
4
5
6
7
8
# zh @ li in ~ [17:35:30]
$ ./rcver 239.255.255.249 enp2s0
---MESSAGE FROM:10.2.1.1:44853---
[i am the 32822th message to 239.255.255.249]

---MESSAGE FROM:10.2.1.1:44853---
[i am the 32821th message to 239.255.255.249]
...