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);
}
Figure 1.4. List all the files in a directory
#include "apue.h"
#define BUFFSIZE 4096
int 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
標准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);
}
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.
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
#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標准定義了兩個函數,他們幫助打印出錯信息
#include
char *strerror(int errnum);
//此函數將errnum(它通常就是errno值)映射為一個出錯信息字符串,並且返回此字符串的指針。
//perror函數基於errno當前值,在標准出錯上產生一條出錯信息,然後返回
#include
void perror(const char* msg);
//它首先輸出由msg指向的字符串,然後是一個冒號,一個空格,接著是對應於errno值的出錯信息,最後是一個換行符
#include "apue.h"
Int main(void)
{
printf("uid = %d, gid = %d\n", getuid(), getgid());
exit(0);
}
由於是root用戶,所以uid是0.我也沒有分配組,所以gid也是0
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.提供一個信號,信號發生時則調用該函數,這被稱為捕捉該信號。我們需要提供自編的函數來處理它
系統調用實際上就是指最底層的一個調用,在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寫文件,都是先將內容寫到用戶空間緩沖區,當用戶空間緩沖區滿或者寫操作結束時,才將用戶緩沖區的內容寫到內核緩沖區,同樣的道理,當內核緩沖區滿或寫結束時才將內核緩沖區內容寫到文件對應的硬件媒介。