.NET 5.0預覽版6發布:支持Windows ARM64設備

2020年6月25日,微軟dotnet團隊在博客宣布了第六個 .NET 5.0 的預覽版:https://devblogs.microsoft.com/dotnet/announcing-net-5-0-preview-6/,在改進性能的同時增加了一些新的功能。ASP.NET Core和 EF Core也將於今日發布了。注意:EF Core 5.0 不支持 .NET Standard 2.0 或 .NET Framework,但是EF Core 5.0是支持.NET Core 3.1。 將所有內容組合在一起的里程碑式版本離預覽版 8 中完成功能只有兩步之遙,最終的 GA 版本定於 11 月發布,2019年Build大會上宣布.NET 5時,微軟就明確說了,”未來將只有一個.NET,您將能夠使用它來定位Windows、Linux、macOS、iOS、Android、tvOS、watchOS和WebAssembly等等。 微軟在4 月份宣布預覽版 2時宣布,它已經處理了其 .NET 站點上 50% 的流量。

 

自那時以來,一系列穩定的預覽版本一直在逐步修復Issue,完善現有功能和添加新的功能,雖然通常是一個小範圍,不斷敏捷迭代,小步快跑。 今天的預覽版 6 依然如此,Microsoft 將其描述為包含”一小組新功能和性能改進”。

本次更新的主要功能是在 Windows ARM64 上支持 Windows Forms 應用程序。此前.NET 5 Preview 4,Windows ARM64 上只支持控制台和 ASP.NET Core 應用程序。 通過這項支持,開發者可以在 Surface Pro X 等 Windows ARM64 設備上構建和運行 Windows Forms 應用。微軟還透露,他們仍在努力為 Windows ARM64 設備提供 WPF支持。 同時.NET 5移除了對WinRT 的內置支持,通過外部工具鏈進行支持,這麼做的最直接的好處是簡化 .NET 運行時代碼庫(一下就刪除 6 萬行代碼),而且這麼多代碼和跨平台無關。這次版本更新的內容如下:

  • Windows 窗體改進:開發人員可以強制其應用程序是單實例的,這意味着一次啟動一個實例。
  • RyuJIT 代碼質量改進:其中大量涉及常規改進(結構處理等)、ARM64 硬件內部功能和 ARM64 生成的代碼改進,從而大大減少了 ARM64 代碼大小。
  • 單個文件應用改進:添加了在單個文件中包含本機二進制文件和任何其他內容(如圖片)的新選項,.NET 5目標是為 Windows、macOS 和 Linux 啟用將應用作為一個文件發布。這個裡程碑進一步接近了。
  • 本機託管應用程序改進:一位貢獻者提供了一種新的模型,用於在本機應用程序中的 .NET 託管模型。
  • 突破性的改變 — 刪除內置 WinRT 支持: “已經用 Windows 團隊在 .NET 5.0 中提供的C#/WinRT工具鏈取代了內置 WinRT 支持。WinRT 互操作中的此更改是一個突破性的變化,使用 WinRT 的 .NET Core 3.x 應用將需要重新編譯。我們將在即將到來的預覽版中提供有關此內容的更多信息。

開發工具支持上需要Visual Studio 16.7 預覽版才支持.NET 5, Visual Studio For Mac 也支持.NET 5, 當然Visual Studio Code 安裝最新版的C#擴展插件也支持.NET 5. Visual Studio .NET遠程調試器對Windows ARM64的支持將在Visual Studio 16.7版本中提供。Visual Studio Code .NET遠程調試器支持將在以後推出。

Richard 在博客結束時說:”我們現在已經過了這個發布周期的一半。”事實上,我們開始’關閉發布’。如果您密切關注我們的代碼倉庫,您會看到我們正開始更仔細地管理問題的里程碑。 在發布了多個.NET 5 版本之後,我可以告訴你,這是一個偉大的時間。 是時候在一組我們構建的功能上取得勝利,並把它們打磨到您樂於使用它們。 這就是我們現在在家裡遠程工作所做的。 儘管 11 月首次亮相仍有望實現,但 .NET 5 不會擁有 Microsoft 最初計劃的所有特性和功能。這是因為COVID-19大流行引起的併發症。因此,完整的包將會在 2021 年 11 月與 .NET 6 一起發布。

您可以使用以下鏈接下載新的 .NET 5.0 的第六個預覽版,適用于于Windows、macOS和Linux: 

  • Windows and macOS installers
  • Binaries
  • Docker images
  • Snap installer

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

動態追蹤技術之SystemTap

SystemTap

從春哥(章亦春)那看到的關於SystemTap的介紹,讓我對動態追蹤這個功能頗為感興趣,覺得這是一個非常有用的功能。

SystemTap對內核及用戶態程序提供了動態追蹤功能,用戶可以自定探測事件來跟蹤程序的運行情況,如函數的調用路徑、CPU佔用和磁盤IO等一系列可以探測的情況。有了systemtap,可以在程序不修改代碼,甚至不用重啟就能分析出程序的運行情況。

配合火焰圖的可視化,對程序的性能分析極其有利。(放一個FlameGraph的官方圖片)

原理

SystemTap 基本思想是命名事件,併為它們提供處理程序。每當發生指定的事件時,內核都會將處理程序視為子例程運行,然後繼續運行。有一系列的事件,例如進入或退出函數,計時器到期或整個SystemTap會話的開始和停止。處理程序是一系列腳本語言語句,用於指定事件發生時要完成的工作。這項工作通常包含從事件上下文中提取數據,將其存儲到內部變量或打印結果。

SystemTap 的工作原理是將腳本翻譯成C語言,執行C編譯器創建一個內核模塊。當模塊被加載后,通過掛載到內核來激活所有的探測事件。然後,當事件發生再任何處理器上時,編譯后的處理程序就運行,最終,SystemTap會話停止,Hook取消,內核模塊被移除,整個過程由命令行程序stap驅動。

原理圖如下:

安裝

需要內核級別的支持,依賴了三個rpm下載鏈接,

  1. kernel-debuginfo-common
  2. kernel-debuginfo
  3. kernel-devel

這三個包的版本必須匹配當前內核的版本,比如我自己的內核版本是 3.10.0-327, 那麼以上三個包版本都必須保持一致。

安裝 SystemTap:

$ yum install systemtap

測試是否成功安裝:

$ stap -ve ‘probe begin{printf(“Hello, World\n”); exit();}’

正常的話會輸出 Hello, World,但是不出意外會出現版本不一致的情況:

ERROR: module version mismatch (#1 SMP Fri Nov 20 11:12:42 CST 2015 vs #1 SMP Thu Nov 19 22:10:57 UTC 2015), release 3.10.0-327.el7.x86_64

出現這個的情況是版本相同但是打包事件不相同的情況,修改這個時間和uname -a 中的時間保持一致。

$ rpm -ql kernel-devel | xargs grep UTS_VERSION 2>/dev/null

/usr/src/kernels/3.10.0-327.el7.x86_64/include/generated/compile.h:#define UTS_VERSION "#1 SMP Fri Nov 20 11:12:42 CST 2015"

再次運行那個hello測試,出現緩存的錯誤,刪除緩存文件

  1. /root/.systemtap/cache/34/stap_34443d4ad1fe1d37c0352b7b8c691aee_975.c
  2. /root/.systemtap/cache/34/stap_34443d4ad1fe1d37c0352b7b8c691aee_975.ko

追蹤

最簡單的探測類型就是跟蹤事件。Systemtap支持許多內置事件,所有的事件家族見 tapset

可以探測的的常用事件:

  • begin, systemtap 會話開始
  • end, systemtap 會話結束
  • kernel.function(“sys_xxx”), 系統調用xx的入口
  • kernel.function(“sys_xxx”).return, 系統調用xx的返回
  • timer.ms(300), 每300毫秒的定時器
  • timer.profile, 每個CPU上周期觸發的定時器
  • process(“a.out”).function(“foo*”), a.out 中函數名前綴為foo的函數信息
  • process(“a.out”).statement(“*@main.c:200”), a.out中文件main.c 200行處的狀態

常用的可打印值(具體見 tapset):

  • tid(), 當前線程id
  • pid(), 當前進程id
  • uid(), 當前用戶id
  • execname(), 當前進程名稱
  • cpu(), 當前cpu編號
  • gettimeofday_s(), 秒時間戳
  • get_cycles(), 硬件周期計數器快照
  • pp(), 探測點事件名稱
  • ppfunc(), 探測點觸發的函數名稱
  • $$var, 上下文中存在 $var,可以使用該變量
  • print_backtrace(), 打印內核棧
  • print_ubacktrace(), 打印用戶空間棧

SystemTap 腳本

stap 腳本簡單,語法類似C;

  • 註釋
# fuck
// fuck
/* fuck */
  • 函數
function foo() {
    // exit(); // 退出 systemtap 會話
}
  • 基本的 if/else/while/for 控制結構
function if_expr() {
    i = 0
    if (i == 1)
        printf("[if] i = %d\n", i);
    else
        printf("[else] i = %d\n", i);
}

function while_expr() {
    i = 0;
    while (i != 2)
        printf("[while] i = %d\n", i++);
}

function for_expr() {
    for (i = 0; i < 2; i++)
        printf("[for] i = %d\n", i);
}

  • 字符串比較,拼接,轉換
function str() {
    uid = uid();
    s_uid = sprint(uid);
    f_uid = "fuck" . s_uid
    printf("uid: %d-%s-%s\n", uid, s_uid, f_uid); // uid: 0-0-fuck0

    // exit();
}
  • 元組
global t; // 聲明元組
global tpl[400]; // 聲明一個400容量的元組

t["fuck"]++;  // t["fuck"] 初始值默認為0, ++ 變成 1
t["fuck"] = 4396; // 賦值為4396

tpl["fuck", pid()]++; // 兩個元素
tpl["shit", tid()]++;
  • 聚集統計
// 包含4個維度 @count @avg @min @max
global t;

t["fuck", tid()] <<< 1
t["fuck", pid()] <<< 1
t[execname(), tid()] <<< 1
t["fuck", 5487] <<< 2
t["fuck", 5487] <<< 3
t["fuck", 5487] <<< 1

具體結構如下:
t["fuck",5487] @count=3 @min=1 @max=3 @sum=6 @avg=2
t["fuck",26060] @count=2 @min=1 @max=1 @sum=2 @avg=1
t["stapio",26060] @count=1 @min=1 @max=1 @sum=1 @avg=1


// 遍歷(升序), 限制5次循環
foreach([key, value] in t+ limit 5)
    printf("%s: %d\n", key, value)

// 結果
stapio: 2571
fuck: 2571
fuck: 5487

應用

stap 常用命令

Usage: stap [options] FILE                    Run script in file.
   or: stap [options] -e SCRIPT               Run given script.
   or: stap [options] -l PROBE                List matching probes.
   or: stap [options] -L PROBE                List matching probes and local variables.
[options]
   -T TIME    terminate the script after TIME seconds

除了直接執行腳本文件外,另外一個比較有用的功能 -L -l 現象,列出可探測點及局部變量

  • 列出程序中的可探測點
// 截取部分~
[root@localhost stp]# stap -l 'process("/tmp/limlog/build/tests/LogTest").function("*")'
process("/tmp/limlog/build/tests/LogTest").function("write@/tmp/limlog/limlog/Log.cpp:107")
process("/tmp/limlog/build/tests/LogTest").function("~LimLog@/tmp/limlog/limlog/Log.cpp:213")
process("/tmp/limlog/build/tests/LogTest").function("~LogLine@/tmp/limlog/limlog/Log.cpp:341")
process("/tmp/limlog/build/tests/LogTest").function("~LogSink@/tmp/limlog/limlog/Log.cpp:59")
process("/tmp/limlog/build/tests/LogTest").function("~_Impl@/usr/include/c++/4.8.2/thread:107")
process("/tmp/limlog/build/tests/LogTest").function("~_Impl_base@/usr/include/c++/4.8.2/thread:97")
  • 列出程序中的可探測點及局部變量(前綴為$)
[root@localhost stp]# stap -L 'process("/tmp/limlog/build/tests/LogTest").function("*")'
process("/tmp/limlog/build/tests/LogTest").function("id@/usr/include/c++/4.8.2/thread:73") $this:class id* const
process("/tmp/limlog/build/tests/LogTest").function("incConsumable@/tmp/limlog/limlog/Log.cpp:313") $this:class LimLog* const $n:uint32_t
process("/tmp/limlog/build/tests/LogTest").function("incConsumablePos@/tmp/limlog/limlog/Log.cpp:135") $this:class BlockingBuffer* const $n:uint32_t
process("/tmp/limlog/build/tests/LogTest").function("incConsumablePos@/tmp/limlog/limlog/Log.cpp:460") $n:uint32_t
process("/tmp/limlog/build/tests/LogTest").function("insert@/usr/include/c++/4.8.2/bits/basic_string.h:1319") $__c:char $__n:size_type $__pos:size_type $this:class basic_string<char, std::char_traits<char>, std::allocator<char> >* const

有這個功能,我們就可以看到函數所在源文件中的位置及可以根據的局部變量。

systemtap 腳本

探針事件的關鍵字是 probe, 由 probe 引出需要跟蹤的事件

// 腳本執行后显示 === begin ===
probe begin {
    printf("=== begin ===\n");
}

根據上述的可跟蹤的事件

  • begin/end, 分別是systemtap會話的起始和結尾
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

probe begin {
    printf("=== begin ===\n");
}

probe end {
    printf("=== end ===\n");
}

// 執行腳本
[root@localhost stp]# stap foo.stp -T 1
=== begin ===
=== end ===
  • kernel.function(“sys_xxx”), 系統調用
// 調用open系統調用
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

probe kernel.function("sys_open").call {
    printf("%s call %s\n", execname(), ppfunc());
}

// open系統調用返回
probe kernel.function("sys_open").call {
    printf("%s call %s over\n", execname(), ppfunc());
}

[root@localhost stp]# stap foo.stp -T 1
sh call SyS_open
sh call SyS_open over
sh call SyS_open
sh call SyS_open over
sh call SyS_open
sh call SyS_open over
  • 定時器調用
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

// 定時調用函數
probe timer.ms(500) {
    printf("now: %d\n", gettimeofday_s());
}

[root@localhost stp]# stap foo.stp -T 3
now: 1593141081
now: 1593141081
now: 1593141082
now: 1593141082
now: 1593141083
  • 定時cpu採樣
// 取自春哥的sample-bt示例代碼,定時調用棧取樣
global bts;

probe timer.profile {
    if (pid() == 5291)
        bts[backtrace(), ubacktrace()] <<< 1
}

probe timer.s(10) {
    foreach([k, u] in bts-) {
        print_stack(k);
        print_ustack(u);
        printf("\\t%d\\n", @count(bts[k, u]));
    }
    exit();
}

// 結果為16進制地址,截取一部分數據,這部分數據需要進一步加工統計
[root@localhost stp]# stap foo.stp
 0xffffffff810d6244 : 0xffffffff810d6244
 0xffffffff810475fa : 0xffffffff810475fa
\t1\n 0xffffffffa0139f0d : 0xffffffffa0139f0d [xfs]
  • 指定程序中的函數事件(需要運行程序)
// 打印程序 LogTest 所有執行的函數
[root@localhost stp]# cat foo.stp
probe process("/tmp/limlog/build/tests/LogTest").function("*") {
    printf("function: %s\n", ppfunc());
}

// 庫函數也加載進來了
[root@localhost stp]# stap foo.stp
function: offsetOfPos
function: __distance<char*>
function: operator+<long int, std::ratio<1l, 1000000000l>, long int, std::ratio<1l, 1000000l> >
function: operator<<
  • 指定程序指定文件中的可追蹤事件
//打印 LogTest 程序屬於 Log.cpp 中的函數信息
[root@localhost stp]# cat foo.stp
probe process("/tmp/limlog/build/tests/LogTest").statement("*@Log.cpp") {
    printf("function: %s\n", ppfunc());
}

// 過濾了庫函數,只留下Log.cpp中的函數執行
[root@localhost stp]# stap foo.stp
function: singleton
function: produce
function: produce
function: produce
function: produce
function: used
function: used
function: consumable
function: operator<<
function: ~LogLine
  • 上下文變量
// 獲取上下文變量
[root@localhost stp]# stap -L 'process("/tmp/limlog/build/tests/LogTest").statement("*@Log.cpp")'
process("/tmp/limlog/build/tests/LogTest").statement("LimLog@/tmp/limlog/limlog/Log.cpp:185") $this:class LimLog* const
process("/tmp/limlog/build/tests/LogTest").statement("LogLine@/tmp/limlog/limlog/Log.cpp:336") $this:class LogLine* const $level:enum LogLevel $loc:struct LogLoc const&
process("/tmp/limlog/build/tests/LogTest").statement("append@/tmp/limlog/limlog/Log.cpp:346") $this:class LogLine* const $data:char const* $n:size_t
process("/tmp/limlog/build/tests/LogTest").statement("consumable@/tmp/limlog/limlog/Log.cpp:140") $this:class BlockingBuffer const* const
process("/tmp/limlog/build/tests/LogTest").statement("consume@/tmp/limlog/limlog/Log.cpp:146") $this:class BlockingBuffer* const $to:char* $n:uint32_t

// 這裏我們追蹤 consume 函數中的兩個參數
[root@localhost stp]# cat foo.stp
probe process("/tmp/limlog/build/tests/LogTest").function("consume") {
    printf("func = %s, $var(n) = %d, $var(to) = %p\n", ppfunc(), $n, $to);
}

[root@localhost stp]# stap foo.stp
func = consume, $var(n) = 406, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 203, $var(to) = 0x7f902a94d1a6
func = consume, $var(n) = 790, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 3319, $var(to) = 0x7f902a94d326
func = consume, $var(n) = 4235, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 4235, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 2326, $var(to) = 0x7f902a94d010
func = consume, $var(n) = 8470, $var(to) = 0x7f902a94d010

示例分析

在探測點被執行時打印這種簡單的用法外,還能進一步做一些其它的事情。

  1. 統計某一時間段內運行次數top k的函數
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

global top_funcs;

probe begin {
    printf("=== begin ===\n");
}

probe process("/tmp/limlog/build/tests/LogTest").statement("*@Log.cpp") {
    top_funcs[ppfunc()]++;
}

probe end {
    foreach(func in top_funcs- limit 10)
        printf("%s: %d\n", func, top_funcs[func]);

    printf("=== end ===\n");
}

// LogTest 在3秒內執行最多的10個函數及次數
[root@localhost stp]# stap foo.stp -T 3
=== begin ===
produce: 1152
append: 720
singleton: 646
operator<<: 608
produceLog: 576
used: 576
consumable: 359
incConsumablePos: 64
getLogLevel: 32
LogLine: 32
=== end ===
  1. 統計系統調用某一時間段內被調用最頻繁的k個程序及次數
[root@localhost stp]# cat foo.stp
#!/usr/bin/env stap

global top_exec;

probe begin {
    printf("=== begin ===\n");
}

probe kernel.function("sys_write") {
    top_exec[execname()]++;
}

probe end {
    foreach(exec in top_exec- limit 10)
        printf("%s: %d\n", exec, top_exec[exec]);

    printf("=== end ===\n");
}

[root@localhost stp]# stap foo.stp -T 3
=== begin ===
qemu-kvm: 2948
sync: 2174
virsh: 673
libvirtd: 305
route: 28
grep: 21
ps: 21
ssh: 16
python: 16
nginx: 16
=== end ===
  1. 打印函數調用層次(用戶程序)

調用層次需要藉助 thread_indent 來打印合適的縮進,另外需要設置函數入口(call)和返回(return)的探測點。函數命名不足以使用function通配符來匹配,而 statement 雖然可以匹配我們想要的結果(過濾第三方函數),但是不支持 call 和return。雖然有一個比較傻但是的確可行的方式是,在腳本裏面手動寫入這些函數。

// 先取出需要關注的函數名稱
stap -L 'process("/tmp/limlog/build/tests/LogTest").statement("*@*.cpp")' |  sed 's/.*statement(\"\(.*\)@.*\".*/\1/g'  | sort -u

腳本如下:

#!/usr/bin/env stap

global f;

function init() {
    f["append"] = 1;
    f["benchmark"] = 1;
    f["consumable"] = 1;
    f["consume"] = 1;
    f["date"] = 1;
    f["datetime"] = 1;
    f["formatTimestamp"] = 1;
    f["getLogLevel"] = 1;
    f["gettid"] = 1;
    f["i16toa"] = 1;
    f["i2a"] = 1;
    f["i32toa"] = 1;
    f["i64toa"] = 1;
    f["incConsumable"] = 1;
    f["incConsumablePos"] = 1;
    f["LimLog"] = 1;
    f["~LimLog"] = 1;
    f["listStatistic"] = 1;
    f["log_10_diff_element_len"] = 1;
    f["log_10_diff_element_str"] = 1;
    f["log_10_diff_element_x1"] = 1;
    f["log_16_same_element_x6"] = 1;
    f["log_1_same_element_x6"] = 1;
    f["log_4_same_element_x6"] = 1;
    f["LogLine"] = 1;
    f["~LogLine"] = 1;
    f["LogSink"] = 1;
    f["~LogSink"] = 1;
    f["main"] = 1;
    f["now"] = 1;
    f["operator<<"] = 1;
    f["produce"] = 1;
    f["produceLog"] = 1;
    f["rollFile"] = 1;
    f["setLogFile"] = 1;
    f["setLogLevel"] = 1;
    f["setRollSize"] = 1;
    f["singleton"] = 1;
    f["sink"] = 1;
    f["sinkThreadFunc"] = 1;
    f["stringifyLogLevel"] = 1;
    f["test_blocking_buffer"] = 1;
    f["test_itoa"] = 1;
    f["test_timestamp"] = 1;
    f["time"] = 1;
    f["u16toa"] = 1;
    f["u2a"] = 1;
    f["u32toa"] = 1;
    f["u64toa"] = 1;
    f["used"] = 1;
    f["write"] = 1;
}

probe begin {
    printf("=== begin ===\n");
    init();
}

probe process("/tmp/limlog/build/tests/LogTest").function("*").call {
    fn = ppfunc()
    if (f[fn] == 1)
        printf("%s -> %s\n", thread_indent(4), ppfunc());
}

probe process("/tmp/limlog/build/tests/LogTest").function("*").return {
    fn = ppfunc()
    if (f[fn] == 1)
        printf("%s <- %s\n", thread_indent(-4), ppfunc());
}

probe end {
    printf("=== end ===\n");
}

函數的部分調用樹如下所示:

=== begin ===
     0 LogTest(25381):    -> main
   101 LogTest(25381):        -> setLogFile
   111 LogTest(25381):            -> singleton
   121 LogTest(25381):                -> LimLog
   129 LogTest(25381):                    -> LogSink
   270 LogTest(25381):                        -> now
   282 LogTest(25381):                        <- now
   288 LogTest(25381):                        -> date
   296 LogTest(25381):                            -> datetime
   453 LogTest(25381):                            <- datetime
   472 LogTest(25381):                        <- date
   478 LogTest(25381):                    <- LogSink
   576 LogTest(25381):                <- LimLog
   586 LogTest(25381):            <- singleton
   603 LogTest(25381):            -> rollFile
     0 LogTest(25382):    -> sinkThreadFunc
   650 LogTest(25381):            <- rollFile
   ···

樹狀調用可以很清楚的展示出來程序的執行邏輯,甚至可以看到多線程的處理:sinkThreadFunc 和 rollFile 之間的跨度。

  1. 開源庫對systemtap的集成
    像libguestfs 和libvirt都有支持,恰好工作環境這兩個庫也經常有一些難以排查的問題出現

觀察 libguestfs 的啟動過程及耗時,libvirt 支持function 和mark(有5個,大致邏輯上的埋點)

#! /usr/bin/env stap

global start_time, prev;

function display_time () {
  now = gettimeofday_us ()
  delta = (prev > 0 ) ? now - prev : 0
  printf ("%18d (+%10d): ", now - start_time, delta)
  prev = now
}

probe begin {
  start_time = gettimeofday_us ()
  prev = 0
  printf ("%-18s (+%10s): %s\n", "# time_usec",
          "delta usec", "event")
}

probe process("/usr/lib*/libguestfs.so.0*").function("*")
{
  display_time()
  printf ("%s\n", ppfunc())
}

打印的結果,如果使用 libguestfs-test-tools 執行失敗,可以根據產生的函數進入源代碼中進行排查,縮小排查的範圍

[root@localhost stp]# stap libguestfs.stp
# time_usec        (+delta usec): event
           1880754 (+         0): _init
           1880765 (+        11): frame_dummy
           1880768 (+         3): register_tm_clones
           1880772 (+         4): compile_regexp_re_hostname_port
           1880804 (+        32): init_libguestfs
           1881074 (+       270): guestfs_int_init_direct_backend
           1881079 (+         5): guestfs_int_register_backend
           1881083 (+         4): guestfs_int_init_libvirt_backend
           1881085 (+         2): guestfs_int_register_backend
           1881088 (+         3): guestfs_int_init_uml_backend
           ···

而 libvirt 中 example/ 目錄中就有包含一些關於 systemtap 的腳本文件。

火焰圖的生成

在函數調用樹之上更進一步,加入樣本的頻率得到svg格式的火焰圖,用戶可以點擊不同的圖層來查看更詳細的信息。

火焰圖工具為brendangregg開發的開源項目FlameGraph,配合春哥寫的systemtap工具,用一段簡單的shell腳本如下,可以得到一張svg的圖片

#!/bin/bash

# 火焰圖工具的路徑
FlameGraph_PATH="/FlameGraph"

# 春哥的棧收集工具
OR_Stap_PATH="/home/zxh/dev/openresty-systemtap-toolkit"

if [ $# != 1 ] ; then
        echo "Usage: sh $0 PID"
        echo "  e.g.: sh $0 4396"
        exit 1;
fi

${OR_Stap_PATH}/sample-bt -p $1 -t 180 -u > /tmp/bt-sample
${FlameGraph_PATH}/stackcollapse-stap.pl /tmp/bt-sample > /tmp/bt_stap.out
${FlameGraph_PATH}/flamegraph.pl /tmp/bt_stap.out > backtrace.svg

rm -rf /tmp/bt-sample
rm -rf /tmp/bt_stap.out

CPU定時採樣,根據採樣生成的 backtrace.svg 如下,橫軸為CPU佔用的時間比例,縱軸為函數調用的深度:

這是一個日誌庫的benchmark(博客園不支持上傳svg格式),而主要耗時也在阻塞隊列的入隊上,符合預期。

rpm 鏈接

  1. https://mirrors.ocf.berkeley.edu/centos-debuginfo/7/x86_64/kernel-debuginfo-common-x86_64-3.10.0-327.el7.x86_64.rpm
  2. https://mirrors.ocf.berkeley.edu/centos-debuginfo/7/x86_64/kernel-debuginfo-3.10.0-327.el7.x86_64.rpm
  3. ftp://ftp.pbone.net/mirror/ftp.scientificlinux.org/linux/scientific/7.0/x86_64/updates/security/kernel-devel-3.10.0-327.el7.x86_64.rpm

參考

  1. systemtap 原理圖, https://www.ibm.com/developerworks/cn/linux/l-cn-systemtap3/index.html?ca=drs-
  2. systemtap 官方教程(推薦),https://sourceware.org/systemtap/tutorial/1_Introduction.html
  3. systemtap 內置函數庫 tapset,https://sourceware.org/systemtap/tapsets/index.html
  4. 春哥的一系列stap工具,https://github.com/openresty/openresty-systemtap-toolkit
  5. 火焰圖工具,https://github.com/brendangregg/FlameGraph

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

設計模式系列之代理模式(Proxy Pattern)——對象的間接訪問

說明:設計模式系列文章是讀劉偉所著《設計模式的藝術之道(軟件開發人員內功修鍊之道)》一書的閱讀筆記。個人感覺這本書講的不錯,有興趣推薦讀一讀。詳細內容也可以看看此書作者的博客https://blog.csdn.net/LoveLion/article/details/17517213

模式概述

近年來,代購已逐步成為电子商務的一個重要分支。何謂代購,簡單來說就是找人幫忙購買所需要的商品,當然你可能需要向實施代購的人支付一定的費用。代購通常分為兩種類型:一種是因為在當地買不到某件商品,又或者是因為當地這件商品的價格比其他地區的貴,因此託人在其他地區甚至國外購買該商品,然後通過快遞發貨或者直接攜帶回來;還有一種代購,由於消費者對想要購買的商品相關信息的缺乏,自已無法確定其實際價值而又不想被商家宰,只好委託中介機構幫其講價或為其代買。

在軟件開發中,也有一種設計模式可以提供與代購類似的功能。由於某些原因,客戶端無法直接訪問某個對象或訪問某個對象存在困難時,可以通過一個稱之為“代理”的第三者來實現間接訪問,該方案對應的設計模式被稱為代理模式。

模式定義

代理模式(Proxy Pattern):給某一個對象提供一個代理或佔位符,並由代理對象來控制對原對象的訪問。

代理模式是一種對象結構型模式。在代理模式中引入了一個新的代理對象,代理對象在客戶端對象和目標對象之間起到中介的作用,它去掉客戶不能看到的內容和服務或者增添客戶需要的額外的新服務。

模式結構圖

代理模式的結構比較簡單,其核心是代理類,為了讓客戶端能夠一致性地對待真實對象和代理對象,在代理模式中引入了抽象層,代理模式結構如下圖所示:

代理模式包含如下三個角色:

  • Subject(抽象主題角色):它聲明了真實主題和代理主題的共同接口,這樣一來在任何使用真實主題的地方都可以使用代理主題,客戶端通常需要針對抽象主題角色進行編程。

  • Proxy(代理主題角色):它包含了對真實主題的引用,從而可以在任何時候操作真實主題對象;在代理主題角色中提供一個與真實主題角色相同的接口,以便在任何時候都可以替代真實主題;代理主題角色還可以控制對真實主題的使用,負責在需要的時候創建和刪除真實主題對象,並對真實主題對象的使用加以約束。通常,在代理主題角色中,客戶端在調用所引用的真實主題操作之前或之後還需要執行其他操作,而不僅僅是單純調用真實主題對象中的操作。

  • RealSubject(真實主題角色):它定義了代理角色所代表的真實對象,在真實主題角色中實現了真實的業務操作,客戶端可以通過代理主題角色間接調用真實主題角色中定義的操作。

模式偽代碼

代理模式的結構圖比較簡單,但是在真實的使用和實現過程中要複雜很多,特別是代理類的設計和實現。

抽象主題類聲明了真實主題類和代理類的公共方法,它可以是接口、抽象類或具體類,客戶端針對抽象主題類編程,一致性地對待真實主題和代理主題,典型的抽象主題類代碼如下:

public interface Subject {
    void request();
}

真實主題類實現了抽象主題類,提供了業務方法的具體實現,其典型代碼如下:

public class RealSubject implements Subject {
    @Override
    public void request() {
        //業務方法具體實現代碼
    }
}

代理類也是抽象主題類的子類,它維持一個對真實主題對象的引用,調用在真實主題中實現的業務方法,在調用時可以在原有業務方法的基礎上附加一些新的方法來對功能進行擴充或約束,最簡單的代理類實現代碼如下:

public class Proxy implements Subject {
    // 維持一個對真實主題對象的引用
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public void preRequest() {
        // ...
    }

    public void postRequest() {
        // ...
    }

    @Override
    public void request() {
        preRequest();
        // 調用真實主題對象的方法
        realSubject.request();
        postRequest();
    }
}

模式應用

像上面代理類所實現的接口和所代理的方法都在代碼中寫死,被稱之為靜態代理。如果要為不同類的不同方法生成靜態代理,代理類的數量將會發生爆炸。Java中也提供了對動態代理的支持。所謂動態代理(Dynamic Proxy),是指系統運行時動態生成代理類。

JDK中提供的動態代理只能代理一個或者多個接口,如果需要動態代理具體類或者抽象類,可以使用CGLib(Code Generation Library)等工具。CGLib是一個功能較為強大、性能也較好的代碼生成包,在許多AOP框架中得到廣泛應用。後面我會專門寫一篇探究動態代理實現的博客,將全面細緻地介紹動態代理。

模式總結

代理模式是常用的結構型設計模式之一,它為對象的間接訪問提供了一個解決方案,可以對對象的訪問進行控制。代理模式類型較多,其中遠程代理、虛擬代理、保護代理等在軟件開發中應用非常廣泛。

主要優點

(1) 能夠協調調用者和被調用者,在一定程度上降低了系統的耦合度。

(2) 客戶端可以針對抽象主題角色進行編程,增加和更換代理類無須修改源代碼,符合開閉原則,系統具有較好的靈活性和可擴展性。

此外,不同類型的代理模式也具有獨特的優點,例如:

(1) 遠程代理為位於兩個不同地址空間對象的訪問提供了一種實現機制,可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高系統的整體運行效率。

(2) 虛擬代理通過一個消耗資源較少的對象來代表一個消耗資源較多的對象,可以在一定程度上節省系統的運行開銷。

(3) 緩衝代理為某一個操作的結果提供臨時的緩存存儲空間,以便在後續使用中能夠共享這些結果,優化系統性能,縮短執行時間。

(4) 保護代理可以控制對一個對象的訪問權限,為不同用戶提供不同級別的使用權限。

主要缺點

(1) 由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢,例如保護代理。

(2) 實現代理模式需要額外的工作,而且有些代理模式的實現過程較為複雜,例如遠程代理。

適用場景

代理模式的類型較多,不同類型的代理模式有不同的優缺點,它們應用於不同的場合:

(1) 當客戶端對象需要訪問遠程主機中的對象時可以使用遠程代理。

(2) 當需要用一個消耗資源較少的對象來代表一個消耗資源較多的對象,從而降低系統開銷、縮短運行時間時可以使用虛擬代理,例如一個對象需要很長時間才能完成加載時。

(3) 當需要為某一個被頻繁訪問的操作結果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結果時可以使用緩衝代理。通過使用緩衝代理,系統無須在客戶端每一次訪問時都重新執行操作,只需直接從臨時緩衝區獲取操作結果即可。

(4) 當需要控制對一個對象的訪問,為不同用戶提供不同級別的訪問權限時可以使用保護代理。

(5) 當需要為一個對象的訪問(引用)提供一些額外的操作時可以使用智能引用代理。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

數據結構與算法(七):迷宮回溯和八皇后問題

一、迷宮回溯問題

1.問題

一個7*8的數組模擬迷宮,障礙用1表示,通路使用0表示,給定起點(1,1)和終點(6,5),要求給出起點到終點的通路

2.解題思路

  1. 首先,我們需要給程序一個尋向的基本策略,我們先假定尋向順序為“下-右-上-左”,也就是說從起點出發,先往下走,往下走不通就往右…..以此類推
  2. 然後我們需要給走過的路一個標記,暫記為2
  3. 而當從一個方向走到一個只能原路返回的死衚衕時,就給這段路標記為3
  4. 當抵達終點坐標(6,5)時程序結束

3.代碼實現

3.1生成地圖

/**
 * 創建一個二維數組,用於模擬8*7迷宮
 * 使用1表示不可通過的實心方塊,0表示可通過磚塊
 * (6,5)為默認終點,(1,1)為默認起點
 * @return
 */
public static int[][] getMap(){
    int[][] map = new int[8][7];
    //上下全置為1
    for(int i = 0;i <7 ;i++){
        map[0][i] = 1;
        map[7][i] = 1;
    }
    //左右全置為1
    for(int i = 0;i < 8;i++){
        map[i][0] = 1;
        map[i][6] = 1;
    }
    //設置擋板
    map[3][1] = 1;
    map[3][2] = 1;

    //輸出地圖
    System.out.println("地圖的初始情況:");
    showMap(map);

    return map;
}

/**
 * 展示地圖
 * @param map
 */
public static void showMap(int[][] map) {
    for(int i = 0;i < 8;i++){
        for(int j = 0;j < 7;j++){
            System.out.print(map[i][j] + " ");
        }
        System.out.println();
    }
}

3.2 尋路邏輯的實現

對於這個尋路程序,我們可以看見,往四個方向走的過程實際上除了方向外動作上是一樣的;而具體分析同一個方向,每走過一個坐標的動作也是一樣的,我們對流程進行分析:

  1. 出發,先往下走,判斷下一格有沒有障礙(int[x][y]==1)
  2. 如果沒有障礙,就繼續往下走,然後重複步驟1到碰到障礙為止
  3. 如果有障礙,就按“下-右-上-左”的順序,換個方向,然後重複步驟1到碰到障礙為止
  4. 如果找到了(6,5)就結束

表現為代碼實際上就是一個遞歸的過程:

  • 找路是方法體
  • 找到了(6,5)或者死衚衕是終止條件
/**
 * 給定起始點,根據地圖找路
 * 使用2表示可以走通的路,使用3表示走過但是不通的路
 * @param map 地圖二維數組
 * @param x 起始點橫坐標
 * @param y 起始點縱坐標
 * @return
 */
public static boolean findWay(int[][] map, int x, int y) {
    //如果走到了終點就終止
    if (map[6][5] == 2){
        return true;
    }else {
        //只有為0的路才能通過
        if (map[y][x] == 0) {
            //如果該點可以走通就打上標記
            map[y][x] = 2;
            if (findWay(map, x, y + 1)) {
                //向下遞歸
                return true;
            } else if (findWay(map, x + 1, y)) {
                //向右遞歸
                return true;
            } else if (findWay(map, x, y - 1)) {
                //向上遞歸
                return true;
            } else if (findWay(map, x - 1, y)) {
                //向左遞歸
                return true;
            } else {
                //都走不通說明是死衚衕
                map[y][x] = 3;
                return false;
            }
        }else {
            //不為0說明要麼是死路要麼是障礙
            return false;
        }
    }
}

3.3 運行結果

將findWay()方法中的終止條件從map[6][5] == 2換成其他坐標即可更換終點位置,

棋盤大小和障礙物位置不影響findWay()方法尋路。

二、八皇后問題

1.問題

皇后問題,一個古老而著名的問題,是回溯算法的典型案例。該問題由國際西洋棋棋手馬克斯·貝瑟爾於 1848 年提出:

在 8×8 格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,求有多少種擺法?

2.解題思路

  1. 首先,我們先使用一個長度為8數組來表示八皇后的擺放位置,數組下標+1即表示棋盤的第幾行,數組下標對應的存放的数字+1即為棋盤的第幾列。舉個例子:

    arr = {0,2,3,8,4,6,2,7}

    其中,元素0下標為0,即表示第一行第一列;元素2下標為1,即表示第二行第三列……以此類推。

  2. 任意假設任意坐標分標為(x1,y1),(x2,y2),也就是用數組表示為arr[x1]=y1,arr[x2]=y2的兩個皇后不允許在同一列,我們可以理解為:

    arr[x1] != arr[x2];

    而任意坐標的皇后不允許在同一斜線,即(x2-x1)=(y2-y1),也就是斜率不應當相同,我們可以理解為:

    Math.abs(x2-x1) != Math.abs(arr[x2]-arr[x1])

    (注:Math.abs()為求絕對值方法)

3.代碼實現

3.1 檢查擺放位置的代碼實現

在前面明確了如何用數組表示位置,以及如何檢查皇后是否允許擺放后,我們有如下代碼:

//表示皇后位置的數組
int[] arr = new int[8];

/**
 * 檢查第n個皇后是否與前面擺放的皇后衝突
 * @param n
 * @return
 */
public boolean check(int n) {
    //檢查第n層之前的皇后位置
    for (int i = 0; i < n; i++) {
        // arr[i] == arr[n] 檢查是否同一列
        // Math.abs(n - i) == Math.abs(arr[n] - arr[i]) 檢查是否同一斜線
        if (arr[i] == arr[n] ||
            Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
            return false;
        }
    }
    return true;
}

3.2 完整代碼

接着我們需要考慮如何使用遞歸方法來做到以下效果:

使用一個方法遍歷第n行的每一列,檢查每一列是否可以放置皇后:

  1. 如果可以放置皇后,將位置出入arr[n]中,然後遞歸調用自己,傳入n+1開始遍歷下一行…..以此類推
  2. 如果不可以放置皇后,就跳過該列檢查下一列,如果可以就重複步驟1
  3. 若n行中全部位置都不合適,則結束本層返回上一層n-1層,重複步驟1
  4. 如果最後n=8,即八個皇后全部放置完畢,記一次完成擺放,然後結束遞歸返回第一層,繼續檢查第一層的下一列

最終代碼實現結果如下:

/**
 * @Author:黃成興
 * @Date:2020-06-26 20:53
 * @Description:八皇后問題
 */
public class EightQueens {

    public static void main(String[] args) {
        EightQueens eightQueens = new EightQueens();
        eightQueens.set(0);
        System.out.println("共有擺法:" + eightQueens.count);
    }

    //記錄八皇後有幾種擺法
    int count = 0;

    //表示皇后位置的數組
    int[] arr = new int[8];

    /**
     * 擺放皇后
     * @param n 第幾個皇后
     */
    private void set(int n) {
        //如果放置好了第8個皇后
        if (n == 8){
            show();
            //記錄一種擺放方式
            count++;
            //回到第一層繼續遞歸
            return;
        }

        //遍歷第n行的每一列
        for (int i = 0; i < 8; i++) {
            //將該皇後放置在第n行第i列
            arr[n] = i;
            //檢查放置位置是否合適
            if (check(n)){
                //如果位置合適,就遞歸找下一個(n+1)皇后的擺放位置
                set(n + 1);
            }
            //如果位置不合適,就跳過這一列檢查下一列
        }
    }

    /**
     * 檢查第n個皇后是否與前面擺放的皇后衝突
     * @param n
     * @return
     */
    public boolean check(int n) {
        //檢查第n層之前的皇后位置
        for (int i = 0; i < n; i++) {
            // arr[i] == arr[n] 檢查是否同一列
            // Math.abs(n - i) == Math.abs(arr[n] - arr[i]) 檢查是否同一斜線
            if (arr[i] == arr[n] ||
                Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
                return false;
            }
        }
        return true;
    }
    

    /**
     * 展示某一擺法中八皇后的擺放位置
     */
    public void show() {
        for (int i : arr) {
            System.out.print(i + " ");
        }
        System.out.println();
    }
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※新北清潔公司,居家、辦公、裝潢細清專業服務

狀態機模式 與 ajax 的結合運用

太神奇了,昨晚做了個夢,夢中我悟出一個道理:凡是涉及到異步操作而且需要返回值的函數,一定要封裝成 Promise 的形式,假如返回值取決於多個異步操作的結果,那麼需要對每個異步操作進行狀態的設計,而且需要封裝一個 next 函數。,到了晚上才覺到很有意思,所以結合 ajax 設置最短返回時間和最大返回時間進行實踐:

  const PENDING = 'PENDING'
  const RESOLVED = 'RESOLVED'
  const REJECTED = 'REJECTED'
  const FULLFILLED = 'FULLFILLED'

  /**
   * @desc 異步操作模擬
   * @param time 響應時間
   * @param isError 是否拋錯
   */
  const mock = (time, isError) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (!isError) {
          resolve({ user: 'ManbaX' })
        } else {
          reject('request error')
        }
      }, time)
    })
  }

  /**
   * @desc 生產不同類型請求的工廠函數
   * @param time 響應時間
   * @param isError 是否拋錯
   */
  var RequestFactory = function (time, isError) {
    var request = function () {
      return new Promise((resolve, reject) => {
        var min = PENDING
        var max = PENDING
        var state = PENDING
        var res = null

        var next = function (name) {
          const cb = function () {
            if (state === RESOLVED) {
              resolve(res)
            } else {
              reject(res)
            }
          }
          if (name === 'res' && min === FULLFILLED) {
            cb()
          }

          if (name === 'min' && (state === RESOLVED || state === REJECTED)) {
            cb()
          }

          if (name === 'max' && state === PENDING) {
            reject('timeout')
          }
        }

        setTimeout(() => {
          min = FULLFILLED
          next('min')
        }, 500)

        setTimeout(() => {
          max = FULLFILLED
          next('max')
        }, 1000)

        mock(time, isError).then(data => {
          res = data
          state = RESOLVED
          next('res')
        }).catch(error => {
          res = error
          state = REJECTED
          next('res')
        })
      })
    }

    return request
  }

  // 不超時, 不返回錯誤
  console.time('r1')
  RequestFactory(200)().then(res => {
    console.log('data: ', res)
  }).finally(() => {
    console.timeEnd('r1')
  })
  
  // 不超時, 返回錯誤
  console.time('r2')
  RequestFactory(200, true)().catch(err => {
    console.log('error', err)
  }).finally(() => {
    console.timeEnd('r2')
  })

  // 超時
  console.time('r3')
  RequestFactory(2000)().catch(res => {
    console.log('error: ', res)
  }).finally(() => {
    console.timeEnd('r3')
  })

上面的運行結果符合預期,本來夢中還有另外一個有意思的東西,但是太模糊了就搞忘記了,下次一定早點記錄。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

互聯網是如何連接的:計網概述

當你在瀏覽器輸入網址之後會發生什麼

最直觀的感受當然是跳轉到網址所指向的頁面啦,但在網絡比較卡的時候,你可能注意到過,瀏覽器的左下角通常會有一些等待什麼什麼請求之類的小字。這時候,一個問題讓你搜索到了這篇博文,我輸入網址之後,瀏覽器到底幹了什麼?更要命的是,我想知道互聯網到底是如何把每個人連接起來的?

我們先來認識一下你的瀏覽器,雖然大多數時間你都在使用它愉快的網上衝浪。可能在你的印象里,這個玩意兒就是只能開開網頁,看看視頻,功能強大,但略顯單一。現在,隨便找個PDF文件然後打開,你的默認方式應該是你心愛的瀏覽器。如果不是,可以右鍵選擇打開方式,再用瀏覽器打開。

應該都能打開吧?這麼一說,好像我們從來沒注意過這傢伙居然還有別的用處。為什麼潛意思里覺得它就是看看網頁呢?這裏要更新一下名詞,實際上我們輸入的網址,更準確的叫法應該是URL。

URL: Uniform Resource Locator,統一資源定位符。詞聽起來有點繞口,你可以看看現在打開PDF標籤頁的地址欄,看看它的開頭是不是跟網址不一樣

URL有各種各樣的寫法“http:”、“https:”、“ftp:”、“file:”、“mailto:”,開頭部分表示了瀏覽器應當採取的訪問方式。那麼輸入網址之後,首先發生了什麼事就呼之欲出了。

瀏覽器先得解析URL

比如我們要訪問博客園的首頁,那麼應該在地址欄輸入https://www.cnblogs.com。瀏覽器看到URL的開頭是https,就會明白這是一個web請求,後面的www.cnblogs.com指的是Web服務器域名。

域名可以看成是IP地址的別名,你可以在搜索引擎里輸入IP,你會看到一連串的数字。沒有人會希望上個網還要先拿小本本把經常用到的IP地址記下來,再說,輸入這一連串的数字也很麻煩,一不小心就輸錯了。所以我們使用域名來代替IP地址,幫助記憶。

IP地址:你可以類比於現實中的住房地址,因特網上的計算機也擁有一個惟一標識符來區別它的網上鄰居(其他的電腦)。

APP支持IPv6網絡是怎麼回事?你可能在APP啟動界面的底部看到過這行小字,實際上IPv6之前是IPv4(互聯網通信協議第四版),就是一個給大家分配地址的協議,在這個版本里IP用32位的整數表示。32位?看起來很長的樣子,然而2011年的時候,IANA就宣布IPv4地址43億IP地址已經分配完畢。

後續的IPv6可謂是超級加強版,32位擴展到了128位意味着能給地球上每一粒沙子編號,他能分配大約3.4×10^38個IP地址!

向DNS查詢Web服務器的IP地址

域名對應着IP地址,那麼如何才能知道這個域名對應的IP地址是什麼呢?自然是得問拿小本本記下這些東西的人啦。

DNS : 一個可以將域名和IP地址相互映射的一個分佈式數據庫,能夠使人更方便的訪問互聯網,而不用去記住能夠被機器直接讀取的IP數串。

只用一台DNS服務器去存儲數不勝數的IP地址是不可能的,所以在分散保存的情況下,一台DNS是如何處理它查詢不到的域名信息呢?

實際上,如同域名一樣,DNS也是分級的。域名的最左邊部分表示機器名字,它隨後的部分則是該計算機所屬的組織,可能還會有子組織。最右邊則代表頂級域名,指定該計算機所屬組織的類型。大部分的頂級域都被用於美國的公司和組織,其他國家的計算機一般是用國家的特有頂級域,比如cn(中國)。

頂級域 主要應用
edu 教育機構
com 商業公司
org 非盈利組織
net 網絡供應商
cn 中國國家頂級域名

負責管理下級域的DNS服務器找不到該域名對應的IP地址就會發送一個請求到它們的上級DNS服務器中去尋找,然後上級DNS還找不到就到更上一級的DNS服務器中,以此類推。最上一級叫做根域,根域服務器的地址在配置DNS服務器的時候就已經寫入其配置文件裏面了。

分配給根域DNS服務器的IP地址在全世界僅有13個,很遺憾中國沒有。日本佔據一個,其他的全是歐美。

雖然IP地址只有13個,但實際上服務器的數量遠遠多於13個,因為根域DNS是使用多台服務器對應一個IP地址的。

不知道你是否有過瀏覽器突然不能訪問網頁的經歷,更離譜的是QQ居然能用,就他瀏覽器不行。這是為什麼呢?多半是DNS服務器地址的問題,在 控制面板\網絡和 Internet\網絡連接 右鍵屬性選擇IPv4 改成下圖一般就好了

為什麼QQ沒問題呢?因為QQ是直接通過IP連接的。

你訪問的其實是文件

現在不如點開一下我首頁的第一篇博客吧,或者直接訪問https://www.cnblogs.com/AD-milk/p/13171059.html然後按下F12打開開發者模式,讓我們看看都有些什麼?

我們請求了一個html文件,1317…好像就是輸入的網址里的那個。這麼說的話,這一個一個斜桿好像確實是像文件路徑的結構!

但如果你訪問博客園的首頁,你會發現路徑名那裡只有一個/,這表示根目錄,好像並沒有指定文件呀。這種省略文件名的情況下,一般是默認訪問根目錄下的index.html或者default.html這樣的文件。

html:超文本標記語言,你可以簡單的認為是可以在文本中嵌入音頻,圖片和鏈接的語言

按理說我們只是請求了13171059.html這個文件,但從圖中來看我們好像請求了不止一個文件,這是為什麼?回想一下,你會有過因為網卡而導致加載的頁面只有文字的情況?這一般是因為網絡問題,css文件以及圖片沒有被傳輸過來。瀏覽器在加載文件之前並不知道這裏還需要什麼圖片或者文件,只有加載之後才會繼續發送請求。

css文件是一種用來指定html樣式的文件。缺少了它頁面就沒有好看的風格了。

1 條請求消息中只能寫 1 個 URI。如果需要獲取多個文件,必須對每個文件單獨發送 1 條請求。

URI:Uniform Resource Identifier,統一資源標識符,URL是特殊的URI

眨眼間的旅途

你說的我都知道了,可你還是沒有回答我,互聯網到底是如何互聯的?

首先來考慮一個簡單的場景:兩台電腦之間如何互相通訊?當然是接跟線把兩個都連上啦,那再來五台怎麼辦?往電腦上再打五個接口?這顯然不現實,我們可以讓集線器幫我們的忙。它具有多個端口,可以實現其內部所有網絡設備的連通。不過集線器是個榆木腦袋,它唯一知道的事情就是:當某一個端口發出數據時,它就把這個數據複製到與其連接的其他端口,這樣所有連接到集線器的設備都可以獲得這個數據包。這跟個大喇叭一樣,太煩人了,說點悄悄話都不行。

之所以電纜不如光纖通信速率高,是因為電信號在提升通信速率的同時,其衰減率也會提高(信號在傳播過程中減弱),導致信號無法傳到目的地。相對地,光信號本來的衰減率就很低,提高通信速率也並不會提高衰減率。此外,光纖還不受電磁噪聲的影響,因此光纖能夠進行高速通信。

不是光信號本來就比電信號快啦!!!

為了解決廣播所帶來的安全問題,以及減少不必要的流量,我們可以換個聰明點的傢伙:交換機。交換機比集線器智能的地方在於,它記下了與其相連的主機的物理地址MAC,他把這些存儲在地址表裡面。那麼現在,當數據報從一個端口發出時,它能根據數據報提供的目標MAC,直接將數據發往預期的目的端口。終於可以和喜歡的女同學說悄悄話了,呼~~。

但集線器和交換機只能在局域網中交換信息,無法在外部網絡交換。因為要在互聯網上通訊需要識別IP地址,顯然集線器和交換機沒有這個功能。所以我們需要用到路由器:它是一個能根據IP地址,將數據報從一個路由轉發到另外一個路由的設備。這意味着它具有過濾功能,但一個數據報達到該路由時,它會檢查這是否是發給自己的,如果不是的話就將它轉發給其他路由。所以它相當於一個網絡的網關。

現在的路由器大多集成了三者的功能,成了“瑞士軍刀”。讓人想不到的是,儘管交換機比路由器簡單,但實際上路由器早於交換機問世。

現在從我們電腦發出的請求數據報就沿着電路,被多個路由器轉發,最後到達目的地。

至此,這眨眼間的漫長旅途迎來了終點。但其實網絡通訊遠不止這些,這裏省去了很多東西,其中最重要的就是各式各樣的協議了。缺少協議會使得網絡世界通訊困難,就好比現實生活中你覺得點頭表示OK,搖頭表示NO,但另外一個人卻持相反的認識,這時候你們倆的交流就會遇上大麻煩!

協議:定義了在兩個或多個通信實體之間交換的報文格式和順序,以及報文發送和接收一條報文或其他事件採取的動作

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

Mariadb之顯式使用表鎖和行級鎖

  首先我們來看看mariadb的鎖定概念,所謂鎖就是當一個進程或事務在操作某一資源時,為了防止其他用戶或者進程或事務對其進行資源操作,導致資源搶佔而發生衝突,通常在A進程操作該資源時,會對該資源進行加鎖,實現多進程或多用戶操作同一資源時,不會發生衝突;通常情況鎖的類型分讀鎖和寫鎖,所謂讀鎖就是共享鎖,它可以實現多個讀操作共享;而寫鎖就是排它鎖,獨佔鎖,一旦加了寫鎖,其他用戶的讀寫操作將被阻塞,直到該寫鎖被釋放或者因超時而被釋放,在其他用戶進行的讀寫操作,此時就會被執行;對於鎖定範圍來講,它又可以分為,表鎖和行鎖,從字面意思就可以理解到,表鎖就是針對整張表所施加的鎖,而這種鎖定力度相當粗糙,併發相對就比較低,但是維持鎖狀態鎖消耗的成本資源就較小;對於行鎖來說,它針對的範圍就是行級別所施加的鎖,這種鎖的粒度就相對要精細,同時併發相對較高,但是維護鎖狀態消耗的成本資源就相對要大;對於mysql來講又鎖分為存儲引擎級別的鎖和mysql server級別的鎖,存儲引擎級別的鎖指的是對於何時施加鎖或者釋放鎖由存儲引擎自行決定;mysql server級別鎖指的是用戶使用命令可自行決定施加鎖或釋放鎖;簡單講就是允許用戶顯式請求加鎖或釋放鎖;顯式鎖就是用戶手動用命令施加的鎖,隱式鎖指的是由存儲引擎根據需要自行施加的鎖;對於innodb存儲引擎來講,它支持事務,行級鎖;而早期的MyISAM存儲引擎它不支持事務,對鎖的粒度是表級鎖,不支持行級鎖;

  显示鎖的使用

  1)LOCK TABLES

    指令使用語法:

    LOCK TABLES  tbl_name  read|write, tbl_name read|write, …

  示例:

  提示:以上語句表示對test_tb這張表施加讀鎖操作,這意味着其他用戶或進程都不能對該表進行寫的操作,只能讀,因為讀鎖是共享鎖;

  測試:對test_tb表進行寫操作,看看是否能夠寫進去?

  提示:從上面的提示,它告訴我們test_tb這張表施加了讀鎖,不允許更新;這說明施加讀鎖,對於寫的操作就不能進行;

  測試:對test_tb表進行讀操作,看看是否能夠進行呢?

  提示:可以看到加了讀鎖的表,對於讀操作上可以繼續進行的;

  測試:對test_tb表施加寫鎖

  提示:釋放鎖用unlock tables即可釋放剛才的讀鎖;

  測試:對test_tb進行寫操作,看看是否能夠進行?

  提示:在當前終端(加寫鎖所在終端)上是可以進行讀寫操作的;

  測試:在其他終端看看是否能夠對test_tb表進行讀寫操作呢?

  提示:從上面的截圖可以看到當我們重新啟動一個終端對test_tb進行寫操作,它一直處於阻塞狀態;

  提示:對於對操作也是同樣的效果;一直阻塞着;

  總結:對於施加讀鎖的表,是可以進行讀操作的,但是不能進行寫操作,包括當前終端也不能寫操作;對於施加寫鎖的表,在當前施加鎖的終端上是可以對其進行讀寫操作的,但是在別的終端讀寫操作都將阻塞;

  除了以上指令來對錶進行加鎖外,還可以使用 flush tables指令來加讀鎖,具體語法請看下面;

  FLUSH TABLES tbl_name,… [WITH READ LOCK];

  測試:加讀鎖

  提示:以上flush tables 只能加讀鎖,不能加寫鎖;

  行級鎖:SELECT cluase [FOR UPDATE | LOCK IN SHARE MODE]

  行級排它鎖

  提示:以上紅框中的內容就是給第一行加了一個排它鎖,這意味着該事務沒有提交,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排它鎖,但是獲取排它鎖的事務是可以對數據就行讀取和修改。

  提示:可以看到我們重新啟動一個事務,然後對第一行進行更新操作,語句就阻塞在哪裡了;說明行級排它鎖對其他事務來講是不允許對加鎖的行進行寫操作;默認情況updeate更新會默認加上排它鎖,因為對於第一行來講,已經有一個排它鎖了,所以其他事務就不能對其在加其他鎖;而對於select語句來講,它執行時默認會加任何鎖的,所以我們執行select語句是可以正常的查看第一行數據;如果我們在select後面手動加鎖,它也會阻塞的;如下

  提示:從上面的截圖信息可以看到,我們手動加上排它鎖,查詢語句也不會順利執行;從上面信息還可以了解到,我們對第二行也沒法進行操作,這又是為什麼呢?

  提示:我們查看test_tb這張表上的索引信息,發現沒有索引,然後我們在上面創建了一個索引;創建索引時,需要把前面的事務提交了,才可創建成功,否則一直鎖在哪裡的;接下來我們在創建一個事務,把第一行加上排它鎖,然後在對第二行操作看看是否還會一直阻塞呢?

  提示:可以看到當我們創建就了索引后,再對第一行加鎖,然後更新第二行就可以正常更新了 ,對第一行還是處於阻塞狀態;這說明innodb存儲引擎的行級鎖的實現其實是依靠其對應的索引,所以如果操作的行並沒有用到索引,那麼用的還是表級鎖。施加行級排它鎖后,其他事務將不能對其在施加任何鎖;那麼對於獲取到排它鎖的是否能夠正常操作呢?

  提示:對於獲取到排它鎖的事務,是可以正常更新修改的;也可以給對應行施加其他鎖;

  行級共享鎖

  提示:以上紅框中的內容表示給第一行施加共享鎖,這意味着在其他事務鎖可以共享這把鎖看到數據,但是不能更新修改數據;

  測試:在當前事務中更新數據,看看是否可更新?

  提示:在當前事務中是可以正常修改數據的;也能正常查看數據;

  在其他事務中修改數據,看看是否可修改?

  提示:可以看到在其他事務中,就不能對有共享鎖的行進行修改操作,但是可以正常讀;

  總結:innodb存儲引擎的行級鎖依賴索引,如果沒有索引,就相當於表級鎖;對於排它鎖來講,獲取到排它鎖的事務是可以正常修改更新以及加共享鎖,對於沒有獲取到排它鎖的事務,是不能夠對有鎖的行進行修改更新以及加鎖的操作;對於共享鎖來講,對於當前事務(加鎖操作的事務)是可以正常修改更新有鎖的行,對於其他事務,是不可修改和更新有鎖的行;

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

四川綿陽2020年產15萬輛新能源車 總產值達5059億

中國四川省綿陽市政府常務會議審議並原則通過了《綿陽市新能源汽車產業發展規劃(2015—2020年)》。規劃提出欲在綿陽科技城集中發展區建設集新能源汽車核心技術研發、核心零件生產製造、整車製造與體驗、國家級專業孵化器、中國國家級工程技術實驗與檢測中心為一體的國家新能源汽車研究與產業發展基地。到2020年,實現年產15萬輛新能源汽車的產能,新能源汽車及關鍵零部件產業總產值1000億元人民幣(折合新台幣約5059億元)。   綿陽新能源汽車產業已擁有川汽、華晨汽車南方基地、中國重汽綿陽分公司等整車製造企業,還擁有長虹、安縣英志鋰電池等一批新能源汽車零配件企業。規劃提出,綿陽要「全域規劃」,加快新能源汽車在公共交通、公務、出租等領域的示範應用,政府採購的特種車輛也要優先選擇新能源汽車;大力發展農用低速電動車,不斷擴大新能源汽車應用範圍。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

LG Chem 開發新技術 電動車充電後可行駛 500 公里

電動車續航力有望加倍,南韓電池巨擘 LG Chem 宣布開發出新技術,電動車充電一次可行駛 400 至 500 公里,預計不久後就能量產。   韓國先驅報(Korea Herald)1 日報導,目前一般電動車充電後僅能行駛不到 200 公里。LG Chem 副會長兼執行長朴鎮洙表示,已研發出新技術,電動車行駛里程能增至 400至 500 公里。他說,產品不久就能投產,但拒絕透露更多細節。   LG Chem 砸錢投入開發,宣稱 2018 年研發費用將由當前的 6,000 億韓圜,提高到 9,000 億韓圜;未來 3 年研發人員也將增至 4,100 人。  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心

新北市:電動車牌照稅再延長 3 年

新北市政府稅捐稽徵處表示,為提高民眾購置電動汽車的意願,加速建立新北市成為綠色交通城市,凡民眾購買完全以電能為動力的電動汽車並於該市完成車籍登記,免徵使用牌照稅優惠延至 107 年 1 月 5 日止。   該處說,「完全以電能為動力的車輛」享有免徵使用牌照的優惠自 101 年 1 月 5 日施行起至今已滿 3 年,原優惠至今年 1 月 5 日截止。為繼續扶植國內電動車產業發展,並鼓勵民眾使用低汙染車輛,總統於 104 年 2 月 4 日公布修正使用牌照稅法第 5 條條文,繼續授權地方政府得對完全以電能為動力的汽車使用牌照稅免稅期間再延長 3 年。   稅捐處表示,配合國際新能源車輛的發展動向,及響應行政院繼續推動使用智慧電動車政策,新北市配合使用牌照稅法第 5 條修正,延長免徵牌照稅優惠期間至 107 年 1 月 5 日止。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準