ALP Chapter 8 Linux系統函數(Linux System Calls)
-
到目前為止我們介紹的所有API都分可歸為兩類:
- 庫函數(library function):普通的函數,由一個不屬于我們程序的外部的庫編寫,這種外部的庫的一個典型例子是C庫。這種函數的參數傳遞是通過寄存器或者堆棧來實現的,執行的時候直接跳轉到該函數代碼的開頭,也就是我們學過的典型的C語言函數調用的方式。這種庫函數的例子:getopt_long, mkstemp
- 系統函數(system call):由Linux內核實現的函數。這種函數的參數傳遞和調用都是由內核實現的,并且需要做一個額外的操作。(例如在操作系統上提到的,轉換進程的狀態,由用戶態轉為內核態)可是為什么我們在實際寫代碼的時候沒有任何的區別?因為GNU C庫已經為我們給這些系統函數外面包了一層,我們調用起來就和調用庫函數是一樣的。這種系統函數的例子:open, read
- See it before explore it,我們來看看系統函數是什么樣子,先睹為快:/usr/include/asm/unistd.h
8.1 使用strace (Using strace)
- Strace命令可以監視另一個程序的執行,并列出該程序所調用的所有system calls和所接收到的所有signal。
- strace輸出的每一行代表一個system call或者signal。對于system call來說,首先是該system call的名字,然后是其參數,最后是返回值。而signal的輸出則是signal symbol和一個signal字符串的形式。
- 接下來我們進入主題,介紹幾個非常重要的系統函數:
8.2 access: 測試文件訪問權限
- 系統函數access測試當前進程是否有權限訪問一個文件。
- access的第一個參數是文件的路徑,第二個參數的值可以是:R_OK, W_OK, X_OK,分部對應讀,寫和執行。另一個可選的值是F_OK,這種情況下access只檢測該文件是否存在。
- access如果返回0則表示當前進程擁有指定的權限。返回-1則表示沒有權限,并且errno被設為EACCES(或者EROFS,如果我們對一個只讀文件要求可寫權限的話)
8.3 fcntl: 加鎖以及其他一些文件操作 (Locks and Other File Operatioins)
- 系統函數fcntl的第一個參數是文件描述符,第二個是要對文件執行的操作。對某些操作來說,還有額外的參數。
- 對于加鎖操作來說,有兩種鎖,讀鎖和寫鎖。很顯然,讀鎖是可以并存的,即多個進程可以在同一個文件上加上各自的讀鎖,寫鎖是排他的,同一時間只有一個進程可以在一個文件上加寫鎖。
- 注意:一個文件被加鎖并不意味著他不能被別的進程打開,讀或者寫。只有當別的進程試圖對該文件加鎖時,現有的鎖才會發生作用。
- 其實在加鎖/解鎖方面,另一個函數flock有著相同的效果。我們之所以選擇使用fcntl的原因是:它支持NFS文件系統!
8.4 fsync和fdatasync:flush磁盤緩沖區 (fysnc and fdatasync: Flushing Disk Buffers)
- fsync的參數只有一個:需要flush的文件描述符。fdatasync的作用也是一樣。
- 他們的區別:fsync保證會更新文件的修改時間,而fdatasync不保證。所以理論上fdatasync是會比fsync更快一點的
- 但是……目前的linux版本里面這兩個是完全一樣的,所以大家隨便用吧。
8.5 getrlimit和setrlimit:資源限制 (getrlimit and setrlimit: Resource Limits)
- 這兩個系統函數是和resource limit相關的。你用過ulimit命令嗎?(反正我沒用過)
- 對于每個資源來說有兩個limit:一個是hard limit,一個是soft limit,其中后者永遠不能超過前者,并且只有擁有superuser權限的進程可以更改前者。
- getrlimit和setrlimit的參數相同:第一個參數是資源的類型,第二個參數是rlimit結構的指針,這個結構里面就只有兩個成員:hard limit和soft limit
-
幾個重要的資源類型
- RLIMIT_CPU:程序執行的最大CPU時間,單位是秒。超過之后程序中止,中止的信號是SIGXCPU
- RLIMIT_DATA:程序執行的最大內存
- RLIMIT_NPROC:程序孵出的最大子進程數量
- RLIMIT_NOFILE:程序打開的文件的最大數量
8.6 getrusage:進程統計信息 (getrusage: Process Statistics)
-
getrusage有兩個參數
- 第一個參數是類型,如果是RUSAGE_SELF,則返回其自身的統計信息;如果是RUSAGE_CHILDREN,則返回其屬下的所有已結束的子進程的統計信息。
- 第二參數是rusage結構的指針。這個結構里面幾個比較重要的成員是:
- ru_utime,類型是timeval結構,記錄user time;
- ru_stime,類型是timeval結構,記錄system time;
- ru_maxrss,記錄最大內存使用量
8.7 gettimeofday: Wall-Clock時間(gettimeofday: Wall-Clock Time)
- gettimeofday返回的是系統的wall-clock時間,這個wall-clock時間我還真的不知道怎么翻譯,它是一個timeval結構,以秒為單位,里面只有兩個域,第一個域就是秒的整數部分,第二個域是毫秒。這個秒的計算方法是:從1970年1月1號的凌晨到現在。
- 很顯然,這么長長的秒非常難用。所以localtime這個函數負責把timeval結構轉化為tm結構,tm結構就很簡單了,有tm_year,tm_mon,tm_day,tm_hour等等域,一看就知道什么意思,不說了。
- 有了tm結構之后,可以用strftime來獲得一個很好看的輸出。這個函數和printf差不多,不同的只是它里面的字符串%Y表示年,%m表示月,等等,具體去看man page吧。(不是我偷懶,要學linux就必須有看man page的習慣,我現在是越來越深刻的認識到這一點了)
8.8 mlock家族:鎖定物理內存(The mlock Family: Locking Physical Memory)
- 什么叫鎖定物理內存?我們先來復習一下操作系統里面換頁的概念:頁是內存分配的基礎,一個程序運行的時候占據了物理內存中的若干頁。當操作系統發現物理內存不夠用的時候,他會根據一個調度算法(通常是找出最近最少使用的)找出最應該換出的頁,把那個頁空閑出來分配給需要的進程。所以這里鎖定物理內存的意思就是:我死活就霸著我指定的這些頁不放,你操作系統要找空閑頁找別的進程去,即時我很久沒有用他們,你也不準把他們釋放出來給別人用!頗有點站著茅坑不拉屎的氣魄。
- 這樣做當然是有缺點的,我們可以想象一下每個進程都這么來一下我們的操作系統同學會有多么的郁悶與無奈。但這個方法確實也是相當有用的:1,對于時間要求很高的程序,換頁耗時;2,對于安全性很高的程序,換頁勢必要把頁內的東西寫出到某個swap文件,這個文件被侵入者偷看了怎么辦?
- 鎖定就是mlock,解鎖就是munlock,鎖定當前進程申請的(也可以包括未來申請的)所有內存空間就是mlockall,釋放所有內存空間就是munlockall(這個也可以用來釋放mlock所鎖住的空間)
- 鎖住很大的空間可能會導致你的操作系統忙死,導致頻繁的換頁操作(這個就是著名的thrashing現象了)。所以理所當然的,只有superuser才可以調用mlock和malockall。
-
另外注意一個很猥瑣的現象:如果你的操作系統不幸是采用copy-on-write戰術的,你申請一塊內存然后馬上mlock它就很可能導致不是你想要的結果。比較猥瑣的一個對付方法是:申請一塊內存時候,給這個內存的每個頁都寫上那么一點東西,一個bit足矣。例如下面:
for (i = 0; i < alloc_size; i += page_size) memory[i] = 0;
8.9 mprotect: 設置內存訪問權限(mprotect: Setting Memory Permissions)
- 還記得mmap函數吧?把一個文件做內存映射,映射的時候可以指定訪問權限。mprotect可以修改這樣的內存的訪問權限。
- 違反mprotect的設定訪問而訪問內存會生成SIGSEGV signal,所以我們可以通過catch這個signal來監控這塊內存的被訪問情況
- 上文只提到了mmap之后的內存可以用mprotect來管理,那么一般的內存呢?例如malloc申請出來的,也可以這樣嗎?根據本人的試驗,答案是:不可以。
8.10 nanosleep: 高精度休眠(nanosleep: High-Precision Sleeping)
- 不說了,一聽名字就夠拉風了,也知道它是干什么的。注意參數是timespec結構。但是!它遠遠沒有它的名字那么厲害。(我也想要它有納秒級別的響應啊,但拜托你先看看你cpu的主頻)通常來說,它的最小精度是10毫秒。
- 另外一點,sleep會在收到中斷的時候被“打醒”,nanosleep也會,但它的第二個參數可以返回還有多少時間才會到它正常的蘇醒。
8.11 readlink: 讀取symbolic links(readline: Reading Symbolic Links)
- readlink:三個參數,第一個是symbolic link的路徑,第二個是存放link目標的buffer,第三個是buffer的長度。
- 正常情況下,readlink會返回target字符串的長度。如果第一個參數不是symbolic link,返回-1。
- 注意:readlink返回的target字符串不是以’\0’結尾的,需要我們自己添上。
8.12 sendfile:快速數據傳輸(sendfile: Fast Data Transfers)
- 以前我們復制文件的時候怎么做?打開源文件,打開目標文件,申請一塊內存,從源文件讀,把該內存寫滿,再把內存的東西寫到目標文件。如此反復,直到所有源文件的內容都拷貝到目標文件為止。
- 現在不需要這種低效的方法了!也不需要額外的這塊內存,用sendfile你可以方便快捷的完成這種操作?。ㄔ趺锤杏X像促銷廣告?!)
- 需要注意的一點:sendfile參數中的文件描述符可以是磁盤文件,socket文件,或者其他設備。(不限于磁盤文件)
8.13 setitimer: 設置時間間隔(setitimer: Setting Interval Timers)
- 看標題大概不明白這個是干什么的。那么,alarm知道嗎?setitimer就是alarm的強化版。
-
簡單的說setitimer會設置時間間隔,在經過指定的時間后,會發出一個signal。setitimer和alarm不同的是,它有三種類型(由第一個參數指定):
- ITIMER_REAL類型:當經過指定的時間后,發出SIGALRM的signal(和alarm相同)
- ITIMER_VIRTUAL類型:當程序執行過指定的時間后(內核或者其他進程執行的時間不計算在內),發出SIGVTALRM的signal
- ITIMER_PROF類型:當程序已經被該程序所導致的內核代碼經過指定的時間后,發出SIGPROF的signal。
8.14 sysinfo: 獲取系統統計信息(sysinfo: Obtaining System Statistics)
- 很簡單,直接調用這個函數。參數類型是struct sysinfo*。這個結構的詳細信息?看man page去
8.15 uname
-
這個也是拿來獲取系統信息的。不過是獲取諸如網絡名,域名,操作系統版本等等。參數類型是struct utsname*,還是那句話,查 man page
轉自 Colar的共享空間
其他文章:
google_ad_client = "pub-2416224910262877"; google_ad_width = 728; google_ad_height = 90; google_ad_format = "728x90_as"; google_ad_channel = ""; google_color_border = "E1771E"; google_color_bg = "FFFFFF"; google_color_link = "0000FF"; google_color_text = "000000"; google_color_url = "008000";
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
