UNIX环境编程
linux函數分析查詢工具
1.優先推薦linux 中man命令
2.一個不錯的中文Linux手冊:http://cpp.ezbty.org/manpage
3.在線查英文Man手冊:
http://www.kernel.org/doc/man-pages/
http://man7.org/linux/man-pages/dir_all_alphabetic.html
http://linux.about.com/od/commands/l/blcmdl.htm
http://linux.die.net/man/
http://www.linuxmanpages.com/
man 命令
| 部分 | 內容 |
|---|---|
| man1 | 一般命令。這個部分中的命令通常不需要超級用戶(即管理員)特權。ls、cat 和 passwd 放在這里,還有 shell。例如,請試試 man bash。 |
| man2 | 用來訪問 UNIX 內核提供的服務的系統調用或函數。例如 fork 系統,它從一個現有的進程生成一個新進程。輸入 man fork 顯示它的手冊頁。使用系統軟件的程序員常常參考這個部分。 |
| man3 | C 庫函數。許多軟件包提供功能豐富的代碼庫,讓開發人員可以創建新軟件來補充現有的特性或開發全新的特性。每個庫通常有一個手冊頁;一些庫(比如系統的 libc)太大了,所以各個函數或一組相關函數有單獨的文檔。 |
| man4 | 特殊文件,比如設備和驅動程序。 |
| man5 | 文件格式。UNIX 幾乎完全使用文本配置文件定制系統的操作。有大量配置文件,包括網絡服務的列表 (/etc/services) 和可用的 shell 列表 (/etc/shells) 等等。 |
| man6 | 游戲和屏幕保護程序。 |
| man7 | 雜類文件。這是一個包羅萬象的類別。在傳統的系統上,可以了解 glob 操作符、正則表達式等方面的信息。 |
| man8 | 系統管理命令,超級用戶很可能要使用它們。 |
在某些情況下,不同部分中的組件可能名稱相同。這種現象很常見,尤其是在一個軟件包有多個部分的情況下。例如,第一部分中有 crontab 命令,它提交要調度的作業。同時,第五部分中有 crontab 文件格式,它描述要運行的作業。
為了區分不同部分中的同名組件,應該在第一個參數中提供部分號:
$ man 1 crontab $ man 5 crontab |
前一個命令顯示 crontab 命令的手冊頁;后一個命令顯示 crontab 文件格式。如果一個軟件在多個部分中存在,而您沒有指定部分號,man 就會顯示在編號最低的部分中找到的匹配。
使用-k的man命令可根據關鍵字搜索
#man -k fork
看第一行就是我們需要的信息,加上小節號
#man 2 fork
從這里我們知道在unistd.h中有fork
該頭文件在/usr/include中
#/usr/include
#vim? unistd.h
庫函數和系統調用的區別
庫函數是高層的,完全運行在用戶空間,為程序員提供調用真正的在幕后完成實際事務的系統調用的更方便的接口。系統調用在內核態運行并且由內核自己提供。標準C庫函數printf()可以被看做是一個通用的輸出語句,但它實際做的是將數據轉化為符合格式的字符串并且調用系統調用 write()輸出這些字符串。
是否想看一看printf()究竟使用了哪些系統調用? 這很容易,編譯下面的代碼。
#include <stdio.h>
int main(void)
{
printf("hello");
return 0;
}
使用命令gcc -Wall -o hello hello.c編譯或者直接cc編譯a.out文件。
用命令
#strace ./hello
或者
#strace ./a.out
跟蹤該可執行文件。
每一行都和一個系統調用相對應。 strace是一個非常有用的程序,它可以告訴你程序使用了哪些系統調用和這些系統調用的參數、返回值。 這是一個極有價值的查看程序在干什么的工具。在輸出的末尾,你應該看到這樣類似的一行 write(1, "hello", 5hello)。這就是我們要找的。藏在面具printf() 的真實面目。既然絕大多數人使用庫函數來對文件I/O進行操作(像 fopen, fputs, fclose)。 你可以查看man說明的第二部分使用命令man 2 write 。man說明的第二部分專門介紹系統調用(像kill()和read())。 man說明的第三部分則專門介紹你可能更熟悉的庫函數(像cosh()和random())。
1.getenv
NAME ? ? ? ??
getenv, secure_getenv - get an environment variable
SYNOPSIS ? ? ? ??
#include <stdlib.h>char *getenv(const char *name);char *secure_getenv(const char *name);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):secure_getenv(): _GNU_SOURCE
http://man7.org/linux/man-pages/man3/getenv.3.html
函數說明 getenv()用來取得參數name環境變量的內容。參數name為環境變量的名稱,如果該變量存在則會返回指向該內容的指針。環境變量的格式為name=value。
返回值:執行成功則返回指向該內容的指針,找不到符合的環境變量名稱則返回NULL。
2.putenv
NAME ? ? ? ??
putenv - change or add an environment variable
SYNOPSIS ? ? ? ??
#include <stdlib.h>int putenv(char *string);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):putenv(): _SVID_SOURCE || _XOPEN_SOURCEhttp://man7.org/linux/man-pages/man3/putenv.3.html
函數說明:putenv()用來改變或增加環境變量的內容。參數string的格式為name=value,如果該環境變量原先存在,則變量內容會依參數string改變,否則此參數內容會成為新的環境變量。
返回值:執行成功則返回0,有錯誤發生則返回-1。
錯誤代碼:ENOMEM 內存不足,無法配置新的環境變量空間。
3.setenv
NAME ? ? ? ??
setenv - change or add an environment variable
SYNOPSIS ? ? ? ??
#include <stdlib.h>int setenv(const char *name, const char *value, int overwrite);int unsetenv(const char *name);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):setenv(), unsetenv():_BSD_SOURCE || _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600http://man7.org/linux/man-pages/man3/setenv.3.html 函數說明: setenv()用來改變或增加環境變量的內容。
參數 name為環境變量名稱字符串。
參數 value則為變量內容。
參數 overwrite用來決定是否要改變已存在的環境變量。如果overwrite不為0,則改變環境變量原有內容,原有內容會被改為參數value所指的變量內容。如果overwrite為0,且該環境變量已有內容,則參數value會被忽略。
返回值: 執行成功則返回0,有錯誤發生時返回-1。
錯誤代碼: ENOMEM 內存不足,無法配置新的環境變量空間
#include <stdio.h>
#include <stdlib.h>
main()
{
char *p;
if((p = getenv("USER")))
printf("USER=%s\n",p);
putenv("USER=root");
printf("USER=%s\n",getenv("USER"));
setenv("USER","test",1);
printf("USER=%s\n",getenv("USER"));
unsetenv("USER");
printf("USER=%s\n",getenv("USER"));
}
輸出:
USER=root
USER=test
USER=test
USER=(null)
4.printf的buffer緩沖
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(void)
{int i;for(i=0; i<2; i++){fork();printf("-");}return 0;
}
注意:輸出8個-,而不是6個-
1.fork()系統調用是Unix下以自身進程創建子進程的系統調用,一次調用,兩次返回,如果返回是0,則是子進程,如果返回值>0,則是父進程(返回值是子進程的pid),這是眾為周知的。
2.還有一個很重要的東西是,在fork()的調用處,整個父進程空間會原模原樣地復制到子進程中,包括指令,變量值,程序調用棧,環境變量,緩沖區,等等。
因為printf(“-”);語句有buffer,所以,對于上述程序,printf(“-”);把“-”放到了緩存中,并沒有真正的輸出。在fork的時候,緩存被復制到了子進程空間,所以,就多了兩個,就成了8個,而不是6個。
Unix下的設備有“塊設備”和“字符設備”的概念,所謂塊設備,就是以一塊一塊的數據存取的設備,字符設備是一次存取一個字符的設備。磁盤、內存都是塊設備,字符設備如鍵盤和串口。塊設備一般都有緩存,而字符設備一般都沒有緩存。
對于上面的問題,我們如果修改一下上面的printf的那條語句為:printf("-\n");
或是
printf("-");fflush(stdout);
就沒有問題了(就是6個“-”了),因為程序遇到“\n”,或是EOF,或是緩中區滿,或是文件描述符關閉,或是主動flush,或是程序退出,就會把數據刷出緩沖區。需要注意的是,標準輸出是行緩沖,所以遇到“\n”的時候會刷出緩沖區,但對于磁盤這個塊設備來說,“\n”并不會引起緩沖區刷出的動作,那是全緩沖,你可以使用setvbuf來設置緩沖區大小,或是用fflush刷緩存。
參考:http://coolshell.cn/articles/7965.html
fflush用于清空緩沖流,雖然一般感覺不到,但是默認printf是緩沖輸出的。
fflush(stdout),使stdout清空,就會立刻輸出所有在緩沖區的內容。
fflush(stdout)這個例子可能不太明顯,但對stdin很明顯。
如下語句:
int a,c;
scanf("%d",&a);
getchar();
輸入:
12(回車)
那么 a=12 ,c= '\n'
而:
int a,c;
scanf("%d",&a);
fflush(stdin);
getchar();
輸入:
12(回車)
那么a=12, c暫時未得到輸入值,還需要再輸入c,因為getchar也是緩沖輸入,'\n'本還在緩沖區,但是被清空了。
另外fflush不能作用于重定向輸入流。
5.linux驅動程序中字符設備和塊設備的三點區別
1.字符設備只能以字節為最小單位訪問,而塊設備以塊為單位訪問,例如512字節,1024字節等
2.塊設備可以隨機訪問,但是字符設備不可以
3.字符和塊沒有訪問量大小的限制,塊也可以以字節為單位來訪問
參考:http://www.cnblogs.com/qlee/archive/2011/07/27/2118406.html總結
- 上一篇: 花生焖猪蹄怎么做好吃,花生焖猪蹄的家常做
- 下一篇: 一笔画问题【数据结构-图论】