linux學習問題總結
本文主要是自己學習linux中的一些思考和總結的記錄
一、環境變數和普通變數的區別
區別就是普通變數只會影響當前程序,子程序可以繼承父程序的環境變數
二、rsyslog和logrotate會不會丟記錄的問題
先說結論:不會
logrotate有create和copytruncate方案,這裡是考慮create這種預設方案。說說logrotate的操作步驟:假設日誌檔案為daemon.log,當前已經到了輪換的時間,logrotate會先重新命名daemon.log為daemon.log.1,然後新建一個daemon.log,給對應的程序傳送HUP訊號通知他日誌已經更新
當時一直在思考這個問題,一個程序在寫入一個日誌,然後logrotate將該日誌檔案重新命名,建立個新的同名檔案的過程中,日誌資料會不會丟失,這個問題其實還是對linux檔案系統的理解不夠深刻。
- linux的檔案分為inode和datanode,而檔案系統是以inode來唯一標誌這個檔案的,程序在開啟檔案的時候對應這個inode的一個檔案標誌符。
- 你要明白linux的檔名存在哪裡,linux檔名其實是存在目錄中,目錄也是檔案,該檔案存放了檔名和inode的對應關係,這裡也就理解了linux的硬連結,同一個檔案可以有多個名字
理解了上面的也就理解了為什麼不會,logrotate在重新命名的時候只是修改了目錄項,並沒有影響實際的檔案,所以該程序還是在往重新命名後的檔案寫入,logrotate給程序發訊號通知程序,程序在響應訊號的函式中去根據檔名查詢新的inode,替換檔案描述符,往新檔案寫入
這裡要特別注意你的程式要能響應這個HUP訊號,不處理這個訊號預設行為就是停止程序。
三、為什麼有些資料夾大小不是4096的整數倍
我們知道檔案系統中檔案存放需要inode(索引區塊)和dnode(資料區塊),一個檔案只有需要一個inode,dnode則是是一個或多個。目錄也是檔案,但是在檢視目錄的時候發現有時候目錄竟然不佔用區塊,很多目錄的大小都是4096的整數倍這個很好理解,因為檔案系統每個dnode大小都是固定的,一般都是4k,所以佔用一個或者多個就是4096的整數倍。那為什麼有些目錄大小不佔用區塊呢?
這其實是xfs的一個優化,如果目錄項比較少,那麼他就將資料存放到了目錄的inode當中,所以不佔用區塊。
我在自己的電腦上裡用ll -s
命令檢視跟目錄,可以看到dev、bin和home不佔用資料區塊(看第一列)。
0 lrwxrwxrwx.1 root root7 3月23 08:30 bin -> usr/bin 4 dr-xr-xr-x.5 root root 4096 4月3 22:01 boot 0 drwxr-xr-x.19 root root 3300 4月3 23:17 dev 12 drwxr-xr-x. 144 root root 8192 4月6 11:18 etc 0 drwxr-xr-x.5 root root45 4月3 23:06 home
既然談到了檔案大小的問題就補充下ls命令和du命令
du檢視檔案和目錄的大小
du -sh *
ls命令顯示檔案大小問題
ls -s -S -s 輸出大小 -S 按大小排序 -r 逆序排序
四、reboot和shutdown等軟連結實現原理
我們先來檢視下reboot和shutdown的檔案:
[chen@chen ~]$ ll `which reboot` `which shutdown` lrwxrwxrwx. 1 root root 16 3月30 22:12 /usr/sbin/reboot -> ../bin/systemctl lrwxrwxrwx. 1 root root 16 3月30 22:12 /usr/sbin/shutdown -> ../bin/systemctl
可以看到這兩檔案都是連結到systemctl符號連結,這裡我覺得很奇怪,都是連結到systemctl為什麼行為能不一樣?難道這兩個符號連結有什麼不一樣嗎?
但是怎麼去檢視符號連結的內容,通過cat /usr/sbin/reboot
這種命令去檢視時是直接到了原始檔,這時候可以使用readlink
這個命令去讀取符號連結的內容,內容如下:
[chen@chen ~]$ readlink `which reboot` `which shutdown` ../bin/systemctl ../bin/systemctl
可以看到內容是一樣的,那麼到底是怎麼實現的。其實這裡是通過獲取啟動時的名稱來做判斷的。看下下面的程式你就明白了:
#include<stdio.h> int main(int argc,char * argv[]){ for(int i=0;i<argc;i++) printf("%s\n",argv[i]); }
上面的程式列印了程式的輸入引數,利用gcc -std=c99 name.c
編譯後產生a.out檔案,建立一個a.out的符號連結ln -s a.out b
。
[chen@chen ~]$ `pwd`/a.out /home/chen/a.out [chen@chen ~]$ ./a.out ./a.out [chen@chen ~]$ ./b ./b
可以看到輸出的程式名稱的不同,你在建立符號連結的時候用到了不同的名字,systemctl可以根據名字來走不同的邏輯。所以如果你在自己的目錄建立一個reboot連結:ln -s /bin/systemctl reboot
實現的效果是一樣的。
五、systemd啟動時執行指令碼的問題
早先SystemV的init中我們有啟動時需要執行的指令碼時都會加入rc.local中,在systemd已經不推薦使用這個方法,我想很大的一個原因就是因為並行執行的問題。如果這個指令碼有大量耗時的任務,那麼這個指令碼只能按順序一個一個的執行才能啟動。所以systemd其中很大的一個改進就是並行執行,它建議我們自己編寫一個service的配置檔案,利用systemd來管理。
我的理解是如果是快速簡單的命令還是可以放在原來的init目錄,畢竟比較方便
六、crontab計劃任務隨機執行
有計劃任務想到的一個問題,如果我有很多工在某個時刻需要執行,為了避免同一個時間執行導致負載過高所以需要一些隨機化的處理而不是同一時刻觸發
思想就是加入隨機函式,例如要在1小時內隨機化:利用RANDOM環境變數$[(RANDOM%60]
別忘了在cron配置檔案中%需要轉義:$[(RANDOM\%60]
最終結果如下 :0 1 * * * sleep $[(RANDOM\%60]m ; /home/data/shell/script.sh
七、日誌輸出和標準輸出
這裡談談自己java開發的業務中的日誌,我們java的日誌現在一般採用的是log4fj和logback這種,在日誌的配置中我們一般會有多個appender,例如輸出到檔案的和輸出到標準輸出的。我們在ide中執行的程式的時候,標準輸出的所有輸出都會輸出在console中,如果去線上部署,例如nohup daemon & &>/dev/null
這種方式,標準輸出被重定向到/dev/null中,因為日誌也配置了檔案的appender所以沒什麼問題,但是如果你對程式碼中有System.out.println
這種輸出,那麼這些資訊就都丟了。所以儘量使用log來統一輸出日誌,還可以配置下將程式的標準輸出也寫入到日誌中。
參考連結