上次忘了說,我的學習順序是按照《Beginning Linux Programming, 4th Edition》(以后簡稱《BLP》)這本書來的,同時參照官方文檔《The GNU C Library Reference Manual》(以后簡稱《GLIBC》)和 man。這次是第四章《The Linux Environment》的學習筆記。
程序參數
沒什么特別,主要是 int getopt(int argc, char** argv, const char* options)
和 int getopt_long(int argc, char* const* argv, const char* shortopts, const struct option* longopts, int* indexptr)
這兩個用于解析命令行參數的使用。只要按照規范定義參數,就可以很方便地進行解析。怎么 Java 就沒有提供類似的方法呢?
環境變量
要設置一個環境變量,《BLP》只講了 int putenv(char* string)
函數。根據《GLIBC》和 man 的描述,調用此函數后,string
就變成了環境的一部分,對它所做的任何更改,不論對鍵還是對值,都將自動反映到環境中,這就要求 string
的生命周期不能在該環境變量被刪除之前結束,否則可能出錯。例如:
char env[] = "a=b";
putenv(env);
printf("%s\n", getenv("a")); // 輸出“b”。
env[2] = 'z';
printf("%s\n", getenv("a")); // 輸出“z”。
env[0] = 'x';
// printf("%s\n", getenv("a")); // 程序崩潰……
printf("%s\n", getenv("x")); // 輸出“z”。
從《GLIBC》可以查到另兩個用起來更為安全可靠的函數: int setenv(const char* name, const char* value, int replace)
和 int unsetenv(const char* name)
。
可通過 extern char** environ
遍歷所有的環境變量,但是最好不要直接使用此變量進行迭代,而是創建一個副本,可避免影響程序的其他部分。
時間和日期
《BLP》只講了精確到秒的 time_t time(time_t* tloc)
。《GLIBC》指出 sys/time.h
中聲明了用來獲取更高精度的結構體 timeval
和函數 int gettimeofday(struct timeval* tp, struct timezone* tzp)
。timeval
定義了表示整秒數的成員 tv_sec
和表示剩余毫微秒數的成員 tv_usec
,其中 tv_usec
不超過一百萬,所以兩者加起來就是實際的時間。由于 timezone
結構體已被廢棄,而且不再被 GNU 支持,所以 gettimeofday
的第二個參數只能設為 NULL
,否則 errno
會被置為 ENOSYS
(Function not implemented)。例如:
time_t now = time(NULL);
struct timeval tvnow;
gettimeofday(&tvnow, NULL);
printf("%jd\n", (intmax_t) now); // 輸出“1304649729”。
printf("%jd.%06ju\n", (intmax_t) tvnow.tv_sec,
(uintmax_t) tvnow.tv_usec); // 輸出“1304649729.644344”。
臨時文件
最好不要先使用 char* tmpnam(char* s)
和 char* mktemp(char* template)
生成“隨機”字符串,再創建相應文件,因為在創建文件前,有可能其他程序也生成了相同的字符串,從而導致沖突。應當使用 FILE* tmpfile(void)
或 int mkstemp(char* template)
直接生成文件——前者的好處所創建的臨時文件在關閉時會被自動刪除,但卻無法取得文件名;后者能得到文件名,但必須手動刪除。
用戶信息
在我的 Debian 上,char* getlogin(void)
不能正確工作,errno
給出的原因是“No such file or directory”。具體哪個文件沒找到沒搞清楚,不過 man 已經指出此函數不安全,不用為妙。通過 uid_t getuid(void)
和 struct passwd* getpwuid(uid_t uid)
可以得到當前用戶的詳細信息。
主機信息
掌握 int uname(struct utsname* name)
就差不多了。
日志記錄
遺憾的是,Linux 本身只提供了寫入系統日志的函數,而沒有類似 java.util.Logger
那種通用接口。
資源和限制
除了偶爾測試一下算法的性能,也許只有專業性能分析工具才用得上這些東西吧。