續上一篇隨筆:https://www.cnblogs.com/kingstarer/p/11355612.html《工作碰上的技術問題及處理經驗》(三) 我這人記憶力比較差,經常出現有些知識學了不久後就忘了,或者有些問題花了很多時間百度解決後,再過一段時間碰上時只有模糊印象,卻忘了具體解決方法。 最 ...
續上一篇隨筆:https://www.cnblogs.com/kingstarer/p/11355612.html《工作碰上的技術問題及處理經驗》(三)
我這人記憶力比較差,經常出現有些知識學了不久後就忘了,或者有些問題花了很多時間百度解決後,再過一段時間碰上時只有模糊印象,卻忘了具體解決方法。
最近幾年工作時我開始有意識地把登記每天工作碰上的技術問題做個簡單筆記。
一般上班時間比較忙,只能草草記了一兩句話。等過一段時間,我會把這個筆記整理一下,把問題和處理經驗整理通順,以加深自己的印象。
20180721: gprof 可以列印出程式運行中各個函數消耗的時間,可以幫助程式員找出眾多函數中耗時最多的函數。 20180723: 字元集和字元編碼的關係:字元集是指字元的集合,也就是一些圖形的集合,在字元集裡面每個圖形會有一個整數值編碼。比較常見的是Unicode字元集。雖然為每個字元分配了一個唯一的整數值,但具體怎麼用位元組來表示每個字元,是由字元編碼決定的。字元編碼有GBK ASCII UTF8。相同的位元組序列使用不同的字元編碼解析,會得到不同的字元表整數序列,從而顯示出不同字元。因為文件信息裡面沒有編碼這個屬性,所以編輯器在讀取文件時先要猜測文件的編碼方法。如果保存時編碼與讀取時用的編碼不一樣,就會出現亂碼。 20180802: crontab裡面設置MAILTO=""可以避免腳本出錯時產生郵件。(如果不然,時間久了會消耗盡/var下麵的inode節點空間) 查看哪個目錄(不包含子目錄)下文件數最多 find . -type d | awk '{print "echo $(ls " $0 "|wc -l) " $0}' | sh | sort -n | tail 查看哪個目錄(包含子目錄)下文件數最多 find . -type d | awk '{print "echo $(ls -R " $0 "|wc -l) " $0}' | sh | sort -n | tail 20180803: 今天找到sqlplus按退格符不刪除字元,而只是顯示^H的解決方法。有兩種方法,試過都有效: 1 使用stty erase ^H,把刪除字元按鍵改為退格鍵。 2 修改SecureCRT會話選項,找到Terminal->Mapped Keys選項卡,鉤選"Other mappings"分組下麵兩個選項改成一個打鉤,另一個不打鉤即可。(我是打鉤了"Backspace send delete") 順便總結一下SecureCRT其它使用技巧吧: a Terminal->Emulation 把Scrollback buffer改大點,這樣可以看到更多的歷史輸出 b 如果伺服器使用UTF-8字元編碼,需要在Terminal->appearance把"Character encoding"改為UTF-8才能正常顯示中文。 c 在Connection->Logon Actions選項卡可以設置終端登錄後自動執行的操作。(如果需要比較複雜的自動登錄功能,還可以使用vbs腳本。設置在登錄時自動執行vbs。vbs腳本可以使用Script->Start Recoding Script根據滑鼠鍵盤操作生成腳本。) d terminal->Log File選項卡可以設置把操作日誌自動輸出到日誌文件。 e View->Chat Window可以打開文本輸入框,可以在文本框寫好腳本命令後一次執行(可以使用滑鼠輔助編輯) f View->Button Bar可以打開一個按鈕視窗,把常用命令寫在一個按鈕裡面,需要時點擊按鈕即可自動輸出 g 如果一次打開多個終端,想同時執行一個命令,可以使用交談視窗(View->Chat Window)編輯好命令,然後右鍵鉤先“Send Chat to All Tabs”,再發送 h 如果打算執行臨時記錄比較關鍵的操作日誌,可以使用File->Log Session功能,可以把操作過程屏幕輸出到指定文件 i Options->Global options可以設置“選中文字自動複製”“右(中)鍵自動粘貼”功能 j 有時候不小心用cat輸出二進位文件後,屏幕會出現花屏。這裡使用Edit->Reset就好了。或者使用echo -e '\xf' (原理是ascii碼14會讓屏幕花屏,15可讓屏幕恢復正常) k 如果需要往伺服器輸入大量文本(剪貼板都無法容納),可以先把文本保存到文件。使用Transfer->Send ASCII選擇發送文件。 l 有些伺服器設置間隔多長時間不操作就會自動斷開連接,可以在設置Session options->terminal選項卡裡面"Anti-idle"設置間隔多少秒自動發送字元。(一般是發空格)或者在離開時執行命令while [ 1 ]; do echo; sleep 1; done(可以配置在Button Bar裡面的快捷按鈕) m 如果終端輸出字元不滿屏,可以考慮是否Session options->terminal->emulation->Size設置的終端高度和寬度太小。如果不是,看看stty命令設置是否有問題。 n 如果使用ls --color=auto輸出的內容沒有彩色,可能是終端類型設置的問題。 o 打開多個終端操作時,可以使用鎖屏功能防止來回切換時誤操作比較重要的終端,ctrl+s鎖屏 ctrl+q解鎖 p 使用ssh2連接伺服器時,可以在終端選項卡右擊選擇"Connect SFTP Tab"快速打開sftp功能上傳或下載文件。 q secureCRT可以配置埠轉發功能,需要使用ssh2連接的伺服器才可以用。配置路徑:Session options->Connection->PortForwarding 20180807: 今天發現有些同事不知道這兩個shell腳本運行技巧: sh -x 調試運行 sh -n 檢查腳本是否有語法錯誤 awk需要從屏幕讀取信息時可以使用:getline < "-",用戶輸入會保存在$0 20180808: 今天給同事們準備培訓ppt時,我在一個頁面裡面放了很多元素,通過動畫控制出現順序。 但發現這樣放的元素太多了頁面很混亂,不好編輯。後來發現可以暫時隱藏一些暫時不關心的素材,這樣編輯起來方便很多。 方法:格式->選擇窗格->關閉需要隱藏的元素右邊的眼睛 20180810: 今天完成gcc在32位win系統編譯 20180814: 今天發現atof("1F01F00000.01")得到的值是1.000000,想來是碰到第一個非數字的字元就返回了 20180815: excel預設篩選數據時碰上空行就會停止,不進行空行後面的數據行的篩選。 如果要忽略空行先全選,再進行數據篩選。 excel 分列第三步可以選中不同列設置數據類型 20180816: vs2010預設if/else語句塊是不能大綱摺疊的,需要修改設置 工具->選項->文本編輯器->C/C++->格式設置->大綱語句塊 把False改為True 20170817: 今天發現source insight居然更新了,出了source insight 4,對中文支持比較好,而且預設文件編碼變成utf-8。 之前一直苦惱,現在項目源碼是utf-8,用source insight看比較累。 不過用4麻煩的是gbk編碼預設顯示是亂碼,要手動選擇編碼重新載入。 20180824: oracle允許線上重建索引增加online選項,對於頻繁訪問的表進行索引重建可以考慮使用這個特性。 20180907: 今天碰到兩個問題:1 對一個sql做邏輯等價修改後執行計劃發生比較大變化,導致效率慢很多。 我們項目原來有一個交易使用的sql裡面有類似這樣的邏輯:where tran_id in ('T1', 'T2', ..., 'TN') 最近為了讓這個sql顯示更靈活,我們建了一個表,把in子句裡面的id全放到表裡面,然後把sql改成這樣where tran_id in (select tran_id from tbl_tran_inf) 結果上線後發現原來只需要運行2秒的sql突然變成60秒。後來查了一下發現是新的sql的tran_id沒使用索引。‘ 這個是一個值得註意的教訓,儘管改造前後sql邏輯結果是一樣的,但效率卻差不少,以後要關註這種非功能變化。 問題2: 我們項目中出現了bug,導致把一些交易的返回報文發給了錯誤的前端socket。幸好我們每個交易有一個跟蹤號,前端系統發現收到的返回報文跟蹤號與發出的不一樣,拋棄了交易。 做非同步程式時要多註意報文錯亂的問題。 20180909: 今天打開一個之前保存在本地的csden博客網頁,打開一會後總是會自動跳轉到csdn首頁。不得以只能關了js。 網上找了fireforx關閉js的方法:在火狐地址欄輸入:about:config。搜索javascript.enabled,然後雙擊該欄,把值變為false即可 20180910: 使用ssh做埠轉發的命令例子: ssh -L 33019:10.153.198.92:33016 hg@132.97.190.157 -N 20180911: 今天在A_SVN_SRV伺服器搭建了svn服務,想在B_SVN_CLIENT伺服器上訪問(兩台都是linux)。不過A_SVN_SRV與B_SVN_CLIENT直連不了。 幸好有win主機C,可以通過ssh分別訪問A_SVN_SRV與B(但a與b不能訪問c,只能單向訪問)。於是嘗試通過secureCRT來做ssh埠轉發實現服務互轉。 配置如下: 首先,建session A,通過ssh連接伺服器A_SVN_SRV。在session options->PortForwarding添加一條映射規則。 Local埠填3691,Remote埠填36901。這樣就實現了將主機A_SVN_SRV的3690埠映射到主機C的36901埠。 訪問C的36901埠就等於訪問了A_SVN_SRV的3690埠。 接著,建Session B,通過ssh連接伺服器B_SVN_CLIENT。在session options->PortForwarding->Remote/X11添加一條映射規則。 Remote埠填3690,Local埠填36901。這樣就實現了將主機C的36901埠映射到主機B_SVN_CLIENT的埠3690埠。 B_SVN_CLIENT伺服器上訪問本機3690埠就等於訪問了C的36910埠。 今天在win上用gcc編譯代碼時碰上一個問題:有一個.c文件編譯總是失敗,gcc運行到一半會自動退出,報錯“MapViewOfFileEx:嘗試訪問無效地址”。 編譯其它.c沒問題,就這個總是報錯。於是嘗試用#if 0屏蔽部分代碼再編譯,逐步縮小編譯代碼範圍。最終確定是由於包含了某個頭文件時出現問題。並且把這個頭文件名字改一下,編譯也能通過。 懷疑是由於有重名頭文件導致的。到硬碟找了一下,發現這個頭文件在目錄裡面有一個對應的.gch文件,刪了再重新編譯就好了。 20180912: windows下的批處理腳本,可以使用start /b把一個命令在後臺啟動運行,如start /b dir 20180913: 今天聽一個網友介紹greenplum分散式資料庫,號稱可以通過增加機器線性增加資料庫性能。有機會要試試真假。 vs 離線安裝擴展:把其它機的擴展文件複製到C:\Users\Andrew\AppData\Local\Microsoft\VisualStudio\10.0\Extensions 然後在擴展管理器就可以看到,啟用即可 20180918: 今天從一臺機複製svn到另一臺機後,啟動時總報找不到對應的.so,但我的so也有複製過來的。 即使我修改了ld_library_path也無效。 後來上網找了資料,使用readlef -d查看程式,發現裡面指定了rpath。把so放到rpath指定的路徑再啟動就好了。 20180919: 分散式系統不同組件之前處理同一個業務超時時間要符合漏斗原則。 今天寫一個頭文件,在最後面的#endif 後沒加回車,導致其它cpp文件引用它後編譯報奇怪的錯誤,查了好一會。 今天編譯安裝svn伺服器,中間make過程耗時很長。後來發現使用make -j 4啟動四個進程同時編譯,速度快不少。 20180928: 番茄vs助手終於安裝好。今年年初由於進了新項目,不能在自己本地寫代碼,需要到雲桌面上開發。雲桌面c:/program file是受限的,沒法自行安裝程式。 開始時跟著原來的團隊一樣,使用source insight 3開發代碼,一段時間後覺得特不好用,最受不了的是它對utf8中文支持不好。 後來偶然發現原來雲桌面上預設安裝了vs2010,於是嘗試使用vs2010寫代碼,但是沒vc助手還是不習慣。而且項目裡面的代碼還有很多gcc語法特性, 沒法在vs上編譯。後來發現在vs上可以使用gcc編譯,寫起代碼就方便多了。但是沒有vs寫代碼還是不方便。 偶然機會,讓我發現原來雲桌面上預設安裝了一個舊版本的vc助手,但可惜過了註冊時間,所以不可用。於是上網找方法破解(正版要上千塊錢,捨不得買) 但這些破解版都需要替換安裝目錄下麵一個dll文件,而我們開發環境沒許可權修改。 在網上找了好久,最後終於發現有一個工具叫trial-Reset,它可以在註冊表刪除vs助手的使用信息,這樣就不會過期了。 20181010: 今天一個同事找過來,說我們提供的sql邏輯有問題,他們在程式裡面運行出現ora-24347錯誤。原在很容易找出來,是因為sum了空值記錄。但我查了一下代碼,發現我們程式用的是同樣的邏輯,居然沒報錯。 後來仔細分析了一下,發現這個“聚合函數出現空值”並不是一個oracle錯誤,而只是警告。java認為這種警告是異常而我們寫的c程式沒處理警告,所以沒出現異常。 類似的情況還有密碼快過期的警告,java程式登錄資料庫時會因為這個警告而導致登錄失敗,而使用sqlplus則不會出錯。 20181011: 今天一個同事讓我看一個問題:他有一個proc腳本,在plsql developer編譯是正常的,但是傳到伺服器上編譯後運行就會出現異常。 我後來發現在來是他腳本裡面有中文,在plsql developer運行時編碼選對了,所以編譯通過。但在伺服器上NLS_LANG設置錯了,導致伺服器編譯腳本時出異常。 20181012: 今天發現gcc支持%.*s格式化輸出,這對於列印沒'\0'結尾的字元串來說特別方便。 20181015: 今天用gdb調試時發現有一個變數在不同函數列印出來變數內容不一樣(變數是指針) 後來發現是改了頭文件裡面結構體定義,但沒刪了所有引用該頭文件的.c文件生成的.o導致的 gdb調試完程式後可以用save breadpoints保存斷點,下次啟動時用source命令重新載入斷點內容 20181016: 今天其它項目組發現一個服務定址組件的問題。定址組件是用於交易定址的,各組件在調用其它系統交易前,通過定址組件查找目標組件ip(需要部署一個定址服務用的客戶端進程)。 發生的問題是定址組件一臺伺服器宕機,導致部署在業務組件機器上的定址客戶端需要更新本地的定址伺服器緩存。 因為定址伺服器比較多(一百多台),這個過程耗時比較長(20+秒),導致這段時間組件定址失敗,業務服務出現故障。 後來定址組件給出的解決方案是,更新緩存前先備份現有的緩存,在更新完畢前繼續使用舊緩存定址。等新緩存更新完畢後替換,這樣就不會出現長時間等待緩存更新的情況。 20181017: 箱形圖(Box-plot)又稱為盒須圖、盒式圖或箱線圖,是一種用作顯示一組數據分散情況資料的統計圖。因形狀如箱子而得名。在各種領域也經常被使用,常見於品質管理。可以通過箱形圖比較直觀地看出數據質量,找出明顯偏離其它數據的點。 20181023: select * from (select c1, c2, c3 from tb where c1 = :key) a, dual where dual.dummy = a.c1(+); 20181024: tcp的keepalive機制是檢測死連接用的,http的keeplive機制則是防止頻繁重連用的 oracle提供了dbms_redefinition存儲過程用於線上修改庫表結構。 select for update是給記錄加鎖,並不是檢查記錄是否有鎖。如果select for update後忘了執行rollback或commit, 記錄會一直鎖著。 20181025: 今天使用Loadrunner進行性能測試時,發現在200併發時頻繁出現連接系統失敗的情況。但是以前是沒這情況的。 查了一下配置,與舊系統比,新增的一個服務(一個服務由多個進程組成)。把這個服務停了,系統又恢復正常的。 但是該服務監聽的埠跟以前系統服務監聽的都不一樣,理論上不應該相互影響。 開始懷疑是由於服務增加導致系統調度更加頻繁,因此降低了系統性能。但是測試了一下,啟動多幾個其它服務,並沒有影響。 於是又懷疑,雖然監聽的是不同埠,但會對系統底層有影響,相互之間會有干擾,造成性能下降。但查百度沒找到相關理論或案例。 後來偶然發現,原來是新服務與舊服務在進程總數配置上使用了同一塊共用記憶體(該共用記憶體記錄目前啟動的進程數量)。 因為調度進程要求新服務進程數+舊服務進程數=共用記憶體總數配置,導致舊服務進程啟動數量大大減少。把配置改改,分別使用不同共用記憶體即可。 20181026: 最近對系統做壓測優化,基本思路就通過loadrunner壓測並出報告,同時用建行內部提供的一個監控工具查看系統負載。 壓測期間通過多次執行pstack,看程式經常停留在函數,是否存在優化點。再通過ltrace和strace分析標準庫函數與系統函數調用次數,時間開銷,占比是否合理。 (我們系統是用c語言寫的) 對於懷疑不合理的地方,可以使用gdb連接進程設置斷點,讓進程執行到相應函數時停下來,看函數堆棧。結合代碼就可以看出函數調用原因進行分析。 這裡用到gdb調試的一個小技巧:忽略一定次數的斷點,命令形式是"ignore break_number count"。因為有些函數調用十分頻繁,而且大部分是合理調用。 如果每次調用都停下來,按c(gdb繼續運行命令)要按得手酸。 20181028: 今天系統cpu突然暴漲,懷疑是監控進程問題。業務增高,導致日誌增多。日誌多了,導致監控進程大量讀寫日誌。於是io與cpu就讓升,導致業務進程處理請求速度下降。 於是導致請求堆積,而接入進程為了處理堆積請求又會大量fork新進程處理,從而導致系統負載更大。 最後只能重啟應用,停了監控完事。奇怪的是我們系統是集群機,不知為何十幾台機裡面只有一臺有這問題。莫非是由於負載不均衡的原因? 20181029: 今天看見別人優化寫日誌的經驗文章,發現有一個常見的標準庫函數:localtime,居然是寫日誌的性能殺手。文章作者自己寫了一個localtime,把寫日誌時間減少了1/3 原因說是因為這個函數內部使用了鎖機制。不明白為何獲取時間需要用到鎖。 我查了一下,系統內部有不少無謂的localtime調用,把它們去掉,tps竟然增加了10。之前把一個重要邏輯優化了,也才差不多增加10 另外昨天的問題,查出來是所有機器cpu都高了,原因是微信出現異常導致相關交易量上升。 20181030: glib的g_list_append時間複雜度並不是O(1),而是O(n) gdb 列印字元串變數 不顯示全 print *var@len也不行 set print element 0才可以 最近用perf分析程式性能問題,記錄下幾個常用的命令perf stat -p perf record -e cpu-clock -g -p 目前發現的性能分析工具有perf pprof gprof valgrind 20181102: 今天想把crontab每30分鐘執行一次的任務改為每兩天運行一次,於是這樣改: 把*/30 * * * *改為* */2 * * * 但發現這樣改後,反而是每分鐘都執行一次。 正確改法應為0 */2 * * * gdb可以使用source 載入gdbscript腳本 最後用callgrind分析程式性能,發現非常好用。記錄幾個常用的命令 time valgrind --tool=callgrind Query 1611 9 time valgrind --tool=callgrind --callgrind-out-file="callgrind.query_1622_%p.out" Query 1622 9 # 在其它會話 callgrind_control -d # dump出目前收集的信息 callgrind_control -b # 列印堆棧 callgrind_control -s # 列印當前的函數收集狀態 callgrind_control -z # 清零 callgrind_control -k # 停止callgrind(不會輸出收集文件 所以先要dump) callgrind_annotate --tree=caller/calling/both 生成函數調用關係 callgrind_annotate --inclusive=yes # --tree=both --auto=yes --separate-threads=yes 今天分另在兩台機上用ltrace分析程式標準庫調用。發現其中一臺輸出結果跟另一臺輸出結果差很多。 程式是一樣的,ltrace版本號不一樣。想來是有些版本的ltrace有bug mv -b:當目標文件存在時,先進行備份在覆蓋 20181107: kill(-1) 所有會話都關閉 今天做壓測時發現怎麼增大客戶端數量,伺服器的壓力上不去。後來發現是loadrunner代理機磁碟空間滿了造成的。 20181112: 今天碰上一個鏈接庫順序,導致鏈接時庫函數找不到的問題: 我的編譯命令是這樣的:a.c (裡面沒有調用OutputLog) -lCommon(裡面有OutputLog函數) -lOpr(裡面調用了OutputLog函數) 這樣編譯會提示“undefind reference to `OutputLog' ” 換過來也不行,因為libOpr.a裡面有函數調用了libCommon.a裡面的函數。即兩個鏈接庫函數互相調用,交纏不清。 20181113: -O -g 即優化,又方便調試(代碼信息會有一點錯亂,但偏差不大) cp -f:目標無法修改,刪除再重試。 適用於替換正在運行的程式 time 輸出到日誌 file命令可以查看core文件是哪個程式生成的 20181115: //巨集定義轉字元串。原理:如果巨集定義涉及到字元串連接##,字元串化#,則編譯器不會對傳入巨集的參數做展開 //所以需要做二層轉換 #define __MACRO2STR(macro_value) #macro_value /*傳人MACRO2STR的macro_name會被編譯器自動展開成macro_value*/ #define MACRO2STR(macro_name) __MACRO2STR(macro_name) 看到一些同事喜歡用if (strlen(str) == 0)檢測字元串是否為空,其實沒必要。直接用(str[0] == '\0')效率會高點 20181116: 今天碰到一個訪問函數返回指針導致程式core的問題。 問題原因是在未聲明函數的情況下調用了一個返回指針的函數。 c語言對於未聲明的函數,預設是以為返回int,占4個位元組。但實際函數是返回了一個8個位元組長的指針。 這樣就導致獲取到的返回值出現了差異,後面訪問時出現段錯誤,程式core了。 今天在atexit註冊了一個函數,裡面執行fclose。運行後出現程式退出時core。猜測是因為fclose使用到的全局變數已銷毀。 今天碰上一個鏈接問題:兩個文件都定義了一個全局變數fp,其中一個有初始化,另一個沒初始化。 鏈接時居然不會出現衝突,而且把兩個符號合併了。後來上網搜,說這是gcc鏈接的規則,有初始化的全局變數是硬符號, 沒初始化的是軟符號,硬符號和軟符號同名時,會使用硬符號覆蓋軟符號。 20181120: 今天使用loadrunner跑應用時發現交易總是不能送達伺服器,昨天還好好的,今天突然不行了。 後來查了一下,發現是代理機空間滿了。把空間清理一下就好了。 今天寫了itoa函數,為了方便我把輸入的負數都乘以-1得到它們的絕對值再處理。 這樣對於大多數測試用例是沒問題的,但後來傳進去INT_MIN,發現結果錯了。原來-1 * INT_MIN用int是表示不了的。 今天看到別人使用args把文本轉成多行輸出,每行三個單詞,感覺很巧妙:cat test.txt | xargs -n3 20181129: 今天碰上一個日誌列印太快,導致日誌丟失的問題:原因是我們程式設置了每個日誌文件最大行數 如果達到最大行數,日誌組件會把目前的日誌重命名,增加時間戳尾碼,成為日誌名.log.hh24miss這樣的形式 但如果日誌列印速度太快,導致一秒內出現兩次備份,後面的備份就會覆蓋前面的備份。 今天測試了一下伺服器的硬碟讀寫速度,發現比家裡的ssd還快。使用的命令: time dd if=/dev/zero of=tmp bs=128k count=10000 time dd if=tmp of=/dev/null bs=128k count=10000 測試結果:寫速度 759M/秒 20181130: grep VmStk /proc/${pid}/status 查看進程占用棧空間 ar rusc lib 要編譯zlib的動態庫和靜態庫的話,從visual studio tool裡面打開Visual Studio 2008 Command Prompt,切換到zlib-1.2.8目錄,在Command Prompt里執行nmake -f win32/Makefile.msc,然就會在zlib-1.2.8目錄生成相關的dll和lib文件。 dumpbin dumpbin /symbols compress.obj /out:compress.txt nm -g --defined-only libpath dumpbin /linkermember libc.lib 20181202: extern "C" unsigned char g_to_char_map_list[4][17]; extern "C" unsigned char *g_to_char_map_list; 訪問異常 [4][17]; 20181203: base64編碼固定把源記憶體三個位元組轉成四個可見字元,不足三個位元組補0並且在字元串末尾添加=補全四個字元 所以base64字元串長度肯定是4的倍數,並且末尾有0~2個等號字元'=' 今天發現vs2010的strcmp比memcmp快一點(debug模式) 100萬次調用,strcmp花了32ms,memcmp花了59ms PerformanceTestLog(TEST_LOG_INF, "test strcmp"); for (i = 0; i < nTimes; ++i) { if (0 == strcmp(tmp, "test strcpy")) { i = i; } } PerformanceTestLog(TEST_LOG_INF, "test memcmp"); for (i = 0; i < nTimes; ++i) { if (0 == memcmp(tmp, "test strcpy", sizeof("test strcpy"))) { i = i; } } 效率差異挺小,記錄下來只是因為出乎我意料。因為之前一直以為strcmp需要判斷字元串結束,會慢一點的。 開始時我以為是函數參數不同帶來的影響,但我把字元串加大幾倍再測試,結果分別是101和158,差距更大了,是不是編譯器對strcmp函數有特別優化。 20181206: 今天發現了程式一處記憶體泄漏,每次泄漏8個位元組。之前壓測都沒發現,當時看監控曲線記憶體上升量幾乎沒變化。 現在想想系統記憶體是32g,壓測時最多時也就壓測幾萬次,總共泄漏80m記憶體,相比記憶體總量太小了,所以看監控曲線是不會有什麼變化的。 後來發現這個bug後啟動了一次8千萬的測試,終於看到記憶體曲線略有點變化。(0.1%變成0.8%) 20181207: 今天發現有同事不知道strncpy會把目標緩衝區結尾添加多個'\0',而不是只在字元串結尾添加一個'\0';在這裡記錄一下,避免其它人踩坑 char tmpBuf[1024]; memset(tmpBuf, 0xff, sizeof(tmpBuf)); printf("bef strncpy %d %d\n", tmpBuf[0], tmpBuf[sizeof(tmpBuf) - 1]); strncpy(tmpBuf, "123", sizeof("123")); //傳入4,只會寫4個位元組 printf("aft strncpy %d %d\n", tmpBuf[0], tmpBuf[sizeof(tmpBuf) - 1]); memset(tmpBuf, 0xff, sizeof(tmpBuf)); printf("bef strncpy %d %d\n", tmpBuf[0], tmpBuf[sizeof(tmpBuf) - 1]); strncpy(tmpBuf, "123", sizeof(tmpBuf)); //傳入1024,會寫1024個位元組,其中後面1021個位元組全是\0 printf("aft strncpy %d %d\n", tmpBuf[0], tmpBuf[sizeof(tmpBuf) - 1]); ================================ bef strncpy -1 -1 aft strncpy 49 -1 bef strncpy -1 -1 aft strncpy 49 0 幸好,測試了vc 2010的snprintf函數倒不會出現這個問題。我們平時用strncpy比snpritnf少很多 20181210: 今天發現開啟了編譯器優化後,有一個函數被編譯器主動內聯了(我沒有加inline)。之前沒註意,以為只有限定inline的函數,編譯器才會內聯。 今天發現gcc也支持靜態斷言:_STATIC_ASSERT 20181211: 同事今天分享的技巧: shell變數預設是全局的,如果需要遞歸調用會出現干擾。可以加上一行變數聲明代碼,將變數聲明成local的以避免這個問題,如:local i; 這個技巧只在bash裡面有用。 20181212: alias在腳本裡面無效 gcc 4.47 static變數(文件級)會自動初始化置0 棧上定義變數占用記憶體空間超過8M多一點後進程會掛(不管變數定義後有沒有用) 此時ps顯示系統占用記憶體很少,但是虛擬記憶體使用比較多(%M為0 VSZ大概是實際使用值) gdb查看core文件,顯示是接到信號11導致進程coredown 看函數堆棧入參顯示很多can not access memory at address 0x7ff7cc738eb78 a.out大小變化不大 static變數可以超過8M,我定義了一個1G大小的char數組都沒問題 如果static變數後代碼裡面根本沒使用,gcc會自動優化掉 監聽 127.0.0.1,埠只有本機客戶端可以訪問,其他伺服器無法訪問 今天用vi打開一個文件,發現顯示出現異常,不能全屏顯示。後來網上找了一下,在打開文件前先執行stty rows 36 cols 134就好了 20181219: 結構體中變數地址是按定義的先後順序從低到高,而在函數中定義的變數則相反,先定義的變數地址反而比後定義的高。 今天又碰上信號函數重入導致程式core或死鎖的問題。 ftp類工具需要註意寫入磁碟失敗(目標磁碟空間滿),今天沒註意,傳了一個文件後沒檢查有沒有成功。 後面出了異常才回來檢查發現問題。 20190102: utf-8編碼規則: 如果只有一個位元組,則其最高二進位位為0;如果是多位元組,其第一個位元組從最高位開始,連續的二進位位值為1的個數決定了 其編碼的位元組數,其餘各位元組均以10開頭。utf-8轉換表表示如下: unicode/ucs-4 bit數 utf-8 0000~007f 0~7 0XXX XXXX 0080~07FF 8~11 110X XXXX 10XX XXXX 0800~FFFF 12~16 1110 XXXX 10XX XXXX 10XX XXXX 10000~1F FFFF 17~21 1111 0XXX 10XX XXXX 10XX XXXX 10XX XXXX 20 0000~3FF FFFF 22~26 1111 10XX 10XX XXXX 10XX XXXX 10XX XXXX 10XX XXXX 400 0000~7FFF FFFF 27~31 1111 10XX 10XX XXXX 10XX XXXX 10XX XXXX 10XX XXXX 10XX XXXX 20190102: 今天碰上ue游標定位不正確的問題,後來發現是字體原因,設置一下精確點陣就好了。 20190107 最近寫了不少loadrunner腳本,記錄一下心得: 1 loadrunner腳本基本可以認為就是c語言代碼(loadrunner支持不同語言的腳本,預設生成的是用c語言寫的腳本) 2 loadrunner自己實現了一個c編譯器及對應的ide(也就是vugen),所以有一些地方跟我們常用的開發習慣不一樣。loadrunner內置編譯編譯器懷疑是在gcc基礎上改的,因為一些語法特性跟gcc比較像。 3 loadrunner的c編譯器實現了不少標準庫函數,但不提供對應的頭文件。所以使用到庫函數(如malloc)時最好自己聲明。(不然malloc會被編譯器認為是未知函數,返回值是int類型) 4 一些特殊的庫函數,如atof必須提前聲明,因為其返回值是double類型,而sizeof(double)跟sizeof(int)是不一樣的,這會導致返回值異常。 5 loadrunner可以使用#include包含頭文件,但由於沒有提供標準庫頭文件,所以#include <stdio.h>這樣會報錯,stdio.h找不到 6 loadrunner具體實現了哪些標準庫函數可以查看幫助 7 loadrunner提供了不少額外函數輔助編寫測試代碼,這些函數以lr_打頭,一樣在幫助文檔裡面有。我覺得比較好用的是日誌、http訪問、變數轉換系列函數。 8 loadrunner編譯腳本時是把工程裡面vuser_init.c Action.c vuser_end.c合併到一個文件再編譯,所以在這些文件裡面聲明的static變數會相互影響 9 loadrunner腳本程式棧空間很小,所以大數組最好用malloc動態生成 10 VuGen動畫模式下運行腳本速度很慢,因為每執行一行腳本代碼前,VuGen都會將游標跳到改行代碼並高亮,很耗時。(12版本這個問題非常明顯,15版本好很多)把動畫模式關閉運行速度能提高不少,但是還是遠不如在Controller裡面執行腳本的速度。 今天裝了個新的loadrunner,報lr_load_dll找不到指定的模塊。後來用windows dependency wallker查了一下,發現是缺少了幾個dll,上網下載回來放到system32就好了 20190117: 今天解決了vi命令打開日誌文件中文總是顯示亂碼的問題。由於項目組中的日誌包含一些特殊字元,所以使用vim打開日誌文件時總是不能正確識別出文件字元編碼。此時用:set fileencoding命令可以看出vim把文件編碼識別成latin1。 在這種情況下無論終端設置成gbk還是utf-8編碼,都不能正確顯示中文。解決方法有兩個: 1 使用:e ++enc=utf-8命令強制讓vim以utf-8編碼重新打開文件 註意:由於我們程式有時也會輸出gbk編碼的中文字元日誌,所以有時還會有少量亂碼。 2 在打開文件前設置好正確的fileencodings(註意這個參數比前面多了一個s,上面的是vim探測出來的文件編碼,這個是可供vim選擇的文件編碼列表) 在~/.vimrc裡面加上一行設置 set fileencodings=ucs-bom,utf-8,gbk18030 (我們項目組機器預設的fileencodings是ucs-bom,utf-8,latin1,latin1是一種相容性很強的字元編碼,這樣的設置讓vim很傾向於認為文件編碼是latin1) 總結一下今天學到的vim編碼知識:vim涉及字元顯示的選項有三個,fileencoding文件字元編碼,encoding緩衝區字元編碼,termencoding終端字元編碼。 vim顯示字元的順序:按fileencoding編碼讀取文件->將讀取到的內容轉成encoding編碼->將encoding編碼轉換成termencoding列印到終端->終端(我們平時主要使用的是securecrt)按設置的編碼(一般是utf8)顯示字元。其中fileencoding必須在文件打開前設置才有效,encoding必須在vim啟動前設置才有效,termencoding可以根據需要隨時設置。 這四個編碼如果設置不統一,就很有可能出現中文亂碼問題,其中前三個編碼可以在vim查看,最後一個編碼需要在securecrt設置查看。 20190122: 今天看了一篇文章介紹說平時我們生成指定範圍內的隨機數方法,其實是有問題的。平時我們要生成[a,b]範圍內的隨機數,是這樣寫的:rand() % (b - a + 1) + a。這樣寫有一個問題是rand的範圍是[0, RAND_MAX],不一定能整除(b - a +1),這樣生成的隨機數概率是不均衡的,裡面某些數字出現的概率是其它數字的幾倍。 一個推薦的做法是用(int)(rand() / RAND_MAX) * (b - a +1) +a,但這樣做也是並不是完全隨機的。具體原因可以參考:https://gameinstitute.qq.com/community/detail/100067 隨機數常見演算法優劣剖析 20190123: 今天發現原來xml換行符轉義寫法有兩種:
和&0010 20190124: 今天發現crontab居然沒辦法設置隔五天運行一次腳本,因為它只支持配置分時日月星期間隔。 crontab這樣設置每隔五天執行:0 0 */5 * *,在月底時會出現問題,30號執行後1號又執行。 20190130: linux刪除文件後空間沒釋放,這種情況一般是由於文件還在被其它進程打開。這裡可以用lsof | grep deleted,找出占用空間的文件及進程,把相關進程全殺完就好了。(註意,最好用root運行上面的命令。因為如果文件是其它用戶進程打開,這樣執行可能因為許可權不夠找不出來) 20190201: 今天用vs調試時,發現一個變數顯示值總是有問題。我明明設置它的值是5,但調試總顯示其它值,我把這個變數改成const變數,也一樣顯示不正確。後來才發現因為我工程是release模式,改成debug就沒問題了。release模式下調試程式,watch視窗顯示的變數值顯示是不一定正確的。 20190202: 今天發現用loadlib載入的dll(假設名為a.dll;)如果依賴其它dll(假設名為b.dll), 並且b.dll不在PATH指定的目錄下(a.dll),會報載入a.dll失敗。 20190203: 今天一個同事分享一篇文章,作者稱經過測試發現strncpy效率不如snprintf高,跟我之前自己測試的結果是相反的。 我自己測試出來snprintf函數比strncpy慢了一個數量級。後來仔細看了文章作者測試過程,是用snprintf(buf, 1024, "test"); 跟strncpy(buf, "test", 1024)做比較。 猜測文章作者並沒有註意到兩個問題導致的:1 snprintf調用比較簡單可能會被編譯器優化(之前碰到過sprintf會被優化成strcpy的情況) 2 strncpy實際往buf寫了1024個位元組,其中前4個位元組是test,剩下1000個位元組填了\0。而snprintf只會往buf填5個位元組。 20190213 今天使用遠程連接上一臺機器看,發現UE菜單欄的字元十分模糊(感覺是一種奇怪的字體)。網上找了幾個方法都沒改過來,後來我隨意設置參數,居然讓我發現一個方法:windows屏幕解析度設置界面->放大或縮小文本和其它項目->調整ClearType文本。 20190214 今天修改了系統程式一個問題:Fork出來的子進程可能帶有日誌緩衝,導致輸出日誌時出現混亂。 解決方法:Fork前先關閉日誌文件 20190219: 今天在windows系統上安裝一個軟體,在用戶環境變數配置了軟體需要的環境變數居然不生效。 要改成在系統環境變數配置才行。之前不知道用戶環境變數與系統環境變數對於windows程式是有區別的,以為效果是一樣。 20190220: 今天安裝loadrunner 11過程中報"存儲空間不足,無法執行此命令"的錯誤。後來網上找原因, 說是因為操作系統是2008,不需要安裝windows installer 3.1,安裝時候會因為版本不正確而出現這個提示。(微軟這個錯誤提示真差) 修改安裝配置lrunner/Chs/pwraper.ini把第一行刪除(我開始用#註釋,發現不行,必須刪除),然後重啟安裝程式。 這樣就不會要求先安裝windows installer 3.1,可以順序往下執行。 20190306: 這兩天發現項目組一個程式有記憶體泄漏(ps aux www觀察可以發現rss一直在增長)。今天使用valgrind跟蹤一下,看是啥情況。記一下命令 valgrind --tool=memcheck --leak-check=full 程式名 運行參數 執行命令後運行一段時間,然後想辦法讓程式退出(我這裡直接kill就行),valgrind就會輸出記憶體泄漏的地方(malloc)。 之後再用gdb跟蹤一下,看malloc申請的記憶體為何沒free,很容易就找到問題。 20190321: 今天碰上一個loadrunner 11程式啟動報msvcr110.dll找不到的問題,解決過程中學到一些知識點,記錄一下。 開始我看C:\Windows\System32\下麵沒這個文件,於是到其它機複製了一個放到該目錄下。 (C:\Windows\System32\是windows尋找dll預設目錄之一,其它的路徑包括當前目錄和PATH) 但重啟程式還是報這個錯,於是我以為是因為msvcr110.dll依賴的dll沒被拷過來的原因。因為以前碰上過類似的問題: 用loadrunner裝載dll,提示模塊不存在,但其實該dll是存在的,但是由於其依賴的dll不存在,導致裝載dll失敗。 但loadrunner誤以為dll不存在,所以提示“模塊不存在”。 於是下載了一個windows dependency wallker。想用dependency檢查一下,發現並沒有缺失依賴dll。 再用它打開啟動報錯的程式,發現也沒提示缺失依賴dll。但有提示dll的cpu類型不正確。後來仔細檢查發現我那個程式是32位的, 但依賴了system32下麵很多64位的dll。 這就比較奇怪了,一般32位程式是不能使用64位dll的,後來上網搜索,偶然發現一篇文章提到一個知識點: “64位windows操作系統,Windows\System32下麵放的是64位的dll,而32位dll是放在Windows\SysWOW64。 為保證程式相容性,32位程式訪問Windows\System32時系統會自動鏈接成Windows\SysWOW64” 於是明白了,我那個msvcr110.dll是32位的,要啟動的程式也是32位的,所以必須把它放到Windows\SysWOW64才行。 把文件拷貝過去後,再啟動程式,果然可以了! 以前只知道windows64位系統相容32位和64位程式,但從來沒考慮過具體怎麼相容,這次踩坑了。 我下載的windows dependency wallker是64位程式,所以使用它去查找32位程式依賴時會出現問題,應該再下載一個32位的。 順便說一下另一個技巧:如何識別程式或者dll是64位還是32位的。最簡單當然是用windows dependency wallker打開看看,但有時沒這個 工具,我們可以使用ue以16進位打開程式或者dll文件,在比較靠前的位置應該能發現字母PE字樣(一般在第一屏就有,找不到可以搜索asscii碼50 45) 看PE後面跟的字線是L還是d,如果是L說明是32位程式,如果是d說明是64位程式。 20190322: 今天有其它系統同事說訪問我們系統失敗,服務拒絕連接,問我們是不是應用有問題。我查看日誌,發現他是使用get協議訪問我們的應用。 而我們的系統對於get請求,預設處理動作是返回指定文件,而那個文件不存在,所以連接就被關閉了。我們的連接底層庫只有對post請求才往上送。 後來咨詢了一下,原來同事直接在瀏覽器輸入我們系統url,沒按協議要求使用post請求。他以為效果應該一樣的,實際上不是。 做交易最好還是多遵守人家的協議標準,有時不能按常識推理。我記得以前有一個系統在nginx配置了url處理規則,使用http功能變數名稱跟使用ip訪問走不同的請求邏輯。 而其他人不知道,訪問他們系統時使用了http://IP地址/path,結果返回的結果錯誤。其實按該系統標準,應該使用http://功能變數名稱/path 20190325: 今天一個同事問我sqlplus連接串 user/pass@128.192.118.1/addb1 最後面的addb1是什麼。我說是資料庫實例名,但他說不是。因為他連接 到資料庫後查詢v$instance裡面顯示資料庫實例名sid是另一個名稱。而且它用這個SID配置了tnsnames.ora,