Graphviz 是一個自動排版的做圖軟件,能夠生成 png pdf 等格式。html
一切以官方文檔爲準,博客只是參考。這裏作一個本身學習的記錄。node
Graphviz 構建組件爲 圖,節點,邊,用屬性對其進行描述。linux
如下是定義DOT語言的抽象語法,約束的規則以下:git
聲明 | 結構 |
---|---|
graph | [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}' |
stmt_list | [ stmt [ ';' ] stmt_list ] |
stmt | node_stmt | edge_stmt | attr_stmt | ID '=' ID | subgraph |
attr_stmt | (graph | node | edge) attr_list |
attr_list | '['** [ a_list ] **']' [ attr_list ] |
a_list | ID '=' ID [ (';' | ',') ] [ a_list ] |
edge_stmt | (node_id | subgraph) edgeRHS [ attr_list ] |
edgeRHS | edgeop (node_id | subgraph) [ edgeRHS ] |
node_stmt | node_id [ attr_list ] |
node_id | ID [ port ] |
port | ':' ID [ ':' compass_pt ] | ':' compass_pt |
subgraph | [ subgraph [ ID ] ] '{' stmt_list '}' |
compass_pt | (n | ne | e | se | s | sw | w | nw | c | _) |
ID 其實就是一個字符串,爲該組件的名稱或者屬性的名稱,命名規則以下:json
[a-zA-Z\200-\377]
下劃線,數字 [0-9]
,數字不能出如今起始位置"..."
<>
dot 語法的關鍵字數據結構
經過 dot 的抽象語法能夠看到tcp
示例 節點的用法 node0 [label = "<postid1> string|<postid2> string|<postid3> string3", height=.5]` node0:head[color=lightblue] // 設置該部分的顏色
方向 | 說明 |
---|---|
n | north 北 |
ne | north east |
e | east 東 |
se | south east 東南 |
s | south 南 |
sw | south west 西南 |
w | west 西 |
nw | north west 西北 |
c | center 中部 |
_ | 任意 |
一個方向的示例gitlab
digraph action { node [shape = record,height=.1]; node0 [label = "<head> head|<body> body|<foot> foot", height=.5] node2 [shape = box label="mind"] node0:head:n -> node2:n [label = "n"] node0:head:ne -> node2:ne [label = "ne"] node0:head:e -> node2:e [label = "e"] node0:head:se -> node2:se [label = "se"] node0:head:s -> node2:s [label = "s"] node0:head:sw -> node2:sw [label = "sw"] node0:head:w -> node2:w [label = "w"] node0:head:nw -> node2:nw [label = "nw"] node0:head:c -> node2:c [label = "c"] node0:head:_ -> node2:_ [label = "_"] node0:body[style=filled color=lightblue] }
效果以下 圖-1
佈局
一個圖中有很是多的 node 和 edge,若是每次都須要聲明一個節點的屬性會很是麻煩,有一個簡單的方式爲聲明一個公共的屬性如post
digraph action { rankdir = LR // 設置方向 node [shape=box color=blue] edge [color=red] node1 // 默認節點屬性 node2 [color=lightblue] // 屬於該節點的顏色屬性 node1 -> node2 // 默認邊屬性 node2 -> node1 [color=green] // 屬於該變的屬性 }
在聲明位置以後的節點都有一個 默認 的形狀和顏色屬性。
所有的屬性見graphviz官網,這裏列舉部分經常使用的屬性
節點的默認屬性爲 shape = ellipse, width = .75, height = 0.5 而且用節點標識符做爲節點的顯示文字。
如圖一中所示,聲明兩個節點 node0 和 node2,node0 或 node2 就表示這個節點的節點標識符,後面緊跟的是該節點的屬性列表;另外一種用法爲 節點標識符:節點部分:方向[屬性列表] node0:body[style=filled color=lightblue]
, 這個爲單一節點聲明的方式。
節點中最基本的屬性爲:
有向圖中的的邊用 ->
表示,無向圖用 --
表示。
能夠同時鏈接多個節點或者子圖,可是隻能有一個屬性列表,以下
digraph { rankdir = LR A -> B -> c[color=green] }
一些關於邊的屬性以下:
digraph { rankdir = LR splines = ortho A -> B -> C -> D -> F [color = green] E -> F -> B -> D [color = blue] B -> E -> H[color = red] }
taillabel 邊上靠近尾部部分的標籤
設置 A->B->C->D->F的權重最大,修改綠色的分支的權重爲 100,使其變成主要邏輯分支。
dir 設置繪製箭頭的邊緣類型
subgraph 必須配合 cluster 一塊兒使用,用法爲 subgraph cluster* {}
須要設置 compound 爲 true,則在羣集之間留出邊緣,子圖的邊界關係在 邊 的定義中有給出,這裏直接給個示例。
digraph G { compound = true // 容許子圖間存在邊 ranksep = 1 node [shape = record] subgraph cluster_hardware { label = "hardware" color = lightblue CPU Memory } subgraph cluster_kernel { label = "kernel" color = green Init IPC } subgraph cluster_libc { label = "libc" color = yellow glibc } CPU -> Init [lhead = cluster_kernel ltail = cluster_hardware] IPC -> glibc [lhead = cluster_libc ltail = cluster_kernel] }
展現了兩個版本,怎麼把這些圖形節點稍微規範的顯示出來
digraph { compound=true fontsize=10 margin="0,0" ranksep = .75 nodesep = .65 node [shape=Mrecord fontname="Inconsolata, Consolas", fontsize=12, penwidth=0.5] edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal] "TCP/IP State Transition" [shape = "plaintext", fontsize = 16] // now start server state transition "CLOSED" -> "LISTEN" [style = blod, label = "應用:被動打開\n發送:<無>"]; "LISTEN" -> "SENT_REVD" [style = blod, label = "接收:SYN\n發送:SYN,ACK"] "SENT_REVD" -> "ESTABLISHED" [style = blod, label = "接收:ACK\n發送:<無>", weight = 20] "ESTABLISHED" -> "CLOSE_WAIT" [style = blod, label = "接收:FIN\n發送:ACK", weight = 20] subgraph cluster_passive_close { style = dotted margin = 10 passive_close [shape = plaintext, label = "被動關閉", fontsize = 14] "CLOSE_WAIT" -> "LAST_ACK" [style = blod, label = "應用:關閉\n發送:FIN", weight = 10] } "LAST_ACK" -> "CLOSED" [style = blod, label = "接收:ACK\n發送:<無>"] // now start client state transition "CLOSED" -> "SYN_SENT" [style = dashed, label = "應用:主動打開\n發送:SYN"]; "SYN_SENT" -> "ESTABLISHED" [style = dashed, label = "接收:SYN,ACK\n發送:ACK", weight = 25] "SYN_SENT" -> "SENT_REVD" [style = dotted, label = "接收:SYN\n發送:SYN,ACK\n同時打開"] "ESTABLISHED" -> "FIN_WAIT_1" [style = dashed, label = "應用:關閉\n發送:FIN", weight = 20] subgraph cluster_active_close { style = dotted margin = 10 active_open [shape = plaintext, label = "主動關閉", fontsize = 14] "FIN_WAIT_1" -> "FIN_WAIT_2" [style = dashed, label = "接收:ACK\n發送:<無>"] "FIN_WAIT_2" -> "TIME_WAIT" [style = dashed, label = "接收:FIN\n發送:ACK"] "FIN_WAIT_1" -> "CLOSING" [style = dotted, label = "接收:ACK\n發送:<無>"] "FIN_WAIT_1" -> "TIME_WAIT" [style = dotted, label = "接收:SYN,ACK\n發送:ACK"] "CLOSING" -> "TIME_WAIT" [style = dotted] } "TIME_WAIT" -> "CLOSED" [style = dashed, label = "2MSL超時"] }
這是一個很挫的版本,排版亂飛了。
digraph rankdot { compound=true margin="0,0" ranksep = .75 nodesep = 1 pad = .5 //splines = ortho node [shape=Mrecord, charset = "UTF-8" fontname="Microsoft YaHei", fontsize=14] edge [charset = "UTF-8" fontname="Microsoft YaHei", fontsize=11, arrowhead = normal] CLOSED -> LISTEN [style = dashed, label = "應用:被動打開\n發送:<無>", weight = 100]; "TCP/IP State Transition" [shape = "plaintext", fontsize = 16] { rank = same SYN_RCVD SYN_SENT point_1 [shape = point, width = 0] SYN_SENT -> point_1 [style = dotted, label = "應用關閉或者超時"] // SYN_SENT -> SYN_RCVD 這個一行代碼和上一行衝突了,syn_sent 會在syn_rcvd右邊 SYN_RCVD -> SYN_SENT [style = dotted, dir = back, headlabel = "接收:SYN\n發送:SYN,ACK\n同時打開"] } LISTEN -> SYN_RCVD [style = dashed, headlabel = "接收:SYN\n發送:SYN,ACK"] SYN_RCVD -> LISTEN [style = dotted, headlabel = "接收:RST"] CLOSED:es -> SYN_SENT [style = blod, label = "應用:主動打開\n發送:SYN"] { rank = same ESTABLISHED CLOSE_WAIT ESTABLISHED -> CLOSE_WAIT [style = dashed, label = "接收:SYN,ACK\n發送:ACK"] } SYN_RCVD -> ESTABLISHED [style = dashed, label = "接收:ACK\n發送:<無>", weight = 9] SYN_SENT -> ESTABLISHED [style = blod, label = "接收:SYN,ACK\n發送:ACK", weight = 10] { rank = same FIN_WAIT_1 CLOSING LAST_ACK point_2 [shape = point, width = 0] FIN_WAIT_1 -> CLOSING [style = dotted, label = "接收:FIN\n發送:ACK"] LAST_ACK -> point_2 [style = dashed, label = "接收:ACK\n發送:<無>"] } CLOSE_WAIT -> LAST_ACK [style = dashed, label = "應用:關閉\n發送:FIN", weight = 10] { rank = same FIN_WAIT_2 TIME_WAIT point_3 [shape = point, width = 0] TIME_WAIT -> point_3 [style = blod, label = "2MSL超時"] } ESTABLISHED -> FIN_WAIT_1 [style = blod, label = "應用:關閉\n發送:FIN"] FIN_WAIT_1 -> FIN_WAIT_2 [style = blod, headlabel = "接收:ACK\n發送:<無>", weight = 15] FIN_WAIT_2 -> TIME_WAIT [style = blod, label = "接收:FIN\n發送:ACK", weight = 10] CLOSING -> TIME_WAIT [style = dotted, label = "接收:ACK\n發送:<無>", weight = 15] FIN_WAIT_1 -> TIME_WAIT [style = dotted, label = "接收:ACK\n發送:<無>"] point_3 -> point_2 [arrowhead = none, style = dotted, weight = 10] point_2 -> point_1 [arrowhead = none, style = dotted] point_1 -> CLOSED [style = dotted] }
這個版本看起來有內味了,最最最的主要的緣由就是我使用 rank = same 屬性,將一些圖形固定在 同一列,一些須要橫豎的直線的地方使用 weight 來調整權重,達到橫豎的直接的效果,不少地方都是微調的結果。有一個不好的地方是 使用了rank限制若干圖形後,就不能使用 subgraph 屬性了,這樣就不能在若干不一樣部分的節點周邊畫線(對比關閉的區域)了。
digraph rankdot { compound=true margin="0,0" ranksep = .75 nodesep = 1 pad = .5 rankdir = LR node [shape=record, charset = "UTF-8" fontname="Microsoft YaHei", fontsize=14] edge [style = dashed, charset = "UTF-8" fontname="Microsoft YaHei", fontsize=11] epoll [shape = plaintext, label = "epoll 相關結構及部分關係"] eventpoll [ color = cornflowerblue, label = "<eventpoll> struct \n eventpoll | <lock> spinlock_t lock; | <mutex> struct mutex mtx; | <wq> wait_queue_head_t wq; | <poll_wait> wait_queue_head_t poll_wait; | <rdllist> struct list_head rdllist; | <ovflist> struct epitem *ovflist; | <rbr> struct rb_root_cached rbr; | <ws> struct wakeup_source *ws; | <user> struct user_struct *user; | <file> struct file *file; | <visited> int visited; | <visited_list_link> struct list_head visited_list_link;" ] epitem [ color = sienna, label = "<epitem> struct \n epitem | <rb>struct rb_node rbn;\nstruct rcu_head rcu; | <rdllink> struct list_head rdllink; | <next> struct epitem *next; | <ffd> struct epoll_filefd ffd; | <nwait> int nwait; | <pwqlist> struct list_head pwqlist; | <ep> struct eventpoll *ep; | <fllink> struct list_head fllink; | <ws> struct wakeup_source __rcu *ws; | <event> struct epoll_event event;" ] epitem2 [ color = sienna, label = "<epitem> struct \n epitem | <rb>struct rb_node rbn;\nstruct rcu_head rcu; | <rdllink> struct list_head rdllink; | <next> struct epitem *next; | <ep> struct eventpoll *ep; | ··· | ··· " ] eppoll_entry [ color = darkviolet, label = "<entry> struct \n eppoll_entry | <llink> struct list_head llink; | <base> struct epitem *base; | <wait> wait_queue_entry_t wait; | <whead> wait_queue_head_t *whead;" ] epitem:ep -> eventpoll:se [color = sienna] epitem2:ep -> eventpoll:se [color = sienna] eventpoll:ovflist -> epitem:next -> epitem2:next [color = cornflowerblue] eventpoll:rdllist -> epitem:rdllink -> epitem2:rdllink [dir = both] eppoll_entry:llink -> epitem:pwqlist [color = darkviolet] eppoll_entry:base -> epitem:nw [color = darkviolet] }
Graphviz Preview
"graphvizPreview.dotPath": "graphviz_path\graphviz-2.38\\release\\bin\\dot.exe"
, graphviz_path 爲所在路徑,這些修改一下既可