續上一篇隨筆: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,結果連接不上, 后來上網搜索了一下,才知道sqlplus連接串/后面一截,除了可以是sid之外,還可以是資料庫服務名,一個資料庫可以有多個服務名,一般只有一個, 而一個資料庫服務可以有多個實體,如果是rac一般是兩個實體, 我讓同事在tnsnames.ora配置,使用SERVICE_NAME = addb1,代替原來SID = addb1就可以正常連接了, 20190326: 今天碰到寫的程式用Os優化后速度反而不如O2優化快的問題,(十次測驗有9次比原來的慢5%左右) 這個有點奇怪,網上說Os是在O2基礎上優化的,相當于O2.5, 另外,今天碰上一個奇怪的問題,我把程式的一個函式改了,原來需要傳兩個引數,改成只傳一個引數(另一個引數原代碼沒有用上), 另外把原來判斷空格的邏輯從ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'改成查表(用256長的陣列,在程式開始時初始化), 本來以為會快一點,但實際運行發現優化過的函式反而比原來的慢了一點點,(程式同時呼叫優化前后的函式處理同樣資料1萬次得出的結論) 后來我打算用callgrind分析效率慢在哪時,又發現在callgrind下面跑,優化過的代碼卻比沒優化的快, 再之后,我把程式編譯優化選項從Os改為O2,再對比,發現這樣改后就比原來函式快,猜測Os編譯選項里面有針ch這種判斷做優化, 20190327: 今天安裝jemalloc時發現目錄下沒有configure,網上說要執行autogen.sh生成,但是我執行時提示autoconf不存在, 于是又上網下載了autoconf安裝, 最開始下載時下錯了版本,下了個2.10的版本,不能用:執行autogen.sh時提示沒有找到configure.in, 后來上網找,說autoconf要下載2.6以上的版本,因為新版本用的組態檔名是configure.ac,以前版本才需要configure.in, 于是又重新下載了新版本再編譯,這次就好了,順便提一下,如果下載的檔案是.xz壓縮包,可以用xz -d命令解壓, jemalloc編譯好后使用倒是簡單,啟動前在LD_PRELOAD環境變數里面增加jemalloc編譯出來的.so路徑就行, 不過我實際測驗,我們的應用使用jemalloc后速度反而下降了,不知道是不是因為我們程式都是單行程的原因 (jemalloc確認是已經使用了,因為程式運行程序中可以使用lsof|grep jemalloc可以看到程式裝載了對應的so) 20190328: 實測:glibc分配超過1k記憶體的時間消耗遠大于分配512B,估計超過1k的記憶體跟沒超過的處理方式是不一樣的, tcmalloc測驗效率差不多, 20190329: 今天一個同事突然找到我,說我前幾天給他們改的一個程式,在測驗機運行得好好的,但到了生產卻報需要的libc版本太低, 后來我查了一下,編譯環境和測驗環境的libc.so鏈接的是libc-2.17.so,而生產的是libc-2.12.so, 之前不知道這個情況,所以出了問題,我后來找了一個裝了低版本的作業系統重新編譯一下程式,再拿到測驗和生產環境,發現可以兼容, 20190407: 今天發現xargs命令做字串處理還是挺方便的,以前知道用它來跟管道組合處理引數太多超過命令列限制的情況,這里摘錄幾個用法: 替換字串:echo "nameXnameXnameXname" | xargs -dX 多行輸入單行輸出:(echo a; echo b; echo c) | xargs 組裝shell命令:cat arg.txt | xargs -I {} ./sk.sh -p {} 20190616: 一個web請求可能需要經過的cdn,dns負載均衡,f5負載均衡,nginx反向代理,訊息佇列,redis,資料庫(讀寫分離,集群),檔案緩沖等這么多種快取機制 20190630: select for update是上鎖,不是檢查有沒有鎖
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/141110.html
標籤:Linux
