1. 程式人生 > >UNIX環境高階程式設計(1):UNIX基礎知識(1)

UNIX環境高階程式設計(1):UNIX基礎知識(1)

本系列文章是學習被譽為UNIX程式設計聖經的《UNIX環境高階程式設計》的讀書筆記。《UNIX環境高階程式設計》的英文全稱為《Advanced Programming in the UNIX Environment》,簡稱《APUE》,其作者是UNIX和網路技術領域的知名專家W.Richard Stevens。

本書描述了UNIX系統的程式設計介面:系統呼叫介面和標準C庫提供的很多函式。與大多數作業系統一樣,UNIX為程式執行提供了大量的服務--開啟檔案,讀檔案,啟動一個新程式,分配儲存區,獲取當前時間等等。這些服務被稱為系統呼叫介面(system call interface)。另外標準C庫提供了大量廣泛應用於C程式中的函式。

本書共分為6個部分:

(1)對UNIX程式設計基本概念和術語的簡要描述,以及對各種UNIX標準化工作和不同UNIX實現的討論;

(2)I/O:不帶緩衝的I/O,檔案和目錄,標準I/O庫和標準系統資料檔案;

(3)程序:UNIX程序的環境,程序控制,程序之間的關係和訊號;

(4)更多的I/0:終端I/O,高階I/O和守護程序;

(5)IPC:程序間通訊;

(6)例項;

接下來就正式進入《UNIX環境高階程式設計》的學習了。本章將從程式設計人員的角度快速瀏覽UNIX,對書中引用的一些術語和概念進行簡要的說明,後續文章再對這些概念作更詳細的說明。

UNIX體系結構:

在嚴格意義上,可將作業系統定義為一種軟體,它控制計算機硬體資源,提供程式執行環境。一般而言,我們稱這種軟體為核心(kernel)。 核心的介面被稱為系統呼叫(system call)。公用函式庫構建在系統呼叫介面之上。應用軟體既可以使用公用函式庫,也可以使用系統呼叫。

Shell是一種特殊的應用程式,它為執行其他的應用程式提供了一個介面。

在廣義上,作業系統包括核心和一些其它軟體,這些軟體使得計算機能夠發揮作用,這些軟體包括系統實用程式(system utilities),應用軟體,shell以及公用函式庫等。

例如,Linux是GNU作業系統使用的核心,僅僅是GNU作業系統的關鍵元件之一,GNU作業系統還包括很多其它的自由軟體,例如bash,Tex,GNU C庫等等。所以嚴格意義上這些作業系統的發行版應該稱為GNU/Linux,但是很多人將其簡稱為Linux。雖然這種表達方法不正確,但是“作業系統”本省就具有雙重含義,所以這也是可以理解的。關於GNU和Linux的關係,可以參考:

http://www.gnu.org/gnu/linux-and-gnu.en.html

登入:

登入名:使用者在登入UNIX系統時,需要輸入登入名及相應的口令。系統在其口令檔案(通常是/etc/passwd檔案)中檢視登入名。

shell:

shell:shell是一個命令列直譯器,它讀取使用者輸入,然後執行命令。使用者通常通過終端(互動式shell),有時則通過檔案(shell script)向shell進行輸入。使用者在登入後,系統從口令檔案中相應使用者登入項的最後一個欄位中瞭解到應該為該登入使用者執行哪一個shell。

檔案和目錄:

檔案系統:UNIX檔案系統是目錄和檔案組成的一種層次結構,目錄的起點稱為根(root),寫為"/"。目錄是一個包含許多目錄項的檔案,邏輯上,可以認為每個目錄項都包含一個檔名,同時還包含說明該檔案屬性的資訊。

檔名:目錄中的各個名字稱為檔名,不能出現在檔名中的字元只有斜線("/")和空字元(null)。斜線用來分隔構成路徑名的各檔名,空字元則用來終止一個路徑名。

在建立新目錄時,會自動建立兩個檔名:.(稱為點)和..(稱為點-點)。點指當前目錄,點點指父目錄。在根目錄中,點和點點相同。

現如今,幾乎所有商品化的UNIX系統都支援至少255個字元的檔名。

路徑名:一個或多個斜線分隔的檔名序列(也可以斜線開頭)構成路徑名。以斜線開頭的路徑名稱為絕對路徑,否則稱為相對路徑。相對路徑引用相當於當前目錄的檔案。

下列程式是ls(1)命令的簡要實現,用於列出某個目錄下的所有檔名。

/*
 * Copyright (C) [email protected]
 */


#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int
main(int argc, char *argv[])
{
	if (argc != 2) {
		printf("usage: ls directory_name\n");
		exit(1);
	}

	DIR *dp;
	struct dirent *dirp;
	
	if ( (dp = opendir(argv[1])) == NULL) {
		printf("can't open %s\n", argv[1]);
		exit(2);
	}
	while ( (dirp = readdir(dp)) != NULL) {
		printf("%s\n", dirp -> d_name);
	}
	
	closedir(dp);
	exit(0);
}

ls(1)這種表示方法是UNIX系統的慣用方法,表示引用UNIX手冊集中的某個特定項。ls(1)表示引用第一部分中的ls項。各部分通常用數字1-8表示,每個部分中的各項則按字母順序排列。在我的CentOS上,這八個部分分別為:


目前,大多數手冊都以電子文件的形式提供。所以如果使用的是聯機手冊,可以使用如下命令檢視ls命令手冊頁:man 1 ls 或 man -s1 ls。

由於不同的UNIX系統的目錄項的格式是不同的,因此上個程式採用opendir,readdir,closedir函式來對該目錄進行處理。關於該程式的更多細節將在後續文章進一步介紹。

工作目錄:每個程序都有一個工作目錄,有時將其稱為當前工作目錄。所有相對路徑名都從工作目錄開始解釋。

起始目錄:登入時,工作目錄設定為起始目錄,起始目錄從口令檔案中的相應使用者的登入項中取得。

輸入與輸出:

檔案描述符:檔案描述符通常是一個小的非負整數,核心用它表示一個特定程序正在訪問的檔案。當核心打一個已有檔案或建立一個新檔案時,它返回一個檔案描述符。之後讀寫檔案時,就可以使用該檔案描述符。

標準輸入、標準輸出、標準出錯:按照慣例,每當執行一個新程式時,shell就為其開啟三個檔案描述符:標準輸入、標準輸出、標準出錯。如果程式中沒有做什麼特殊處理,則這三個檔案描述符都鏈向終端。大多數shell也提供相應的方法,讓這三個檔案描述符重定向到某個檔案。

不帶緩衝的I/O:函式open,read,write,lseek以及close都提供了不用緩衝的I/O,這些函式都使用檔案描述符

下列程式將標準輸入複製到標準輸出:

/*
 * Copyright (C) [email protected]
 */


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define BUF_SIZE 4096

int
main(int argc, char *argv[])
{
	int n;
	char buffer[BUF_SIZE];

	while ( (n = read(STDIN_FILENO, buffer, BUF_SIZE)) > 0) {
		if (write(STDOUT_FILENO, buffer, n) != n) {
			printf("write error\n");
			exit(1); 
		}
	}

	if (n < 0) {
		printf("read error\n");
		exit(2); 
	}

	exit(0);
}

標頭檔案<unistd.h>以及兩個常量STDIN_FILENO以及STDOUT_FILENO都是POSIX標準的一部分。該標頭檔案包含了許多UNIX系統服務的函式原型。STDIN_FILENO以及STDOUT_FILENO分別指定了標準輸入和標準輸出的檔案描述符。它們的典型值是0和1,但是考慮到可移植性,最好還是使用識別符號。

在shell中執行該程式時,通過重定向,可以用於複製任何一個UNIX普通檔案。

標準I/O:標準I/O函式提供一種對不帶緩衝I/O函式的帶緩衝的介面。使用標準I/O函式可以無需擔心如何選取最佳的緩衝區大小,而且簡化了對輸入行的處理。

下列程式用標準I/O函式實現了將標準輸入複製到標準輸出:

/*
 * Copyright (C) [email protected]
 */


#include <stdio.h>
#include <stdlib.h>


int
main(int argc, char *argv[])
{
	int c;

	while ( (c = getc(stdin)) != EOF) {
		if (putc(c, stdout) == EOF) {
			printf("put char error\n");
			exit(1);
		}
	}

	if(ferror(stdin)) {
		printf("get char error\n");
		exit(2);
	}

	exit(0);
}

getc一次讀取一個字元,putc將該字元寫到標準輸出。程式中的標準輸入常量 stdin,標準輸出常量stdout,以及檔案結束符EOF,都定義在<stdio.h>中。

程式和程序

程式:程式是存在磁碟上、處於某個目錄中的可執行檔案。使用6個exec函式中的一個由核心將該程式讀入儲存器,並使其執行。

程序和程序ID:程式的執行例項被稱為程序。UNIX系統確保每個程序都有一個唯一的數字識別符號,稱為程序ID。程序ID總是一個非負整數。

下列程式通過呼叫getpid函式來獲取自己的程序ID:

/*
 * Copyright (C) [email protected]
 */


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
	printf("hello world from process ID : %d\n", getpid());
	exit(0);
}

程序控制:有三個用於程序控制的主要函式:fork,exec,waitpid。(exec函式有6種變體,但把它們統稱為exec函式)。

下列程式展示UNIX系統的程序控制功能,該程式從標準輸入中讀取命令,然後執行這些命令,是一個類似於shell的簡單實現:

/*
 * Copyright (C) [email protected]
 */


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#define MAX_LINE 32

int main(void)
{
	char buf[MAX_LINE];
	pid_t pid;
	int status;

	printf("%% ");
	while (fgets(buf, MAX_LINE, stdin) != NULL) {
		if (buf[strlen(buf) - 1] == '\n') {
			buf[strlen(buf) - 1] = '\0';
		}

		if ( (pid = fork()) < 0) {
			printf("fork error");
			exit(1);
		} else if (pid == 0) {
			/* child process */
			execlp(buf, buf, (char*)0);
			printf("can't execute %s\n", buf);
			exit(2);
		} 

		if ( (pid = waitpid(pid, &status, 0)) < 0) {
			printf("waitpid error\n");
			exit(3);
		}
		printf("%% ");
	}

	exit(0);
}

該程式最主要的部分就是呼叫fork函式建立一個新程序。新程序是呼叫程序的複製品,因此呼叫程序稱為父程序,新建立的程序為子程序。fork函式向父程序返回子程序的程序ID(非負),向子程序返回0。所以fork函式是呼叫一次,返回兩次(分別在父程序和子程序中)。

在子程序中,呼叫execlp函式執行從標準輸入讀入的命令。這就用新的程式檔案代替了子程序原先執行的程式檔案。而父程序呼叫waitpid函式等待子程序終止。

關於該程式的更多細節將在後續文章進一步學習。

執行緒和執行緒ID:

通常,一個程序只有一個控制執行緒。但是對於某些問題,如果不同部分各使用一個控制執行緒,那麼解決問題會更加容易,而且多個控制執行緒也能充分地利用多處理器系統的並行性。

在一個程序內所有執行緒共享同一地址空間,檔案描述符,以及相關的程序屬性。因為所有執行緒都能訪問同一儲存區,所以各執行緒在訪問共享資料時需要採取同步措施以避免不一致性。

執行緒也用ID標識,但是執行緒ID只在它所屬的程序內起作用。一個程序中的執行緒ID在另一個程序中並無意義。

在程序模型建立很久之後,執行緒模型才被引入UNIX系統中,這兩個模型間存在複雜的相互作用。

相關推薦

UNIX環境高階程式設計1UNIX基礎知識1

本系列文章是學習被譽為UNIX程式設計聖經的《UNIX環境高階程式設計》的讀書筆記。《UNIX環境高階程式設計》的英文全稱為《Advanced Programming in the UNIX Envi

UNIX環境高階程式設計5檔案I/O1

UNIX系統中的大多數檔案I/O只需要用到5個函式:open、read、write、lseek以及close。本章說明的函式經常稱為“不帶緩衝的I/0”,術語不帶緩衝指的是每個read和write都呼

UNIX環境高階程式設計的學習

UNIX系統程序控制程式說明 該程式從標準輸入讀取命令,然後執行這些命令,程式涉及到的函式主要用法包括: fgets的用法 execlp的用法 waitpid的用法 程式如下: #include "apue.h" #include <sys/wai

UNIX環境高階程式設計的學習

UNIX環境高階程式設計第一個例子的編譯 這本書中有很多的例子,為了加深理解,習慣性自己敲一遍程式碼然後看執行結果,再去理解其中的知識點,但是在虛擬機器下如何編譯這些程式碼呢,需要以下幾步: 下載並解壓縮apue.3e檔案包 在“apue.h”中最後一行加:#inclu

UNIX環境高階程式設計第三版 第五章筆記

5.2 流和物件 只有兩個函式可以改變流的定向: freopen函式清楚一個流的定向,fwide函式可用於設定一個流的定向。 #include <stdio.h> #include <wchar.h> int fwide(FILE

unix環境高階程式設計第三版-讀書筆記2

限制 unix系統實現定義了很多幻數和常量,其中有很多已被硬編碼到程式中,或用特定的技術確定。 已下兩種型別是必須的。 (1)編譯時限制(例如,短整型的最大值是什麼?) (2)執行時限制(例如,檔名有多少個字串?) 編譯時限制可在標頭檔案中定義。程式在編譯時包含這些標頭檔案。但是執行

UNIX環境高階程式設計學習筆記程序控制

1.程序標識 每個程序都有一個非負整型表示的唯一程序ID。因為程序ID識別符號總是唯一的,常將其用作其他識別符號的一部分以保證其唯一性。程序ID是可複用的,當一個程序終止後,其程序ID就成為複用的候選者。 ID為0的程序通常是排程程序,常常被稱為交換程序(s

檔案和目錄--unix環境高階程式設計

     普通檔案和目錄linux中最多的兩類檔案,linux中一共有七種型別的檔案,如下:1.普通檔案 2.目錄 3.字元特殊裝置 4.塊特殊裝置 5.FIFO,又叫命名管道 6.Socket,即套接字 7.符號連結 獲取一個檔案的詳細資訊可以使用stat函式組,stat

Unix環境高階程式設計》總結

程序環境 (第七章) 1、程序終止方式 正常終止: - main函式返回 - exit() - _exit() 或 _Exit() - 最後一個執行緒返回 - 最後一個執行緒呼叫pthread_exit() 異常終止: - abort() -

unix環境高階程式設計》 讀書筆記 5

date and time 涉及到的函式列出如下,然後再舉例執行,輸出結果,比較直觀。 時間這塊資料有限,如果有誤,還望指正。 #include <time.h>

[UNIX環境高階程式設計] 檔案和目錄

1 引言 上文圍繞了普通檔案I/O進行了討論——開啟檔案、讀檔案或寫檔案。本文將描述檔案系統的其他特徵和檔案的性質。將從stat函式開始,stat結構中的大多數成員都是基本系統資料型別,逐個分解stat結構的每一個成員以瞭解檔案的所有屬性。 使用stat函式最多的地方可能就是[ls -l

[Unix環境高階程式設計] 檔案I/O

1.引言 UNIX系統中的大多數檔案I/O只需要用到5個函式:open、read、write、lseek以及close,這裡所涉及到的函式經常被稱為不帶緩衝的I/O1。只要涉及在多個程序之間共享資源,原子操作的概念就變得非常重要,我們將通過I/O和open函式的引數來討論此概念,dup、

UNIX環境高階程式設計(三) 第六章

6 系統資料檔案和資訊 6.2 口令檔案 #include <pwd.h> struct passwd *getpwuid(uid_t uid); /* 檢視使用者登入名 */ struct passwd *getpwnam(const

UNIX環境高階程式設計(3) 第三章

3 檔案I/O 3.1 引言 3.2 檔案描述符 檔案描述符是一個標示,非負整數,類似於windows裡的控制代碼,為了與標準C保持一致(標準C裡的檔案的讀寫都是通過File Pointer)UNIX採用了這樣的三級結構,我混淆於檔案描述標誌和檔案

UNIX環境高階程式設計(3) 第八章

8 程序控制 8.1 引言 8.2 程序標識 #include <unistd.h> pid_t getpid(void); return: 呼叫程序的程序ID pid_t getppid(void

UNIX環境高階程式設計(3) 第二章

2.2UNIX標準化 2.2.1 ISO C 國際標準化組織(International Organization for Standardization,ISO) 國際電子技術委員會(International Electrotechnical Co

UNIX環境高階程式設計(3) 第一章

1.1 引言 所有作業系統都為他們所執行的程式提供服務。典型的服務包括:執行新程式、開啟檔案、讀檔案、分配儲存區以及獲取當前時間等。 1.2 UNIX體系結構 層級從裡向外擴充套件應用。 1. 核心:可將作業系統定義為一種軟體,即稱為核心,它控制

linux學習筆記1linux基礎知識

1.linux是什麼? Linux這個詞本身只表示Linux核心,但實際上人們已經習慣了用Linux來形容整個基於Linux核心,並且使用GNU 工程各種工具和資料庫的作業系統。 2. 什麼是shell 保護系統核心並對外提供操作介面的軟體 在linux中預設的

UNIX環境高階程式設計-概述

    檔案I/O 基本的系統呼叫函式 open,create,close,lseek,read,write 以及原子性的pread,pwrite 複製檔案描述符 dup 同步重新整理fsync,fdatasync,sync 更改已經開啟的檔案屬性 fcntl,雜物箱

UNIX環境高階程式設計》中涉及到一段程式碼,ourhdr.h檔案,以後備用!

#ifndef __ourhdr_h   #define __ourhdr_h   #include    <errno.h>               /*for definition of errno                      */ #inc