(UNIX基礎)
1 Unix體繫結構
vc/U2tfuuvOjqTwvcD4NCjxoMiBpZD0="2-列出一個目錄中所有檔案的名字也就是命令ls的實現">2 列出一個目錄中所有檔案的名字,也就是命令ls的實現
Figure 1.3. List all the files in a directory
#include "apue.h"#include int main(int argc, char *argv[]){ DIR *dp; struct dirent *dirp; if (argc != 2) err_quit("usage: ls directory_name"); if ((dp = opendir(argv[1])) == NULL) err_sys("can't open %s", argv[1]); while ((dirp = readdir(dp)) != NULL) printf("%s\n", dirp->d_name); closedir(dp); exit(0);}
3 不用緩衝的IO ( open, read, write, lseek, close )
Figure 1.4. List all the files in a directory
#include "apue.h"#define BUFFSIZE 4096int main(void){ int n; char buf[BUFFSIZE]; while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) if (write(STDOUT_FILENO, buf, n) != n) err_sys("write error"); if (n < 0) err_sys("read error"); exit(0);}
The constants STDIN_FILENO and STDOUT_FILENO are defined in
4 標準IO
標準IO函數提供一種對不用緩衝IO的帶緩衝介面。它有兩個優點:
1) 無須擔心如何選取最佳的緩衝區大小
2) 簡化了對輸入行的處理
The function getc reads one character at a time, and this character is written by putc. After the last byte of input has been read, getc returns the constant EOF (defined in
#include "apue.h"int main(void){ int c; while ((c = getc(stdin)) != EOF) if (putc(c, stdout) == EOF) err_sys("output error"); if (ferror(stdin)) err_sys("input error"); exit(0);}
5 列印進程ID
Figure 1.6. Print the process ID
#include "apue.h"int main(void){ printf("hello world from process ID %d\n", getpid()); exit(0);}
When this program runs, it calls the function getpid to obtain its process ID.
6 從標準輸入讀命令並執行
Figure 1.7. Read commands from standard input and execute them
#include "apue.h"#include Int main(void){ char buf[MAXLINE]; /* from apue.h */ pid_t pid; int status; printf("%% "); /* print prompt (printf requires %% to print %) */ while (fgets(buf, MAXLINE, stdin) != NULL) { if (buf[strlen(buf) - 1] == "\n") buf[strlen(buf) - 1] = 0; /* replace newline with null */ if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ execlp(buf, buf, (char *)0); err_ret("couldn't execute: %s", buf); exit(127); } /* parent */ //waitpid()會暫時停止目前進程的執行,直到有訊號來到或子進程結束。 if ((pid = waitpid(pid, &status, 0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0);}
We call fork to create a new process, which is a copy of the caller. We say that the caller is the parent and that the newly created process is the child. Then fork returns the non-negative process ID of the new child process to the parent, and returns 0 to the child. Because fork creates a new process, we say that it is called once by the parent but returns twice in the parent and in the child.
Fork()函數調用一次返回兩次,一次是自己的進程ID,另外一個是子進程,是0
7 Demonstrate strerror and perror
#include "apue.h"#include Int main(int argc, char *argv[]){ fprintf(stderr, "EACCES: %s\n", strerror(EACCES)); errno = ENOENT; perror(argv[0]); exit(0);}
C標準定義了兩個函數,他們協助列印出錯資訊
#includechar *strerror(int errnum);//此函數將errnum(它通常就是errno值)映射為一個出錯資訊字串,並且返回此字串的指標。//perror函數基於errno當前值,在標準出錯上產生一條出錯資訊,然後返回#includevoid perror(const char* msg);//它首先輸出由msg指向的字串,然後是一個冒號,一個空格,接著是對應於errno值的出錯資訊,最後是一個分行符號
8 Print user ID and group ID
#include "apue.h"Int main(void){ printf("uid = %d, gid = %d\n", getuid(), getgid()); exit(0);}
由於是root使用者,所以uid是0.我也沒有分配組,所以gid也是0
9 訊號
Figure 1.10. Read commands from standard input and execute them
#include "apue.h" #include static void sig_int(int); /* our signal-catching function */ Int main(void) { char buf[MAXLINE]; /* from apue.h */ pid_t pid; int status; if (signal(SIGINT, sig_int) == SIG_ERR) err_sys("signal error"); printf("%% "); /* print prompt (printf requires %% to print %) */ while (fgets(buf, MAXLINE, stdin) != NULL) { if (buf[strlen(buf) - 1] == "\n") buf[strlen(buf) - 1] = 0; /* replace newline with null */ if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ execlp(buf, buf, (char *)0); err_ret("couldn't execute: %s", buf); exit(127); } /* parent */ if ((pid = waitpid(pid, &status, 0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0); } void sig_int(int signo) { printf("interrupt\n%% "); }
訊號是通知進程已發生某種情況的一種技術。例如,若某一進程執行除法操作,其除數為0,則將名為SIDFPE(浮點異常)發送給進程。進程如何處理有三種選擇:
1.忽略該訊號。有些訊號表示硬體異常,例如除以0或者訪問地址空間以外的單元,這些異常產生的後果不確定,所以不推薦
2.按系統預設處理。對於除以0,系統預設終止進程
3.提供一個訊號,訊號發生時則調用該函數,這被稱為捕捉該訊號。我們需要提供自編的函數來處理它
10 系統調用與庫函數
系統調用實際上就是指最底層的一個調用,在linux程式設計裡面就是底層調用的意思。面向的是硬體。而庫函數調用則面向的是應用開發的,相當於應用程式的api
1.系統調用
系統調用提供的函數如open, close, read, write, ioctl等,需包含標頭檔unistd.h.以write為例:其函數原型為 size_t write(int fd, const void *buf, size_t nbytes),其操作對象為檔案描述符或檔案控制代碼fd(file descriptor),要想寫一個檔案,必須先以可寫入權限用open系統調用開啟一個檔案,獲得所開啟檔案的fd,例如 fd=open(\“/dev/video\”, O_RDWR)。fd是一個整型值,每新開啟一個檔案,所獲得的fd為當前最大fd加1(Linux系統預設分配了3個檔案描述符值:0-standard input,1-standard output,2-standard error)
系統調用通常用於底層檔案訪問(low-level file access),例如在驅動程式中對裝置檔案的直接存取。
系統調用是作業系統相關的,因此一般沒有跨作業系統的可移植性。
2.庫函數
標準C庫函數提供的檔案操作函數如fopen, fread, fwrite, fclose, fflush, fseek等,需包含標頭檔stdio.h.以fwrite為例,其函數原型為size_t fwrite(const void *buffer, size_t size, size_t item_num, FILE *pf),其操作對象為檔案指標FILE *pf,要想寫一個檔案,必須先以可寫入權限用fopen函數開啟一個檔案,獲得所開啟檔案的FILE結構指標pf,例如pf=fopen(\“~/proj/filename\”, \“w\”)。實際上,由於庫函數對檔案的操作最終是通過系統調用實現的,因此,每開啟一個檔案所獲得的FILE結構指標都有一個核心空間的檔案描述符fd與之對應。同樣有相應的預定義的FILE指標:stdin-standard input,stdout-standard output,stderr-standard error.
庫函數調用通常用於應用程式中對一般檔案的訪問。
庫函數調用是系統無關的,因此可移植性好。
由於庫函數調用是基於C庫的,因此也就不可能用於核心空間的驅動程式中對裝置的操作。
使用庫函數也有系統調用的開銷,為什麼不直接使用系統調用呢?
這是因為,讀寫檔案通常是大量的資料(這種大量是相對於底層驅動的系統調用所實現的資料操作單位而言),這時,使用庫函數就可以大大減少系統調用的次數。這一結果又緣於緩衝區技術。在使用者空間和核心空間,對檔案操作都使用了緩衝區,例如用fwrite寫檔案,都是先將內容寫到使用者空間緩衝區,當使用者空間緩衝區滿或者寫操作結束時,才將使用者緩衝區的內容寫到核心緩衝區,同樣的道理,當核心緩衝區滿或寫結束時才將核心緩衝區內容寫到檔案對應的硬體媒介。