Table of Contents
Table of Contents
Linux是使用C語言開發的,基於Linux平臺的應用程序開發,C語言是首選的開發語言。本章記錄C語言的基本概念/ 和基礎知識。
在設置Linux的系統路徑時,使用冒號分隔每個路徑名。如:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"
在Linux中的程序有兩種,一種是可執行程序,與Windows下的.exe文件類似,一種是腳本,與Windows下的.bat文件類似。
Linux中常用的程序存放路徑有以下幾個:
-
/bin,該路徑存放系統啓動時需要使用的程序。
-
/usr/bin,該路徑存放用戶需使用的標準程序。
-
/usr/local/bin,該路徑存放本地安裝的程序。
-
Linux使用斜槓"/"分隔路徑名,而不是Windows的反斜槓"/"。
-
Linux下的C編譯器使用GCC,由於歷史的原因,在POSIX兼容的操作系統中,C編譯器都叫cc,所以Linux下也有一個cc命令,它是一個到gcc的軟鏈接。
開發工具,多數位於/usr/bin或/usr/local/bin目錄下。
頭文件,位於/usr/include目錄。頭文件包含有常量定義、系統調用和庫函數調用的聲明。這是系統默認的頭文件存放路徑,在編譯程序時,編譯器會自動查找該目錄。gcc編譯器在編譯程序時也可用-I參數指定另外的頭文件路徑。如:
gcc -I/usr/local/myinclude test.c。
庫文件,庫是一組已編譯的函數集合,可方便我們重用代碼。默認存放在/lib和/usr/lib目錄。庫文件可分爲靜態和共享兩類。
-
.a,靜態庫文件。使用靜態庫將會把所有的庫代碼引入程序,佔用更多的磁盤空間和內存空間,所以一般建議使用共享庫。
-
.so,共享庫文件。使用共享庫的程序不包含庫代碼,只在程序運行才調用共享庫中的代碼。
在編譯時可用包含路徑的庫文件名或用-l參數指定使用的庫文件,/usr/lib/libm.a等價於-lm。如:
gcc -o hello hello.c /usr/lib/libm.a 或用-l參數寫成 gcc -o hello hello.c -lm
如果我們要使用的庫文件不在默認位置,在編譯程序時可用-L參數指定庫文件的路徑。下面例子使用了/usr/hello/lib目錄下的libhello庫文件:
gcc -o hello -L/usr/hello/lib hello.c -lhello
創建和使用靜態庫。
-
分別創建兩個函數,函數a的內容如下:
#include <stdio.h> void a(char *arg) { printf("function a,hello world %s/n",arg); }
函數b的內容如下:
#include <stdio.h> void b(int arg) { printf("function b,hello world %d/n",arg); }
-
接着,生成兩個對象文件。
debian:~/c# gcc -c a.c b.c debian:~/c# ls *.o a.o b.o
-
最後,用ar歸檔命令把生成的對象文件打包成一個靜態庫libhello.a。
debian:~/c# ar crv libhello.a a.o b.o r - a.o r - b.o
-
爲我們的靜態庫定義一個頭文件lib.h,包含這兩個函數的定義。
/* * this is a header file. */ void a(char *arg); void b(int arg); }}} * 創建jims.c程序,內容如下。{{{#!cplusplus #include "lib.h" int main() { a("jims.yang"); b(3); exit(0); }
-
利用靜態鏈接庫編譯程序。
debian:~/c# gcc -c jims.c debian:~/c# gcc -o jims jims.o libhello.a debian:~/c# ./jims function a,hello world jims.yang function b,hello world 3 debian:~/c#
gcc -o jims jims.o libhello.a也可以寫成gcc -o jims jims.o -L. -lhello。
預處理,在程序開頭以“#”開頭的命令就是預處理命令,它在語法掃描和分析法時被預處理程序處理。預處理有以下幾類:
-
宏定義,用#define指令定義。如:#define BUFFER 1024。取消宏定義用#undef指令。宏還可帶參數,如:
#define BUF(x) x*3
-
包含頭文件,用#include指令,可把包含的文件代碼插入當前位置。如:
<#include <stdio.h>。
包含的文件可以用尖括號,也可用雙引號,如:
#include "stdio.h"。
不同之處是,使用尖括號表示在系統的包含目錄(/usr/include)下查找該文件,而雙引號表示在當前目錄下查找包含文件。每行只能包含一個包含文件,要包含多個文件要用多個#include指令。
-
條件編譯,格式如下:
格式一,如果定義了標識符,則編譯程序段1,否則編譯程序段2: #ifdef 標識符 程序段1 #else 程序段2 #endif 格式二,如果定義了標識符,則編譯程序段2,否則編譯程序段1,與格式一相反: #ifndef 標識符 程序段1 #else 程序段2 #endif 格式三,常量表達式爲真則編譯程序段1,否則編譯程序段2: #if 常量表達式 程序段1 #else 程序段2 #endif
使用gcc編譯程序時,要經過四個步驟。
-
預處理(Pre-Processing),用-E參數可以生成預處理後的文件。
debian:~/c# gcc -E hello.c -o hello.i
-
編譯(Compiling)
-
彙編(Assembling)
-
鏈接(Linking)
GCC默認將.i文件看成是預處理後的C語言源代碼,所以我們可以這樣把.i文件編譯成目標文件。
debian:~# gcc -c hello.i -o hello.o}}}
在GCC中使用-pedantic選項能夠幫助程序員發現一些不符合ANSI/ISO C標準的代碼,但不是全部。從程序員的角度看,函數庫實際上就是一些頭文件(.h)和庫文件(.so或者.a)的集合。
在Linux系統內所有東西都是以文件的形式來表示的,除一般的磁盤文件外,還有設備文件,如硬盤、聲卡、串口、打印機等。設備文件又可分爲字符設備文件(character devices)和塊設備文件(block devices)。使用man hier命令可以查看Linux文件系統的分層結構。文件的處理方法一般有五種,分別是:
-
open,打開一個文件或設備。
-
close,關閉一個打開的文件或設備。
-
read,從一個打開的文件或者設備中讀取信息。
-
write,寫入一個文件或設備。
-
ioctl,把控制信息傳遞給設備驅動程序。
open,close,read,write和ioctl都是低級,沒有緩衝的文件操作函數,在實際程序開發中較少使用,一般我們使用標準I/O函數庫來處理文件操作。如:fopen,fclose,fread,fwrite,fflush等。在使用標準I/O庫時,需用到stdio.h頭文件。
一些常用的文件和目錄維護函數:chmod、chown、unlink、link、symlink、mkdir、rmdir、chdir、getcwd、opendir,closedir、readdir、telldir、seekdir等。
fcntl用於維護文件描述符,mmap用於分享內存。
創建文檔並輸入信息的示例代碼:
#include <stdio.h> main(void) { FILE *fp1; char c; fp1 = fopen("text.txt","w"); while ((c = getchar())!= '/n') putc(c,fp1); fclose(fp1); }
顯示路徑的示例代碼
#include <unistd.h> #include <stdio.h> #include <dirent.h> #include <string.h> #include <sys/stat.h> #include <stdlib.h> int main(int argc, char *argv[]) { char *topdir = "."; if (argc >= 2) topdir = argv[1]; printf("Directory scan of %s/n", topdir); printdir(topdir,0); printf("done./n"); exit(0); } printdir(char *dir, int depth) { DIR *dp; struct dirent *entry; struct stat statbuf; if((dp = opendir(dir)) == NULL) { fprintf(stderr,"cannot open directory:%s/n",dir); return; } chdir(dir); while((entry = readdir(dp)) != NULL) { lstat(entry->d_name,&statbuf); if(S_ISDIR(statbuf.st_mode)) { if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0) continue; printf("%*s%s//n",depth,"",entry->d_name); printdir(entry->d_name,depth+4); } else printf("%*s%s/n",depth,"",entry->d_name); } chdir(".."); closedir(dp); }
void main()表示程序沒有參數,int main(int argc, char *argv[])表示程序要帶參數,argc保存着參數的個數,argv[]數組保存着參數列表。如:
debian:~# mytest a b c argc: 4 argv: ["mytest","a","b","c"]
getopt()函數和getopt_long()用來處理程序選項。getopt_long()函數可以處理以"--"開頭的選項。Gnu官方手冊頁:http://www.gnu.org/software/libc/manual/html_node/Getopt.html
獲取命令行參數的示例代碼:
#include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { int opt; while((opt = getopt(argc,argv,"if:lr")) != -1) /* 返回“-1”表示已沒選項需要處理。*/ { switch(opt){ case 'i': case 'l': case 'r': printf("option: %c/n", opt); break; case 'f': printf("filename: %s/n", optarg); /*如果選項需要一個參數,則參數存放在外部變量optarg中。*/ break; case ':': printf("option needs a value /n"); /*“:”表示選項需要參數*/ break; case '?': printf("unknown option: %c/n", optopt); /*返回“?”表示無效的選項,並把無效的選項存放在外部變量optopt中。*/ break; } } for(; optind < argc; optind++) /*外部變量optind指向下一個要處理的選項索引值。*/ printf("argument: %s/n", argv[optind]); }
在bash shell中使用set命令可以列出Linux系統的環境變量,在C程序中我們也可以用putenv()和getenv()函數來獲取Linux系統的環境變量。這兩個函數的聲明如下:
char *getenv(const char *name); int putenv(const char *string);
系統有一個environ變量記錄了所有的系統變量。下面的示例代碼可把environ的值顯示同來。
#include <stdlib.h> #include <stdio.h> extern char **environ; int main() { char **env = environ; while(*env) { printf("%s/n",*env); env++; } }
linux和其它unix一樣,使用GMT1970年1月1日子夜作爲系統時間的開始,也叫UNIX紀元的開始。現在的時間表示爲UNIX紀元至今經過的秒數。
#include <time.h> time_t time(time_t *t); 顯示系統時間的示例代碼: #include <time.h> #include <stdio.h> #include <unistd.h> int main() { int i; time_t the_time; for(i = 1; i <= 10; i++){ the_time = time((time_t *)0); printf("%d the time is %ld/n", i, the_time); sleep(2); } }
用ctime()函數以友好方式返回當前時間,它的函數聲明格式:
#include <time.h> char *ctime(const time_t *timeval); 示例: #include <time.h> #include <stdio.h> int main(void) { time_t time1; (void)time(&time1); printf("The date is: %s/n",ctime(&time1)); }
用mkstemp()函數創建臨時文件。聲明:
#include<stdlib.h> int mkstemp(char * template); 示例: #include <stdio.h> int main(void) { char template[] = "template-XXXXXX"; int fp; fp = mkstemp(template); printf("template = %s/n", template); close(fp); }
獲取用戶信息。
聲明: #include <sys/types.h> #include <pwd.h> struct passwd *getpwuid(uid_t uid); /* 根據uid返回用戶信息 */ struct passwd *getpwnam(const char *name); /* 根據用戶名返回用戶信息 */ passwd結構體說明: passwd Member Description char *pw_name The user's login name uid_t pw_uid The UID number gid_t pw_gid The GID number char *pw_dir The user's home directory char *pw_gecos The user's full name char *pw_shell The user's default shell 示例代碼: #include <stdio.h> #include <sys/types.h> #include <stdio.h> #include <pwd.h> int main(void) { uid_t uid; gid_t gid; struct passwd *pw; uid = getuid(); gid = getgid(); pw = getpwuid(uid); printf("User is %s/n", getlogin()); printf("The uid is:%d/n", uid); printf("The gid is:%d/n",gid); printf("The pw struct:/n name=%s, uid=%d, gid=%d, home=%s,shell=%s/n", pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell); }
用gethostname()函數獲取主機名。
函數聲明: #include <unistd.h> int gethostname(char *name, size_t namelen); /* 主機名返回給name變量 */ 示例代碼: #include <stdio.h> #include <unistd.h> int main(void) { char computer[100]; int status; status = gethostname(computer, 100); printf("The status is %d/n", status); printf("The hostname is: %s/n", computer); }
用uname()函數獲取主機詳細信息,就像shell的uname命令返回的信息一樣。
函數聲明: #include <sys/utsname.h> int uname(struct utsname *name); utsname結構體說明: utsname Member Description char sysname[] The operating system name char nodename[] The host name char release[] The release level of the system char version[] The version number of the system char machine[] The hardware type 示例代碼: #include <stdio.h> #include <unistd.h> #include <sys/utsname.h> int main(void) { char computer[100]; int status; struct utsname uts; status = gethostname(computer,100); printf("The computer's size is %d/n",sizeof(computer)); printf("The status is %d/n", status); printf("The hostname is: %s/n", computer); uname(&uts); printf("The uname's information./n uts.sysname=%s/n uts.machine=%s/n uts.nodename=%s/n uts.release=%s/n uts.version=%s/n", uts.sysname,uts.machine,uts.nodename,uts.release,uts.version); }
使用syslog()函數處理日誌信息。
函數聲明: #include <syslog.h> void syslog(int priority, const char *message, arguments...); priority參數的格式(severity level|facility code) 示例: LOG_ERR|LOG_USER severity level: Priority Level Description LOG_EMERG An emergency situation LOG_ALERT High-priority problem, such as database corruption LOG_CRIT Critical error, such as hardware failure LOG_ERR Errors LOG_WARNING Warning LOG_NOTICE Special conditions requiring attention LOG_INFO Informational messages LOG_DEBUG Debug messages facility value(轉自syslog.h頭文件): /* facility codes */ #define LOG_KERN (0<<3) /* kernel messages */ #define LOG_USER (1<<3) /* random user-level messages */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ #define LOG_LPR (6<<3) /* line printer subsystem */ #define LOG_NEWS (7<<3) /* network news subsystem */ #define LOG_UUCP (8<<3) /* UUCP subsystem */ #define LOG_CRON (9<<3) /* clock daemon */ #define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */ #define LOG_FTP (11<<3) /* ftp daemon */ 示例代碼: #include <syslog.h> #include <stdio.h> int main(void) { FILE *f; f = fopen("abc","r"); if(!f) syslog(LOG_ERR|LOG_USER,"test - %m/n"); }
上面的日誌信息由系統自動給出,我們也可過濾日誌信息。用到以下函數:
#include <syslog.h> void closelog(void); void openlog(const char *ident, int logopt, int facility); int setlogmask(int maskpri); logopt參數的選項: logopt Parameter Description LOG_PID Includes the process identifier, a unique number allocated to each process by the system, in the messages. LOG_CONS Sends messages to the console if they can’t be logged. LOG_ODELAY Opens the log facility at first call to . LOG_NDELAY Opens the log facility immediately, rather than at first log. 示例代碼: #include <syslog.h> #include <stdio.h> #include <unistd.h> int main(void) { int logmask; openlog("logmask", LOG_PID|LOG_CONS, LOG_USER); /*日誌信息會包含進程id。*/ syslog(LOG_INFO, "informative message, pid=%d", getpid()); syslog(LOG_DEBUG,"debug message, should appear"); /*記錄該日誌信息。*/ logmask = setlogmask(LOG_UPTO(LOG_NOTICE)); /*設置屏蔽低於NOTICE級別的日誌信息。*/ syslog(LOG_DEBUG, "debug message, should not appear"); /*該日誌信息被屏蔽,不記錄。*/ }
不同安全級別的日誌信息存放在/var/log目錄下的哪個文件中是由/etc/syslog.conf文件控制的,下面是我係統中syslog.conf文件的內容:
# /etc/syslog.conf Configuration file for syslogd. # # For more information see syslog.conf(5) # manpage. # # First some standard logfiles. Log by facility. # auth,authpriv.* /var/log/auth.log *.*;auth,authpriv.none -/var/log/syslog #cron.* /var/log/cron.log daemon.* -/var/log/daemon.log kern.* -/var/log/kern.log lpr.* -/var/log/lpr.log mail.* -/var/log/mail.log user.* -/var/log/user.log uucp.* /var/log/uucp.log # # Logging for the mail system. Split it up so that # it is easy to write scripts to parse these files. # mail.info -/var/log/mail.info mail.warn -/var/log/mail.warn mail.err /var/log/mail.err # Logging for INN news system # news.crit /var/log/news/news.crit news.err /var/log/news/news.err news.notice -/var/log/news/news.notice # # Some `catch-all' logfiles. # *.=debug;/ auth,authpriv.none;/ news.none;mail.none -/var/log/debug *.=info;*.=notice;*.=warn;/ auth,authpriv.none;/ cron,daemon.none;/ mail,news.none -/var/log/messages # # Emergencies are sent to everybody logged in. # *.emerg * # # I like to have messages displayed on the console, but only on a virtual # console I usually leave idle. # #daemon,mail.*;/ # news.=crit;news.=err;news.=notice;/ # *.=debug;*.=info;/ # *.=notice;*.=warn /dev/tty8 # The named pipe /dev/xconsole is for the `xconsole' utility. To use it, # you must invoke `xconsole' with the `-file' option: # # $ xconsole -file /dev/xconsole [...] # # NOTE: adjust the list below, or you'll go crazy if you have a reasonably # busy site.. # daemon.*;mail.*;/ news.crit;news.err;news.notice;/ *.=debug;*.=info;/ *.=notice;*.=warn |/dev/xconsole