POSIX標准總體分析
POSIX,全稱為可移植性操作系統接口,是一種關於信息技術的IEEE標准。它包括了系統應用程序接口(API),以及實時擴展(C語言)。
該標准的目的是定義了標准的基於UNIX操作系統的系統接口和環境來支持源代碼級的可移植性。現在,標准主要提供了依賴C語言的一系列標准服務,再將來的版本中,標准將致力於提供基於不同語言的規范。
該標准對核心需求部分定義了一系列任何編程語言都通用的服務,這一部分服務主要從其功能需求方面闡述,而非定義依賴於編程語言的接口。語言規范主要有兩部分組成。一部分包括了訪問核心服務的編程語言的標准接口,這些核心服務為標准中基於編程語言的核心需求部分所定義;另一部分包含了一個特殊語言服務的標准接口。基於任何語言,與該標准一致的執行都必須遵循語言規范的任何章節。
該標准一共被分為四個部分:
1) 陳述的范圍和一系列標准參考(第一章);
2) 定義和總概念;(第二章)
3) 各種接口設備;(第三章到第十七章)
4) 頭文件;(第十八章)
該標准的主要目的有:
1) 面向應用
2) 定義接口,而不是它的具體實現;
3) 涉及資源和可移植性,而非對象;
4) 基於c語言;
5) 無超級用戶,無系統管理;
6) 最小限度的接口,最小限度的定義;
7) 應用領域廣泛;
8) 對以前的實現進行最小限度改變;
9) 對原有程序代碼做最小的修改;
10) 實時擴展;
二 概述
2.1 范圍
定義范圍的關鍵要素有:
(1)定義足夠的一套功能適用於實時應用程序領域的重要部分;
(2)定義足夠的實現規范和性能相關的函數,以便允許實時應用程序完成系統的確定性的響應;
2.2 一致性
系統須支持標准中定義的接口,系統能夠提供標准中沒有要求到的函數和工具。在遵循於該標准的實現中,一種一致性文檔是需要用到的,它必須具有與該標准相同的結構,包含有全名,數字,和標准所指示的日期,以及頭文件<limits.h>和<unistd.h>中的界限值等等。該一致性文檔詳細說明了標准中定義的執行行為。該標准要求了應用程序的一致性,所有遵循標准的應用程序都使用基於C語言的服務。
2.3 定義
1) 術語
該標准中定義了一系列術語,如一致性文檔,被定義的實現,可能性,無用的特性等,還定義了一些通用名詞,如絕對路徑,存取模式,地址空間,適當權限,定時器,異步I/O操作,后台進程,后台進程組,塊文件,阻塞進程等等。
2) 基本概念
擴展安全控制;文件存取允許;文件級別;文件名可移植性;路徑名的決定;
3) 錯誤號
大部分函數都在外部變量errno中提供了錯誤號,定義如下:
extern int errno;
4) 簡單系統的數據類型
這些數據類型在頭文件<sys/types.h>中定義,它包含了至少以下類型:
dev_t |
用於設備號 |
gid_t |
用於進程標志符 |
ino_t |
用於文件序列號 |
inode_t |
用於一些文件參數 |
nlink_t |
用於連接內容 |
off_t |
用於文件大小 |
pid_t |
用於進程或進程組標志符 |
size_t |
在c標准(2)中定義 |
ssize_t |
用於返回字節數和錯誤標志的函數 |
uid_t |
用於用戶標志符 |
5) 環境描述
當一個進程開始執行時,將調用一個表示環境的字符串數組,這個數組為外部變量environ所指向,其定義如下:
extern char **environ;
6) 其他
在該章中,標准還總體介紹了c語言定義的一些標志符,數字方面的限制,以及一些符號常量,這些在以后的章節中都會一一出現。
三 進程原語
3.1 進程的創建和執行
1) 進程創建
函數原型:
#include <unistd.h>
pid_t fork(void)
函數功能:
調用時需要引用的頭文件是<unistd.h>,fork()創建了一個新的進程。子進程中返回0,父進程中返回子進程ID,出錯返回-1。
2) 執行一個文件
函數原型:
#include <unistd.h>
int exec1(const char *path, const char *arg, …);
int execv(const char *path, const *char argv[] );
int execle(const char *path, const char *arg, …);
int execve(const char *path, const *char argv[], char *const envp[]);
int execlp(const char *file, const char *arg. …);
int execvp(const char *file, char *const argv[]);
函數功能:
exec系列的函數用一個新的進程映像替代了當前的進程的正文、數據、堆和棧段,這個新的進程映像創建於一個規則的可執行文件,叫做新進程映像文件。執行成功無返回值因為調用進程映像以為新的進程映像所覆蓋。
3) 建立fork處理程序
函數原型:
#include <pthread.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
函數功能:
調用pthread_atfork(),建立fork處理程序,可以清除鎖狀態。
3.2 進程的終止
進程的終止有兩種情況:
(1)從main()函數返回時或者執行exit()或_exit()函數時正常的終止;
(2)被abort()函數請求或者接受到一些信號時不正常的終止;
1) 等待進程終止
函數原型:
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid,int *stat_loc.int options);
函數功能:
wait()和waitpid()允許調用進程獲得它的一個子進程的狀態信息。wait()函數將掛起調用進程直到獲得了它的子進程的狀態信息,或者是直到獲得一個終止進程信號;如果pid=-1並且options=0,waitpid()函數功能將和waitpid()相同,否則它的功能將依據pid和options的值而改變。
區別:在一個子進程終止前,wait()使其調用者阻塞,而waitpid()有一個選項,可使調用者不阻塞。waitpid()並不等待在起調用之后的第一個終止子進程,它有若干個選項,可以控制它所等待的進程。
2) 終止一個進程
函數原型:
#include <stdlib.h>
void _exit(int status);
函數功能:
_exit()函數將終止一個調用進程,該函數不能返回給其調用者。
說明:exit()和_Exit()是由ISO C說明的,而_exit()是由POSIX.1說明的。
3.3 信號
在頭文件<signal.h>中聲明了sigset_t類型和sigaction結構。完成所定義的信號分三類:必需的信號、任務控制信號、內存保護信號。分別如下表:
必需信號
符號常量 |
描述 |
SIGABRT |
非正常終止信號 |
SIGALRM |
超時信號 |
SIGFPE |
錯誤運算操作 |
SIGHUP |
為控制中斷所檢測到的掛斷 |
SIGILL |
無效硬件信號的檢測 |
SIGINT |
交互式信號 |
SIGKILL |
終止信號 |
SIGPIPE |
寫信號 |
SIGQUIT |
交互式終止信號 |
SIGSEGV |
無效內存引用檢測信號 |
SIGTERM |
終止信號 |
SIGUSR1 |
保留信號 |
SIGUSR2 |
保留信號 |
任務控制信號
符號常量 |
描述 |
SIGCHLD |
子進程終止或停止 |
SIGCONT |
停止后繼續 |
SIGSTOP |
停止信號 |
SIGTSTP |
交互式的停止信號 |
SIGTTIN |
從控制終端讀 |
SIGTTOU |
寫到控制終端 |
內存保護信號
符號常量 |
描述 |
SIGBUS |
獲取內存中不確定的部分 |
每一個進程有一個進程標記(process mask),它定義了一組產生但被阻塞傳遞的信號集。sigaction(),sigpromask(),sigsuspend()函數控制這個進程標記的行為。
1) 送一個信號到進程
函數原型:
#include <sys/types.h>
#include<signal.h>
int kill(pid_t pid, int sig)
函數功能:
該函數發送一個信號到一個由pid指明的進程或者進程組,sig標志了信號類型,其值是0或者上表中的值。如果發送成功,返回’0’,否則返回’1’。
2) 操縱信號集
函數原型:
#include<signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set,int signo);
函數功能:
sigemptyset()初始化由set指向的信號集,清除其中所有信號;
sigfillset()初始化由set指向的信號集,使其包括所有信號;
所有應用程序在使用信號集前,要對該信號集調用sigemptyset()和sigfillset()一次;
sigaddset()將一個信號添加到先有集中;
sigdelset()從信號集中刪除一個信號;
sigismember()測試一指定位。
3) 檢測和更改信號的行為
函數原型:
#include<signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
函數功能:
該函數允許調用函數檢查與確定的信號相聯系的行為,參數sig確定了信號,sigaction結構在頭文件<signal.h>中被定義,描述了所采取的行為。如果參數act不為null,它指向一個結構,它指定了與信號相聯系的行為。如果參數oact不為null,先前與信號相聯系的行為將被存儲到由oact指向的地方。
4) 檢查和改變阻塞信號
函數原型:
#include<signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
函數功能:
該函數sigprocmask可以檢測或更改其信號屏蔽字。首先,若oset是非空指針,那么進程的當前信號屏蔽字通過oset返回。其次,若set是一個非空指針,則參數how指示如何修改當前信號屏蔽字。如果set是空指針,則不改變該進程的信號屏蔽字,how的值也無意義。
5) 檢查未定的信號
函數原型:
#include<signal.h>
int sigpending(sigset_t *set);
函數功能:
該函數返回一個信號集,其中的各個信號對於調用進程是阻塞的而不能遞送,因而也一定是當前未決的。該信號集通過set參數返回。
6) 等待一個信號
函數原型:
#include<signal.h>
int sigsuspend(const sigset_t *sigmask);
函數功能:
該函數提供了在一個原子操作中先回復信號屏蔽字,然后使進程休眠。
將進程的信號屏蔽字設置為sigmask所指向的值。在捕捉到一個信號或發生了一個會中止該進程的信號之前,該進程被掛起。如果捕捉到了一個信號好而且從該信號處理程序返回,則sigsuspend()返回,並且該進行的信號屏蔽字設置為調用sigsuspend()之前的值。
注意,該函數沒有成功返回值。如果它返回到調用者,則總是返回-1,並將errno設置為EINTR(表示一個被中斷的系統調用)。
Sigsuspend()的另一種應用時等待一個信號處理程序設置一個全局變量。
7) 同步接受一個信號
函數原型:
#include<signal.h>
int sigwaitinfo(const sigset_t *set, siginfo_t *info);
int sigtimedwait(const sigset_t *set,siginfo_ *info, const struct timespec *timeout);
函數功能:
該函數從參數set所確定的信號集中選擇一個未定的信號出來。如果該函數成功,返回一個信號數;否則返回-1。
8) 排隊一個信號到進程
函數原型:
#include<signal.h>
int sigqueue(pid_t pid,int signo, const union sigval value);
函數功能:
該函數功能是使由signo確定的信號將參數value所確定的值發送到由pid指明的進程中去。
3.4 定時器操作
1) 調度警報
函數原型:
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
函數功能:
使用該函數可以設置一個計時器,在將來某個指定的時間該計時器會超時。當計時器超時時,產生SIGALRM信號。如果不忽略或者不捕捉該信號,則其默認動作時終止調用該函數的進程。
2) 掛起進程的執行
函數原型:
#include<unistd.h>
int pause(void);
函數功能:
該函數掛起一個調用進程直至捕捉到一個信號,這個信號或者執行信號跟蹤功能或者是終止該進程。如果是終止進程,該函數不返回;如果是執行信號跟蹤功能,則該函數在信號跟蹤函數返回后也要返回。
3) 延遲進程的執行
函數原型:
#include<unistd.h>
unsigned int sleep(unsigned int seconds);
函數功能:
該函數使當前進程從執行狀態轉化為掛起狀態,直到參數seconds所指定的一段實時時間過去后,或者是一個喚醒信號跟蹤功能或終止進程功能的信號到來。該掛起時間由於系統的其他調度活動可能會比要求的時間長。
四 進程環境
4.1 進程標志符
1) 獲得進程和父進程的ID
函數原型:
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
函數功能:
getpid()返回調用進程的進程ID,
getppid()返回調用進程的父進程ID.
4.2 用戶ID
1) 獲得真實用戶,有效用戶,真是組,有效組的ID
函數原型:
#include <unistd.h>
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
函數功能:
getuid()返回調用進程的真實用戶ID, geteuid()返回調用進程的有效用戶ID,getgid()返回調用進程的真實組ID,getegid()返回調用進程的有效組的ID。
2) 設置用戶和組的ID
函數原型:
#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
int seteuid(uid_t uid);
int setegid(gid_t gid);
函數功能:
這兩個函數分別根據進程的權限設置真實用戶ID,有效用戶ID,真實組ID,有效組ID。
3) 獲得輔助組ID
函數原型:
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
函數功能:
該函數在隊列的組列表中填入調用進程的輔助組ID。參數gidsetsize確定了組列表中的元素個數。
4) 獲得用戶名
函數原型:
#include <unistd.h>
char *getlogin(void)
函數功能:
該函數返回一個指針,指向與調用進程相關的用戶名。
4.3 進程組
1) 獲得進程組ID
函數原型:
#include <unistd.h>
pid_t getpgrp(void);
函數功能:
該函數返回調用進程的進程組ID。
2) 創建會話並且設置進程組ID
函數原型:
#include <unistd.h>
pid_t setsid(void)
函數功能:
如果調用進程不是進程組的組長,則該函數將創建一個新會話。結果將發生下面三件事:1、該進程成為一個新進程組的組長進程。此時,該新進程是新會話中唯一的進程。2、該進程成為一個新進程組的組長進程。新進程組ID是該調用進程的進程ID。3、該進程沒有控制終端。如果再調用setsid()之前該進程有一個控制終端,那么這種聯系也會被中斷。
3) 為作業控制設置進程組ID
函數原型:
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
函數功能:
如{_POSIX_JOB_CONTROL}被定義,則該函數用來加入已經存在的進程組或者創建一個新的進程組。
4.4 系統標志
1) 獲得系統名
函數原型:
#include<sys/utaname.h>
int uname(struct utsname *name);
函數功能:
該函數返回與當前主機與操作系統有關的信息。通過該函數的參數向其傳遞一個utsname結構的地址,然后改函數填寫此結構。
struct utsname{
char sysname[]; /*name of the operation system*/
char nodename[];/*name of this node*/
char release[]; /*current release of operating system*/
char version[]; /*current version of this release*/
char machine[]; /*name of hardware type*/
};
4.5 時間
1) 得到系統時間
函數原型:
#include<time.h>
time_t time(time_t *calptr);
函數功能:
該函數返回自從公元1970年1月1日00:00:00以來的某個時間值,以秒為單位。參數calptr指向一個時間值所存儲的地方。
2) 獲得進程時間
函數原型:
#include<sys/times.h>
clock_t times(struct time *buffer);
函數功能:
參數buffer指向一個結構,該函數向該結構填寫有關時間的信息。clock_t和tms結構在<sys/times.h>中定義。
4.6 環境變量
1) 獲取環境
函數原型:
#include<stdlib.h>
char *getenv(const char *name);
函數功能:
該函數在環境列表查找字符串name=value,返回指向value的指針。如果沒有找到,則返回null。
2) 設置環境
函數原型:
#include<stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
函數功能:
putenv()取形式為name=value的字符串,將其放到環境表中。如果name已經存在,則先刪除其原來的定義。
setenv()將name設置為value。如果再環境中name已經存在,那么若rewrite非0,則首先刪除其現有的定義,若rewrite為0,則先不刪除現有定義。
unsetenv()刪除name的定義。
4.7 終端標志
1) 產生終端路徑
函數原型:
#include<stdio.h>
char *ctermid(char *s);
函數功能:
該函數產生一個字符串,作為路徑名,提交到當前進程的當前控制終端, 用來確定控制終端的名字。此函數的主要作用是幫助提高向其他操作系統的可移植性。
2) 確定終端設備名
函數原型:
#include <unistd.h>
int isatty(int fildes);
char *ttyname(int fildes);
函數功能:
ttyname()返回一個指針指向一個字符串,它包含了該文件描述符上打開的終端設備的路徑名;
如果fildes是一個有效的與終端聯系的文件描述符,isatty()返回’1’,否則返回’0’。
4.8 可配置的系統變量
1) 獲得可配置的系統變量
函數原型:
#include<unistd.h>
long sysconf(int name);
函數功能:
該函數提供了一個應用方法來決定可配置系統變量的當前值。參數name代表了所查詢的系統變量。
五 文件和目錄
5.1 目錄
1) 目錄的入口形式
頭文件<dirent.h>定義了一個結構和目錄程序用到的類型,沒有確定的文件內部格式。readdir()返回了一個指針指向一個類型對象struct dirent。
2) 目錄操作
函數原型:
#include<sys/types.h>
#include<dirent.h>
dir *opendir(const char *dirname);
struct dirent *readdir(dir *dirp);
void rewinddir(dir *dirp);
int closedir(dir *dirp);
函數功能:
opendir()根據參數dirname打開一個目錄流;readdir()返回一個指針,它指向一個目錄流中當前位置的目錄入口,目錄流由參數dirp指向;rewinddir()重置目錄流的位置到目錄的起點;closedir()關閉目錄流,如成功,則返回“0”值。
5.2 工作目錄
1) 改變當前的工作目錄
函數原型:
#include<unistd.h>
int chdir(const char *path);
函數功能:
path指向當前目錄的路徑名。該函數使命名的目錄成為當前的工作目錄。
2) 獲得工作目錄路徑名
函數原型:
#include<unistd.h>
char *getcwd(char *buf,size_t size);
函數功能:
該函數復制當前工作目錄的絕對路徑名到buf所指向的隊列中。
5.3 基本文件的創建
1) 打開一個文件
函數原型:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int open(const char *path, int oflag,…);
函數功能:
open()在文件和文件描述符之間建立了一個連接,它創建了一個指向一個文件的打開文件描述,參數path指向文件的路徑名。
2) 創建一個新文件或者重寫一個已經存在的文件
函數原型:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int creat(const char *path, mode_t mode);
函數功能:
該函數創建一個新文件。調用creat(path, mode)相當於open(path, o_wronly|o_creat|o_trunc,mode);
3) 設置文件的創建標記
函數原型:
#include<sys/types.h>
#include<sys/stat.h>
mode_t umask(mode_t cmask);
函數功能:
umask()設置進程的文件模式創建標記到cmask,並且返回原來的標記值。
4) 鏈接到一個文件
函數原型:
#include<unistd.h>
int link(const char *existingpath ,const char *newpath);
函數功能:
參數existingpath指向路徑名來命名存在文件,參數newpath指向一個路徑名,它命名了一個創建的新的目錄入口。該函數為已存在的文件自動的創建一個新的連接,並且將文件連接數加1。如果newpath已經存在,則返回出錯。
5) 創建一個符號鏈接
函數原型:
#include <unistd.h>
int symlink(const char *path1, const char *path2);
函數功能:
該函數創建了一個指向path1的新目錄項path2,在創建此符號鏈接時,不需要求path1已經存在。
6) 打開符號鏈接
函數原型:
#include <unistd.h>
int symlink(const char *restrict path, char *restrict buf, size_t bufsize);
函數功能:
打開符號鏈接,此函數組合了open,read,close的所有操作。如果此函數成功執行,則返回讀入buf的字節數。
5.4 文件創建
1) 生成一個目錄
函數原型:
#include<sys/types.h>
#include<sys/stat.h>
int mkdir(const char *path,mode_t mode);
函數功能:
該函數依據參數path創建一個新的目錄。新目錄的允許位根據mode初始化。
2) 創建一個FIFO類型的文件
函數原型:
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
函數功能:
mkfifo()創建一個新的fifo類型文件,它由pathname指向的路徑名命名。
FIFO有下面兩種用途:1、FIFO由shell命令使用以便將數據從一條管道線傳到另一條,為此無需創建中間臨時文件。2、FIFO用於客戶進程-服務器進程應用程序中,以在客戶進程和服務器進程之間傳遞數據。
5.5 文件的移動
1) 移動目錄入口
函數原型:
#include<unistd.h>
int link(const char *existingpath ,const char *newpath);
函數功能:
該函數刪除目錄項,並將有pathname所引用文件的鏈接計數減1。如果還有指向該文件的其他鏈接,則仍可以通過其他鏈接訪問該文件的數據。如果出錯,則不對該文件做任何更改。
2) 移去一個目錄
函數原型:
#include<unistd.h>
int rmdir(const char *path)
函數功能:
移除一個路徑為path的目錄。
3) 重命名一個文件
函數原型:
int rename(const char *old,const char *new);
函數功能:
該函數改變一個文件的名字,參數old指向被重命名文件的路徑名,參數new指向文件的新路徑名。
5.6 文件特征
1) 獲得一個路徑下的信息
函數原型:
#include <sys/stat.h>
int stat(const char *restrict path, struct stat *restrict buf);
函數功能:
該函數獲得路徑下的文件信息,並把信息保存在buf緩沖區內。
2) 獲得文件信息
函數原型:
#include <sys/stat.h>
int fstat(int fildes, struct stat *buf);
int lstat(const char *restrict path, struct stat *restrict buf);
函數功能:
fstat()函數獲得一個已經打開的文件的信息,並存入緩沖區buf內;lstat()函數獲得一個符號鏈接的文件的信息,並保存在buf緩沖區內。
3) 檢查文件的權限
函數原型:
#include <unistd.h>
int access(const char *path, int amode);函數功能:
函數功能:
該函數檢查路徑下的文件權限,若權限正確返回0,否則返回-1。
5.7 可配置文件變量
1) 改變文件權限變量
函數原型:
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fildes,, mode_t mode);
函數功能:
chmod()和fchmod()函數改變路徑下的文件權限變量mode,若修改正確,返回0,否則返回-1。不同的是chmod()是在制定的文件上進行操作,而fchmod()則對已打開的文件進行操作。
2) 更改文件的用戶ID和組ID
函數原型:
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fildes, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);
函數功能:
除了所引用的文件時符號鏈接外,這三個函數的操作相似。在符號鏈接的情況下,lchown()更改符號鏈接本身的所有者,而不是該符號鏈接所指向的文件。
3) 更改文件訪問和修改時間
函數原型:
#include <utime.h>
int utime(const char *path, const struct utimbuf *times);
函數功能:
此函數使用的數據結構是:
struct utimbuf{
time_t actime;
time_t modtime;
}
調用函數后得到文件訪問和修改的日歷時間。
4) 文件截短
函數原型:
#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fildes, off_t length);
函數功能:
這兩個函數將把現有的文件長度階段為length字節。如果該文件以前的長度大於length,則超過length以外的數據就不再能訪問了。函數ftruncate()是POSIX.1的組成部分,truncate()函數式POSIX.1功能的XSI擴展部分。
5) 文件路徑名變量配置
函數原型:
#include <unistd.h>
long fpathconf(int fildes, int name);
long pathconf(const char *path, int name);
函數功能:
配置文件路徑名變量。
六 輸入與輸出源語
6.1 管道
1) 創建內進程通道
函數原型:
#include<unistd.h>
int pipe(int filedw[2]);
函數功能:
該函數創建一個管道並且創建兩個文件描述符,一個是fildes[0],一個是fildes[1],它們分別指的是‘讀’與‘寫’的管道端。
6.2 文件描述符控制
1) 復制一個打開文件描述符
函數原型:
#include<unistd.h>
int dup(int fildes);
int dup2(int fileds,int fileds2);
函數功能:
這兩個函數為fcntl()服務提供了兩個選擇的接口,用到了F_DUPFD命令。
fid = dup(fildes);相當於fid = fcntl(fildes, F_DUPFD, 0);
fid = dup2(fildes, fildes2);相當於close(fildes2);fid = fcntl(fildes, F_DUPFD, fildes2);
6.3 撤除文件描述符
1) 關閉一個文件
函數原型:
#include<unistd.h>
int close(int fildes);
函數功能:
關閉一個文件。關閉一個文件時還會釋放該進程加載該文件上的所有記錄鎖。當一個進程終止時,內核自動關閉它所有打開的文件。
6.4 輸入和輸出
1) 文件讀
函數原型:
#include<unistd.h>
ssize_t read(int fildes,void *buf,size_t nbyte);
函數功能:
從打開文件中讀數據。如read成功,則返回讀到的字節數。如果已到達文件結尾,則返回0。
2) 文件寫
函數原型:
#include<unistd.h>
ssize_t write(int fildes,const void *buf,size_t nbyte);
函數功能:
該函數向打開的文件寫數據。
6.5 一個文件的控制操作
1) 文件控制操作的數據定義
頭文件<fcnt1.h>為fcntl()和open()定義了一下的要求和參數:
fcntl()的_cmd值
常量 |
描述 |
F_DUPFD |
復制文件描述符fileds |
F_GETFD |
對應於filedes的文件描述符標志作為函數值返回 |
F_GETLK |
判斷由flockptr所描述的鎖是否會被另外一把鎖所排斥 |
F_SETFD |
對於filedes設置文件描述符標志 |
F_GETFL |
對應於filedes的文件狀態標志作為函數值返回 |
F_SETFL |
將文件狀態標志設置為第三個參數的值(即為整形值) |
F_SETLK |
設置由flockptr所描述的鎖 |
F_SETLKW |
這是F_SETLK的阻塞版本(w表示等待(wait)) |
F_GETOWN |
取當前接收SIGIO和SIGURG信號的進程ID或進程組ID |
F_SETOWN |
設置接收SIGIO和SIGURG信號的進程ID或進程組ID |
2) 文件控制
函數原型:
#include <sys/types.h>
#inclued<unistd.h>
#include<fcntl.h>
int fcntl(int fildes,int cmd,…);
函數功能:
fcntl()為打開的文件提供了一系列控制,參數fildes是一個文件描述符。cmd決定了控制的內容。
fcntl()有5種功能:1、復制一個現有的描述符(cmd=F_DUPFD)2、獲得/設置文件描述符標記(cmd=F_GETFD或F_SETFD)3、獲得/設置文件狀態標志(cmd=F_GETFL或F_SETOWN)4、獲得/設置一部I/O所有權(cmd=F_GETOWN或F_SETOWN)5、獲得/設置記錄鎖(cmd=F_GETLK、F_SETLK或F_SETLKW)。
3) 讀/寫文件偏移量的重新定位
函數原型:
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fildes,off_t offset,int whence);
函數功能:
lseek()為fildes所指定的文件重新設置偏移量。
若whence是SEEK_SET,則將該文件的偏移量設置為距文件開始處offset個字節。
若whence是SEEK_CUR,則將該文件的偏移量設置為其當前值加offset,offset可為正或負。
若whence是SEEK_END,則將該文件的偏移量設置為文件長度加offset,offset可為正或負。
若lseek()成功執行,返回新的文件偏移量。
6.6 文件同步
1) 文件的狀態同步
函數原型:
#include<unistd.h>
int fsync(int fildes);
函數功能:
該函數用來暗示所有的文件描述數據被傳輸到了存儲設備上。該函數值對由文件描述符filedes指定的單一文件起作用,並且帶寫磁盤操作結束,然后返回。
2) 文件數據的同步
函數原型:
#include<unistd.h>
int fdatasync(int fildes);
函數功能:
該函數迫使當前所有排隊的I/O操作進入同步 I/O狀態。該函數類似於函數fsync(),但它只影響文件的數據部分。
6.7 異步輸入與輸出
1) 異步輸入與輸出的數據定義
頭文件<aio.h>能使<sys/types.h>,<signal.h>,<time.h>和<fcntl.h>中的符號可見。
異步I/O控制塊
異步I/O控制塊結構aiocb在許多異步I/O接口中使用,它在<aio.h>中定義。
2) 異步讀
函數原型:
#include<aio.h>
int aio_read (struct aiocb *aiocbp);
函數功能:
aiocbp->io_nbytes,表示讀的字節數;aiocbp->aio_fildes,表示讀的文件;aiocbp->aio_buf,表示讀到的緩沖區。
3) 異步寫
函數原型:
#include<aio.h>
int aio_write(struct aiocb *aiocbp);
函數功能:
參數表示同上。
4) 列出直接I/O
函數原型:
#include<aio.h>
int lio_listio(int mode,struct aiocb *const list[],int nent,struct sigevent *sig);
函數功能:
該函數允許用一個函數調用初始化一個I/O請求列表。
5) 得到異步I/O操作的錯誤狀態
函數原型:
#include<aio.h>
int aio_error(const struct aiocb *aiocbp);
函數功能:
該函數返回aiocbp指向的結構所表示的錯誤狀態。
6) 得到異步I/O操作的返回狀態
函從數原型:
#include<aio.h>
ssize_t aio_return(struct aiocb *aiocbp);
函數功能:
得到異步I/O操作的返回狀態。
7) 刪除異步I/O請求
函數原型:
#include<aio.h>
int aio_cancel (int fildes,struct aiocb *aiocbp);
函數功能:
參數fildes是文件描述符,參數aiocbp指向異步I/O控制塊上的請求刪除部分。
8) 等待異步I/O請求
函數原型:
#include<aio.h>
int aio_suspend(const struct aiocb *const list[],int nent,const struct timespec *timeout);
函數功能:
標准定義該函數掛起調用進程直到至少一個list指向的異步I/O操作完成,或者一個信號中斷了一個函數,或者超時了(timeout指定)。
9) 異步文件同步化
函數原型:
#include<aio.h>
int aio_fsync(int op,struct aiocb *aiocbp);
函數功能:
該函數迫使所有與(參數aiocbp指向的)結構aiocb中aio_fildes所指定的文件相關異步I/O操作進入同步狀態。
七 設備和面向類的函數
7.1 基本的終端接口
1) 接口特性
●當一個終端文件被打開,通常它將引起進程等待直到連接被建立。
●進程組一個終端可以具有與它相關的前台進程組,它發揮特定的角色,后面會講到。
●控制終端
●終端存取控制
●輸入操作和讀進程
●規范的輸入操作
●非規范模式的輸入操作
●寫數據和輸出處理
●特殊的符號(INTR,QUIT,ERASE,KILL…)
●modem斷掉連接
●關閉終端設備文件
2) 可設置的參數
●termios機構
該結構在<termios.h>中定義,在控制特定的終端I/O特性中要用到。
●輸入模式
termios c_iflag
標記名 |
描述 |
BRKINT |
信號中斷 |
ICRNL |
輸入時將CR映射到NL |
IGNBRK |
忽略中斷狀態 |
IGNCR |
忽略CR |
IGNPAR |
忽略奇偶錯誤 |
INLCR |
輸入時將NL映射到CR |
INPCK |
輸入奇偶校驗使能 |
ISTRIP |
Strip字符 |
IXOFF |
開始/停止輸入控制使能 |
IXON |
開始/停止輸出控制使能 |
PARMRK |
產生奇偶錯誤 |
●輸出模式
termios c_oflag
標記名 |
描述 |
BSDLY |
退格延遲屏蔽 |
CMSPAR |
標記或空奇偶性 |
CRDLY |
CR延遲屏蔽 |
FFDLY |
換頁延遲屏蔽 |
NLDLY |
NL延遲屏蔽 |
OCRNL |
將輸出的CR轉換為NL |
OFILL |
對於延遲使用填充符 |
OLCUC |
將輸出的小寫字符轉換為大寫字符 |
ONLCR |
將NL轉化為CR-NL |
ONLRET |
NL執行CR功能 |
ONOCR |
在0列不輸出CR |
ONOEOT |
在輸出中刪除EOT(^D)字符 |
OPOST |
執行輸出處理 |
OXTABS |
將制表符擴充為空格 |
TABDLY |
水平制表符延遲屏蔽 |
VTDLY |
垂直制表符延遲屏蔽 |
●控制模式
termios c_cflag
標記名 |
描述 |
CBAUDEXT |
擴充的波特率 |
CREAD |
啟用接收裝置 |
CSIZE |
字符大小屏蔽 |
CS5 |
5位 |
CS6 |
6位 |
CS7 |
7位 |
CS8 |
8位 |
CSTOPB |
送兩個停止位,否則為1位 |
HUPCL |
在最后的關閉中掛起 |
PARENB |
奇校驗使能 |
PARODD |
奇校驗或偶校驗 |
●本地模式
termios c_lflag 值
標記名 |
描述 |
ECHO |
進行回送 |
ECHOE |
可見擦除符 |
ECHOK |
回送kill符 |
ECHONL |
響應’/n’ |
ICANON |
規范輸入 |
IEXTEN |
啟用擴充的輸入字符處理 |
ISIG |
啟用中斷產生的信號 |
NOFLSH |
中斷,停止或掛起后關掉flush |
TOSTOP |
為后台輸出發送SIGTTOU |
●特殊的控制字符
這些特殊的控制字符值在隊列c_cc中定義,分為規范和非規范兩種模式。
●波特率值
3) 波特率函數
函數原型:
#include<termios.h>
speed_t cfgetospeed(const struct termios *termios_p);
int cfsetospeed(struct termios *termios_p,speed_t speed);
speed_t cfgetispeed(const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p,speed_t speed);
函數功能:
以上這些接口被用來在termios結構獲得和設定輸入與輸出的波特率值。
7.2 基本的終端接口控制函數
1) 獲得並設定狀態
函數原型:
#include<termios.h>
int tcgetattr(int fildes,struct termios *termios_p);
int tcsetattr(int fildes,int optional_actions,const struct termios * termios_p);
函數功能:
tcgetattr()獲得fildes所確定的文件的參數並將其存儲在t_erops_p所指向的結構中;tcsetattr()將設置參數。
2) 行控制函數
函數原型:
#include<termios.h>
int tcsendbreak(int fildes,int duration);
int tcdrain(int fildes);
int tcflush(int fildes,int queue_selector);
int tcflow(int fildes,int action)’
函數功能:
如果終端使用異步連續數據傳輸,tcsendbreak()引起在一段時間內連續的‘0’位傳輸;tcdrain()等待直到輸出傳輸完畢;tcflush()函數刷清輸入緩沖區或輸出緩沖區;tcflow()用於對輸入和輸出流控制進行控制。
3) 獲得和設置終端屬性
函數原型:
#include<termios.h>
int tcgetattr(int filedes, stuct termios *termptr);
int tcsetattr(int filedes, int opt, const struct termios *termptr);
函數功能:
這兩個函數都有一個指向termios結構的指針作為其參數,它們返回當前終端的屬性,或者設置該終端的屬性。
八 基於C語言的服務
8.1 C語言輸入/輸出函數
1) 設置程序環境
函數原型:
#include <locale.h>
char *setlocale(int category, const char *locale);
函數功能:
該函數設置程序運行環境。
2) 映射一個流指針到一個文件描述符
函數原型:
#include<stdio.h>
int fileno(FILE *stream);
函數功能:
每個標准I/O流都有一個與其相關聯的文件描述符,可以對一個流調用fileno函數以獲得其描述符。該函數是POSIX.1支持的擴展。
3) 根據一個文件描述符打開一個流
函數原型:
#include<stdio.h>
FILE *fdopen(int fildes,const char *type);
函數功能:
該函數獲取一個現有的文件描述符(我們可能從open,dup,dup2,fcntl,pipe,socket,socketpair或accept函數得到此文件描述符),並使一個標准的I/O流與該描述符相結合。該函數常用於由創建管道和網絡通信通道函數返回的描述符。
4) 其他文件類型函數之間的相互作用
一個單一的文件描述說明可以通過流和文件描述符訪問,流或者是文件描述符被稱作位打開文件的句柄,一個打開文件說明可以有多個句柄。句柄可以在不影響重要的打開文件說明的情況下被創建和刪除,創建如fcntl(),dup(),fdopen(),fileno(),fork();刪除如fclose(),close()。
a) fopen()隨着open()的調用分配描述符,基礎函數為open()。
b) fclose():該函數完成在與FILE流相關的,對文件描述符的close()功能。
c) freopen():具有fclose()和fopen()的特點。
d) fflush():如果流可寫或者緩存數據還沒有寫入文件時,該函數標記下基礎文件st_ctime和st_mtime的值。
e) fgetc(),fgets(),fread(),getc(),getchar(),gets(),scanf(),fscanf():這些函數標記更新的st_atime值。 基礎函數是read()和lseek()。
f) fputc(),fputs(),fwrite(),putc(),putchar(),puts(),printf(),fprintf():從以上任一個函數的成功執行到下一個調用(在同一個流中的fflush()或fclose()或exit()或abort()),記下更新的st_ctime和st_mtime值。基礎函數是write()和 lseek()。
g) fseek(),rewind():如果流可寫或者緩存數據還沒有寫入文件時,該函數標記下文件更新的st_ctime和st_mtime值。基礎函數是lseek()和write()。
h) perror():記下與標准錯誤流相關的文件。
i) tmpfile():fopen()執行后為文件分配文件描述符。
j) ftell(): 基礎文件是lseek()。執行fflush()后執行該函數的結果與執行fflush前執行該函數的結果相同。
k) 報錯
l) exit(),abort():exit()終止進程的時候要關閉流, abort()只終止進程對流沒有影響。
5) 文件操作remove()函數
該函數和unlink()函數的功能一樣。
8.2 其他的C函數
1) 非局部跳轉
函數原型:
#include<setjmp.h>
int sigsetjmp(sigjmp_buf env,int savemask);
void siglongjmp(sigjmp_buf env,int val);
函數功能:
sigsetjmp()宏要與標准中setjmp()宏的定義一致,如果參數savemask不為“0”,sigsetjmp()就要保存當前的信號標記作為調用環境的一部分。siglongjmp()同理。
2) 設置時間域
函數原型:
#include<time.h>
void tzset(void);
函數功能:
該函數用環境變量TZ的值來設定時間變化信息。
九 系統數據庫
9.1 系統數據庫
本章描述了兩個數據庫:組數據庫和用戶數據庫。
組數據庫包括的信息有:組名,組的數字ID,組中的用戶列表;
用戶數據庫包含的信息有:用戶名,用戶的數字ID,組的數字ID,初始化的工作目錄和初始化的用戶程序。
9.2 數據庫的訪問
1) 組數據庫的訪問
函數原型:
#include<sys/type.h>
#include<grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
函數功能:
getgrid()和getgrnam()返回指針,它指向一個struct group類型的對象,包含了組數據庫的入口。
2) 用戶數據庫的訪問
函數原型:
#include<sys/types.h>
#include <pwd.h>
struct paswd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
函數功能:
getpwuid()和getpwnam()返回一個指針,指向struct passwd類型的一個對象,它包含了用戶數據庫的入口。
十 信號量
10.1信號量特征
頭文件<semaphore.h>定義了sem_t類型,它用於信號量操作中。sem_t代表了信號量,用文件描述符能夠實現這些信號量,應用程序能夠打開至少{OPEN_MAX}這么多的文件和信號量。標准中,頭文件<semaphore.h>能使頭文件<sys/types.h>和<fcntl.h>中的符號可見。
為了獲得共享資源,進程需要執行下列操作:
(1) 測試控制該資源的信號量
(2) 若此信號量的值為正,則進程可以使用該資源。進程可以使用該資源。進程將信號量只減1,表示它使用了一個資源單位。
(3) 若此信號量的值為0,則進程進入休眠狀態,直至信號量值大於0.進程被喚醒后,它返回至第(1)步。
10.2信號量函數
1) 初始化一個未命名的信號量
函數原型:
#inxlude<semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
函數功能:
該函數被用來初始化sem引用的未命名信號量。在成功調用該函數以后,在接下來的sem_wait(),sem_trywait(),sem_post(),sem_destroy()的調用中,該信號量將被用到。如果參數pshared不為零,信號量將在進程中被共享,任何可以訪問信號量sem的進程都可以使用sem。只有sem能被用來進行同步。如果參數pshared為零,則結構不確定。如果標准中未定義,可以由執行者來支持該函數。
2) 刪除一個未命名信號量
函數原型:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
函數功能:
該函數用來刪除sem引用的未命名信號量,只有在sem_init()中創建的信號量才能被該函數刪除。
3) 初始化/打開一個命名信號量
函數原型:
#include<semaphore.h>
sem_t *sem_open(const char *name,int oflag,…);
函數功能:
該函數在進程和命名信號量之間創建一個鏈接。接着調用帶有信號量名name的該函數,進程引用與name相關的信號量。該信號量在一些函數的調用中用到,如sem_wait(),sem_trywait(),sem_post,和sem_close()。信號量一直可用知道調用函數sem_close(),_exit,exec()關閉它。參數oflag控制是否信號量由sem_open()創建或者僅被它訪問。
4) 關閉一個命名信號量
函數原型:
#include<semphore.h>
int sem_close(sem_t *sem);
函數功能:
該函數用來提示調用進程已經完成使用sem所指明的信號量。該函數釋放系統資源,這些資源被擁有該信號量的進程占有。
5) 移走一個命名信號量
函數原型:
#include<semaphore.h>
int sem_unlink(const char *name);
函數功能:
該函數將移走被字符串name命名的信號量。如果該信號量當前被其他進程引用,則該函數對信號量狀態沒有影響。如果一個和更多的進程打開了該信號量,則銷毀該信號量被延遲知道所有的信號量被sem_close(),_exit(),exec關閉。
6) 鎖定一個信號量
函數原型:
#include<semaphore.h>
int sem_wait (sem_t*sem);
int sem_trywait(sem_t *sem);
函數功能:
sem_wait()鎖定sem引用的一個信號量,對該信號進行鎖定操作。如果信號量為’0’,調用進程將不會返回直到鎖定了這個信號量或者被一個信號中斷。Sem_trywait()只能在信號量當前沒被鎖定的情況下鎖定它,否則將不會鎖定信號量。成功返回,信號量的狀態將被鎖定直到sem_post()被調用並且成功返回。
7) 解鎖一個信號量
函數原型:
#include<semaphore.h>
int sem_post(sem_t *sem);
函數功能:
該函數通過對一個信號量的解鎖操作解鎖一個被sem引用的信號量。如果該操作的結果,信號量值為正數,則沒有進程被鎖定來等待一個信號量解鎖,信號量值是單一的增加。如果信號量值為’0’,則進程被鎖定來等待一個信號量被允許從sem_wait()成功返回。
8) 取得一個信號量值
函數原型:
#include<semaphore.h>
int sem_getvalue(sem_t *sem,int *sval);
函數功能:
該函數更新參數sval所引用的位置,在不改變信號量狀態的情況下得到信號量值。更新的值代表了一個確切的值,它產生在調用中的一個不定時刻,但它返回給調用進程是不一定需要信號量的確定值。如果sem被鎖定,則sem_getvalue()返回的值為0,或者負數,它代表了等待信號量的進程數。
十一 內存管理
該章節描述了進程內存鎖定,內存映象文件和共享內存工具。
內存鎖定和內存映象文件以頁的方式定義。執行者可以據據頁大小的規范限制和排列鎖定和映象范圍。頁的大小,以字節為單位,是一個可配置系統變量。默認為1B。
內存鎖定保證了一部分地址空間的駐留。
11.1內存鎖定函數
1) 鎖定/解鎖進程地址空間
函數原型:
#include<sys/mman.h>
int mlockall(int flags);
int munlockall(void);
函數功能:
mlockall()使進程地址空間所映射的所有頁面成為內存駐留區,直到解鎖或者進程退出或者execs另一個進程映象。參數flags決定了是否被鎖定的頁面是由當前,(將來,或者兩者都是)的進程地址空間映射的。Munlockall()解鎖當前所有的進程地址空間映射頁面。所有映射到進程地址空間的頁面,調用了該函數,將不會被鎖定,除非有中斷調用mlockall()確定MCL_CURRENT,或者並發調用mlockall()確定MCL_CURRENT。
2) 鎖定/解鎖一連續的地址空間
函數原型:
#include<sys/mman.h>
int mlock(const void *addr,size_t len);
int munlock(const void *addr,size_t len);
函數功能:
mlock()使一個范圍的進程地址空間成為內存駐留區(addr決定了空間的起始地址,len決定了空間長度)直到解鎖或者進程退出或者execs另一個進程映象;munlock()解鎖一個范圍的進程地址空間(addr決定了空間的起始地址,len決定了空間長度);
11.2內存映象函數
1) 映象進程地址到一個內存目標
函數原型:
#include<sys/mman.h>
void *mmap(void *addr,size_t len,int prot,int flags,int fildes,off_t off);
函數功能:
該函數在一個進程的地址空間和內存對象間創建一個映射,調用格式如:pa=mmap(addr,len,prot,flags,fildes,off);pa代表進程進程地址空間的地址,由執行者定義(addr和flags),len表示空間長度,fildes代表內存對象,off表示偏移量。函數成功調用返回pa,空間地址范開始與pa,長度為連續的len個字節;內存對象開始與off,長度為len字節。參數prot決定了讀,寫,執行,或一些映射數據的訪問活動的集合。
2) 取消以前映射地址的映射關系
函數原型:
#include<sys/mman.h>
int munmap(void *addr,size_t len);
函數功能:
該函數移去任何包含有進程地址空間的頁面的映射關系,該進程地址空間起始於addr,長度為len字節。
3) 改變內存保護
函數原型:
#include <sys/mman.h>
int mprotet(const void *addr,size_t len,int prot);
函數功能:
該函數更改訪問的保護活動,由參數prot確定。訪問對象是一部分進程地址空間,起始於addr,長度為len字節。
4) 內存對象同步
函數原型:
#inxlude<sys/mman.h>
int msync(void *addr,size_t len,int flags);
函數功能:
該函數將所有更改了的數據寫到擁有存儲的地方,它包含了進程地址空間,起始於addr,長度為len字節。如果沒有這樣的存儲區域存在,則給函數沒有作用。該函數須保證寫操作的完成符合所定義的同步I/O數據的一致完成性。參數flags確定了寫的同步/異步性。
11.3共享內存函數
1) 打開一個共享內存對象
函數原型:
include<sys/mman.h>
int shm_open(const *name,int oflag,mode_t mode);
函數功能:
該函數在共享內存對象和文件描述符之間創建了一個連接。它創建了一個打開文件描述涉及到共享內存對象,並且創建了一個文件描述符指向這個打開文件描述。這個文件描述符可悲其他函數用來指向共享內存對象,參數name指向共享內存對象名。如函數調用成功,則返回一個共享內存的文件。文件狀態和文件訪問模式根據oflag的值來設定。
2) 移去一個共享內存對象
函數原型:
#include <sys/mman.h>
int shm_unlink(const char *name);
函數功能:
該函數移去由name命名的共享內存對象。如果一個或者多個對共內存的引用在對象斷開連接的同時退出的話,這個名字應在shim_unlink()返回之前移走,但是對內存對象的移走須延遲到所有對共享內存對打開和映射引用被移去后。
十二 執行調度
12.1調度參數
一個調度參數結構sched_param包括了調度策略所支持的執行者所需要的調度參數,它在頭文件<sched.h>中定義。執行者可根據規對該結構進行擴展。
12.2調度策略
該章所闡述的調度術語是從概念模型上定義的,它包含了一組進程列表。這個模型只討論了可運行進程的處理器調度,但是它注重了在其他資源考慮到處理器調度策略的情況下,增強了實時操作的可預見性。在這里,概念上講是一個進程列表一個策略。調度策略的目的就是對這一組列表定義允許的操作(例如,在進程列表之中和之間移動進程)。每一個進程應由相關的調度策略和優先級,於每一個策略相關的是一個優先級范圍。
1) SCHED_FIFO
該策略是一種先進先出的調度策略。如果正在執行的進程是被搶占的進程,則他應該在進程列表頭;如果一個鎖定的進程變成可執行進程,它就進入進程列表尾;當執行進程調用sched_setscheduler(),則函數中確定的進程被改為指定的策略;當執行進程調用sced_setparam(),進程的優先級根據參數param被修改,如果改進程是正在執行和可執行,則它將進入進程列表尾;當正在執行的進程調用seced_yield(),進程進入列表尾。
2) SCHED_RR
該策略與上面的策略不同的是,如果執行中檢測到進程的執行時間已經到達或超過了sched_rr_get_interval()所返回的時間片,進程就進入列表尾並且進程列表頭移動一個進程進入執行狀態。
3) SCHED_OTHER
12.3進程調度函數
1) 設置調度參數
函數原型:
#include<sched.h>
int sched_setparam(pid_t pid,const struct sched_param *param);
函數功能:
pid指明了進程,param指向了sched_param結構,該結構設定了進程的調度參數。如果pid=0,調度參數為調用進程設定。如果pid指定的進程的優先級高於正在指向的進程並且該進程可執行,則搶占現在正在運行的進程。如果當前的策略不是前面將的三種方式,則由執行者定義。
2) 取得調度參數
函數原型:
#include<sched.h>
int sched_getparam(pid_t,struct sched_param *param);
函數功能:
該函數返回調度參數,如果一個pid進程退出並且調用進程允許,則ID等於pid的進程返回其調用參數。如果pid=0,則返回調用進程的調度參數。
3) 設置調度策略和調度參數
函數原型:
#include<sched.h>
int sched_setscheduler(pid_t pid,int policy,const struct sched_param *param);
函數功能:
該函數設置進程的調度策略和調度參數,pid表示進程,policy指明策略,參數param指向的sched_param結構指明了調度參數。執行者可要求請求進程能允許設定自己或其他進程的調度參數。
4) 取得調度策略
函數原型:
#include<sched.h>
int sched_getscheduler(pid_t pid);
函數功能:
該函數返回pid進程的調度策略,其返回值在頭文件<sched.h>中定義。
5) 放棄處理器
函數原型:
#include<sched.h>
int sched_yield(void);
函數功能:
該函數迫使正在執行進程放棄處理器直到重新進入進程列表頭。
6) 獲得調度參數范圍
函數原型:
#incude<sched.h>
int sched_get_priority_max(int policy);
int sched get_priority min(int policy);
int sched_rr_get_interval(pid_t pid, struct timespec *interval);
函數功能:
sched_get_priority_max()和sched_get_priority_min()返回policy調度策略相應的最大最小值。Sched_rr_get_interval()更新interval參數引用的timespec結構,包含了當前進程執行的時間限制。
十三 時鍾和定時器
13.1時鍾和定時器的數據定義
頭文件<time.h>定義了時間設備用到的類型和主要常量。
1) 時間值的規格結構
一個時間值結構timespec確定了單一時間值並且包括了以下值:
成員類型 |
成員名 |
描述 |
time_t |
tv_sec |
seconds |
long |
tv_nsec |
nanosenconds |
執行者可對他做擴展,tv_nsed成員要大於等於零時才可用。
時間值結構itimerspec確定了一個初始化的定時器和各進程定時器函數用到的重復間隔。結構包括:
成員類型 |
成員名 |
描述 |
Struct timespec |
It_interval |
Timer period |
Struct timespec |
It_value |
Timer expiration |
執行者也可以對其做擴展。
2) 定時器活動喚醒控制塊
為了使完成支持實時信號擴展功能,各進程定時器被創建通過排列實時擴展信號來通知定時器超時的進程。Sigevent結構在頭文件<signal.h>中定義,用來創建一個這樣的定時器。
3) 類型定義
在<sys/types.h>中定義。
4) 主要常量
在<time.h>中定義:
CLOCK_REALTIME 系統范圍的實時時鍾標志
TIMER_ABSTIME flag指定的時間對於時鍾是“完全”的
13.2時鍾和定時器函數
1) 時鍾
函數原型:
#include<time.h>
int clock_settime(clockid_t clock_id,const struct timespec *tp);
int coock_gettime(clockid_t clock_id,struct timespec*tp);
int clock_getres(clockid_t clock_id,struct timespec *res);
函數功能:
clock_settime()設置特定時鍾,clock_id,到tp指定的值。
Clock_gettime()返回當前的tp值,即時鍾,clock_id;
clock_getres()可以得到時鍾的確定值,該值由執行者定義而不由進程設定。如果res不為空,則該值被存儲在res指向的地方,如果res為空,則時鍾決定值不被返回。
2) 創建一個總進程定時器
函數原型:
#include<signal>
#include<time.h>
int timer_create(clockid_t clock_id,struct sigevent *evp,timer_t *timerid);
函數功能:
timer_create()創建一個總進程定時器,用來指明時鍾,clock_id,作為計時基礎。該函數在timerid指向的地方返回一個timer_t類型的定時器ID,該ID在調用進程中必須是唯一的直到定時器被刪除。參數evp如果不為空,則指向一個sigevent結構,定義了定時器超時時出現的異步通知。結構中sigev_notify為SIGEV_SIGNAL時,結構就包括了超時時送往進程的信號數和應用數據;如果sigev_notify為SIGEV_NONE,則不發送任何通知;sigev_notify其他的值由完成者自行定義。總進程的定時器不被其子進程通過fork()繼承,它應該被exec撤銷。
3) 刪除一個總進程定時器
函數原型:
#include<time.h>
int timer_delete(time_t timerid);
函數功能:
該函數刪除一個指定的定時器,timerid,該定時器是在以前的timer_create()中創建的。
4) 總進程定時器
函數原型:
#include<time.h>
int timer_settime(timer_t timerid,int flags,const struct itimerspec *value,struct itimerspec *ovalue);
int timer_gettime(timer_t timerid,struct itimerspec *value);
int timer_getoverrun(timer_t timerid);
函數功能:
timer_settime()設置時間直到下一個timerid指定的定時器終止。如果該函數調用時定時器已經被裝備,該調用則重置時間直到value確定的下一次終止。如果value的it_value成員為零,定時器被解除。如果flag沒有設定為TIMER_ABSTIME,則定時器從調用開始在it_value十億分之一秒內超時;如果設定為TIMER_ABSTIME,該函數表現為時間直到下一次超時被設定為it_value指定的絕對時間和與timerid相聯的時鍾值的差值。定時器的再裝由value的it_interval成員值來設定。如果參數ovalue不為空,該函數被存儲在ovalue引用的地方。該函數要存儲時間累計值直到timerid指定的定時器終止或重裝定時器值到value參數。在任一時刻,僅有一個單一信號被派了在指定的定時器的進程中。如果支持實時信號擴展,timer_getoverrun()返回定時器終止的溢出值。
5) 高度睡眠
函數原型:
#include<time.h>
int nanosleep(const struct timespec *rqtp,struct timespec *rmtp);
函數功能:
該函數使當前執行的進程掛起直到參數rptp指定的時間間隔到達或者信號被送到調用進程並且其行為就是喚醒信號跟蹤功能或者使終止進程。掛起時間也許要比要求時間長是因為參數值向上舍入成一個整數或者是因為系統對其他活動的調度。但是除非被信號打斷,掛起時間不會少於tqtp值,這是系統時鍾,CLOCK_REALTIME測量的。
十四 消息傳遞
14.1消息隊列的數據定義
1) 數據結構
頭文件< mqueue.h>定義了以下執行者定義的類型:
mqd_t :用於消息隊列描述符
頭文件< mqueue.h>定義了以下執行者定義的結構:
mq_attr用來設定消息隊列參數。
struct sigevent結構包含了至少以下的成員:
類型 |
成員名 |
描述 |
long |
mq_flags |
消息隊列標志 |
long |
mq_maxmsg |
最大消息數 |
long |
mq_msgsize |
消息的最大長度 |
long |
mq_curmsgs |
當前排列的消息數 |
14.2消息傳遞函數
1) 打開消息隊列
函數原型:
#include<mqueue.h>
mqd_t mq_open(const char *name,int oflag,…);
函數功能:
該函數在進程和消息隊列之間建立連接。它創建了一個消息隊列描述符指向消息隊列。參數oflag請求對消息隊列發送或接收所需信息。如果調用進程承認對相應保護文件的讀寫請求,則對接收或發送消息的請求允許被通過。
2) 關閉一個消息隊列
函數原型:
#include<mqueue.h>
int mq_close(mqd_t mqdes);
函數功能:
該函數撤銷消息隊列描述符(mqdes)和消息隊列之間的關系。如果進程成功的配屬了mqdes表示的消息隊列通知請求,則這個配屬關系被撤銷,該消息隊列可用於其他進程來配屬通知。
3) 移去一個消息隊列
函數原型:
#include<mqueue.h>
int mq_unlink(const char *name);
函數功能:
該函數移去路徑名name指向的消息隊列。如果該調用成功,並且flag沒有設為O_CREATE,則mq_open()對於同一個name將會失敗。所有對該隊列的引用撤銷后,該隊列才能被關閉。該函數調用不會被阻塞指定所有的引用被關閉。
4) 發送一個消息到消息隊列
函數原型:
#include<mqueue.h>
int mq_send(mqd_t mqdes,const char *msg_ptr,size_t mag_len,unsigned int msg_prio);
函數功能:
該函數添加參數msg_ptr指向的消息到mqdes指定的消息隊列中去。參數msg_len表示消息長度,以字節為單位,該參數應小於等於消息隊列的mq_msgsize參數,否則調用失敗。如果消息隊列沒有滿,則該函數的功能就是插入消息到消息隊列的指定位置,這個位置郵msg_prio參數指定。msg_prio大者先插入隊列,msg_prio的值應小於等於{MQ_PRIO_MAX}。如果消息已滿並且O_NONBLOCK沒有設定,該函數阻塞一直到空間可用或者mq_send()被信號中斷。如果空間可用時,多於一個進程在等待發送則按優先級,等待最久的進程先發送它的信息。如果O_NONBLOCK被設定,並且隊列已滿,則函數返回error。
5) 從消息隊列接受一條消息
函數原型:
#include<mqueue.h>
ssize_t mq_receive(mqd_t mqdes,char *msg_ptr,size_t msg_len,unsigned int *msg_prio);
函數功能:
該函數接受mqdes確定的消息隊列中最久優先級最高的消息,對參數值的限制同上。所選消息從隊列移出,復制到msa_ptr指向的緩沖區。如果參數msg_prio不為空,則指定消息被存儲在msa_prio所引用的地方。如果消息隊列為空,並且O_NONBLODK沒有設定,該函數阻塞直到消息排列入隊列中或者該函數被信號中斷。當有多個進程在消息到達隊列后請求接受,則優先級原則與上相同。
6) 通知一個進程隊列中的一條信息可用
函數原型:
#include<mqueue.h>
int mq_notify(mqd_t mqdes,const struct sigevent *notification);
函數功能:
如果參數notification不為空,函數記錄下調用進程被通知空的消息隊列(由消息隊列描述符mqdes相聯)中有一條消息到來。當消息隊列從空到非空時,一條通知消息將發送到進程中,在任一時刻,只有一個通知消息被一個消息隊列記錄。如果notifiction為空並且進程當前被指定的隊列記錄,則已存在的記錄被移去。
7) 設置消息隊列參數
函數原型:
#include <mqueue.h>
int mq_setattr(mqd_t mqdes,const struct mq_attr *mqstat,struct mq_attr *omqstat);
函數功能:
該函數用來設置與mqdes指定的消息隊列相關的參數。mq_attr結構中的mq_maxmsg,mq_magsize,mq_curmsgs成員被mq_setattr()忽略。如果omqstat為非空,則該函數被存儲,由omqstat指明存儲位置。
8) 得到消息隊列參數
函數原型:
#include<mqueue.h>
int mq_getattr(mqd_t mqdes,struct mq_attr *mqstat);
函數功能:
該函數用來取得狀態信息和與mqdes消息隊列相關的參數。
十五 互斥量
15.1互斥量屬性
1) 初始化互斥量屬性
函數原型:
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
函數功能:
pthread_mutexattr_init()函數將用默認值初始化互斥屬性對象attr來定義的所有屬性,可以用宏PTHREAD_MUTEX_INITIALIZER來靜態的初始化鎖。如果初始化成功,返回0,否則返回error。
2) 刪除互斥量屬性
函數原型:
#include <pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
函數功能:
pthread_mutexattr_ destroy()函數刪除互斥屬性對象attr定義的所有屬性。
3) 獲得和設置互斥量屬性
函數原型:
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict attr, int *restrict protocol);
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);
函數功能:
pthread_mutexattr_getprotocol()函數獲得互斥屬性對象的協議屬性; pthread_mutexattr_setprotocol()函數設置互斥屬性對象的協議屬性,設置是否支持優先級翻轉。有以下三種協議屬性:
PTHREAD_PRIO_NONE:線程的優先級和調度不會受到互斥鎖擁有權的影響。
PTHREAD_PRIO_INHERIT:使用該屬性值可以避免優先級倒置。
PTHREAD_PRIO_PROTECT:當線程擁有一個或多個使用初始化的互斥鎖屬性時,此協議值會影響其他線程的優先級和調度。
4) 互斥量屬性優先級上限屬性
函數原型:
#include <pthread.h>
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *restrict attr, int *restrict prioceiling);
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling);
函數功能:
pthread_mutexattr_setprioceiling()可用來設置互斥鎖屬性對象的優先級上限屬性。prioceiling 指定已初始化互斥鎖的優先級上限。優先級上限定義執行互斥鎖保護的臨界段時的最低優先級。prioceiling 位於 SCHED_FIFO 所定義的優先級的最大范圍內。要避免優先級倒置,請將 prioceiling 設置為高於或等於可能會鎖定特定互斥鎖的所有線程的最高優先級。oldceiling 包含以前的優先級上限值。
如果成功完成,pthread_mutexattr_setprioceiling() 會返回 0。其他任何返回值都表示出現了錯誤。
如果出現以下任一情況,pthread_mutexattr_setprioceiling() 將失敗並返回對應的值。
ENOSYS:選項_POSIX_THREAD_PRIO_PROTECT未定義並該實現不支持此函數。
EINVAL:attr 或 prioceiling 指定的值無效。
EPERM:調用方無權執行該操作。
pthread_mutexattr_getprioceiling()函數可用來獲取互斥鎖屬性對象的優先級上限屬性,返回 prioceiling 中已初始化互斥鎖的優先級上限。
5) 共享互斥量屬性
函數原型:
int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr, int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
函數功能:
pthread_mutexattr_getpshared()可為屬性對象 mattr 獲取 pshared 的當前值,該值為PTHREAD_PROCESS_SHARED或 PTHREAD_PROCESS_PRIVATE。該函數成功完成之后會返回零。其他任何返回值都表示出現了錯誤。如果出現以下情況,該函數將失敗並返回對應的值。EINVAL:由 mattr指定的值無效。
要在多個進程中的線程之間共享互斥量,可以利用pthread_mutexattr_setpshared()函數,在共享內存中創建互斥量,並將 pshared 屬性設置為 PTHREAD_PROCESS_SHARED。
15.2互斥量
1) 初始化和刪除互斥量
函數原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
函數功能:
如果針對以前初始化的但尚未銷毀的互斥鎖調用 pthread_mutex_init(),則該互斥鎖不會重新初始化。如果屬主無法使狀態保持一致,請勿調用 pthread_mutex_init(),而是解除鎖定該互斥鎖。在這種情況下,所有等待的線程都將被喚醒。以后對 pthread_mutex_lock() 的所有調用將無法獲取互斥鎖,並將返回錯誤代碼 ENOTRECOVERABLE。現在,通過調用 pthread_mutex_destroy() 來取消初始化該互斥鎖,即可使其狀態保持一致。調用 pthread_mutex_init() 可重新初始化互斥鎖。
2) 互斥量鎖操作
函數原型:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
函數功能:
鎖操作主要包括加鎖pthread_mutex_lock()、解鎖 pthread_mutex_unlock()和測試加鎖 pthread_mutex_trylock()三個,不論哪種類型的鎖,都不可能被兩個不同的線程同時得到,而必須等待解鎖。對於普通鎖和適應鎖類型, 解鎖者可以是同進程內任何線程;而檢錯鎖則必須由加鎖者解鎖才有效,否則返回EPERM;對於嵌套鎖,文檔和實現要求必須由加鎖者解鎖。在同一進程中的線程,如果加鎖后沒有解鎖,則任何其他線程都無法再獲得鎖。 pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被占據時返回EBUSY而不是掛起等待。
3) 互斥量高級鎖操作
函數原型:
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
函數功能:
當線程試圖獲取一個已加鎖的互斥變量時,pthread_mutex_timedlock互斥量原語允許綁定線程阻塞的時間。pthread_mutex_timedlock函數與pthread_mutex_lock是基本等價的,但是在達到超時時間值時,pthread_mutex_timedlock不會對互斥量進行加鎖,而是返回錯誤碼ETIMEDOUT。
4) 互斥鎖的優先級上限
函數原型:
#include <pthread.h>
int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict mutex,int *restrict prioceiling);
int pthread_mutex_setprioceiling(pthread_mutex_t *restrict mutex,int prioceiling, int *restrict old_ceiling);
函數功能:
pthread_mutex_getprioceiling()會返回 mutex 的優先級上限 prioceiling。pthread_mutex_setprioceiling()可更改互斥鎖 mutex 的優先級上限 prioceiling。可鎖定互斥鎖(如果未鎖定的話),或者一直處於阻塞狀態,直到 pthread_mutex_setprioceiling()成功鎖定該互斥鎖,更改該互斥鎖的優先級上限並將該互斥鎖釋放為止。鎖定互斥鎖的過程無需遵循優先級保護協議。
十六 線程管理
16.1線程屬性
1) 線程屬性的初始化與刪除
函數原型:
#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_init(pthread_attr_t *attr);
函數功能:
線程具有屬性,用pthread_attr_t()表示,在對該結構進行處理之前必須進行初始化,在使用后需要對其去除初始化。我們用pthread_attr_init()函數對其初始化,用pthread_attr_destroy()對其去除初始化。調用pthread_attr_init()之后,pthread_t結構所包含的內容就是操作系統實現支持的線程所有屬性的默認值。
線程屬性結構如下:
typedef struct
{
int detachstate; /*線程的分離狀態*/
int schedpolicy; /*線程調度策略*/
structsched_param schedparam; /*線程的調度參數*/
int inheritsched; /*線程的繼承性*/
int scope; /*線程的作用域*/
size_t guardsize; /*線程棧末尾的警戒緩沖區大小*/
int stackaddr_set;
void* stackaddr; /*線程棧的位置*/
size_t stacksize; /*線程棧的大小*/
}pthread_attr_t;
2) 線程屬性的分離狀態
函數原型:
#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
函數功能:
線程的分離狀態決定一個線程以什么樣的方式來終止自己。在默認情況下線程是非分離狀態的,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()返回時,創建的線程才算終止,才能釋放自己占用的系統資源。
而分離線程不是這樣子的,它沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。程序員應該根據自己的需要,選擇適當的分離狀態。所以如果我們在創建線程時就知道不需要了解線程的終止狀態,則可以pthread_attr_t結構中的detachstate線程屬性,讓線程以分離狀態啟動。
可以使用pthread_attr_setdetachstate()把線程屬性detachstate設置為下面的兩個合法值之一:設置為PTHREAD_CREATE_DETACHED,以分離狀態啟動線程;或者設置為PTHREAD_CREATE_JOINABLE,正常啟動線程。可以使用pthread_attr_getdetachstate()獲取當前的datachstate線程屬性。
3) 線程屬性的繼承性
函數原型:
#include <pthread.h>
int pthread_attr_getinheritsched(const pthread_attr_t *restrict attr,int *restrict inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
函數功能:
這兩個函數分別用來設置和得到線程的繼承性。
這兩個函數具有兩個參數,第1個是指向屬性對象的指針,第2個是繼承性或指向繼承性的指針。繼承性決定調度的參數是從創建的進程中繼承還是使用在schedpolicy和schedparam屬性中顯式設置的調度信息。Pthreads不為inheritsched指定默認值,因此如果你關心線程的調度策略和參數,必須先設置該屬性。
繼承性的可能值是PTHREAD_INHERIT_SCHED(表示新現成將繼承創建線程的調度策略和參數)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam屬性中顯式設置的調度策略和參數)。
如果你需要顯式的設置一個線程的調度策略或參數,那么你必須在設置之前將inheritsched屬性設置為PTHREAD_EXPLICIT_SCHED.線程屬性結構如下:
4) 線程屬性的調度策略
函數原型:
#include <pthread.h>
int pthread_attr_getschedpolicy(const pthread_attr_t *restrict attr,int *restrict policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
函數功能:
這兩個函數分別用來設置和得到線程的調度策略。
這兩個函數具有兩個參數,第1個參數是指向屬性對象的指針,第2個參數是調度策略或指向調度策略的指針。調度策略可能的值是先進先出(SCHED_FIFO)、輪轉法(SCHED_RR),或其它(SCHED_OTHER)。
SCHED_FIFO策略允許一個線程運行直到有更高優先級的線程准備好,或者直到它自願阻塞自己。在SCHED_FIFO調度策略下,當有一個線程准備好時,除非有平等或更高優先級的線程已經在運行,否則它會很快開始執行。
SCHED_RR(輪循)策略是基本相同的,不同之處在於:如果有一個SCHED_RR
策略的線程執行了超過一個固定的時期(時間片間隔)沒有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同優先級的線程准備好時,運行的線程將被搶占以便准備好的線程可以執行。
當有SCHED_FIFO或SCHED_RR策賂的線程在一個條件變量上等持或等持加鎖同一個互斥量時,它們將以優先級順序被喚醒。即,如果一個低優先級的SCHED_FIFO線程和一個高優先織的SCHED_FIFO線程都在等待鎖相同的互斥且,則當互斥量被解鎖時,高優先級線程將總是被首先解除阻塞。
5) 線程屬性的調度參數
函數原型:
#include <pthread.h>
int pthread_attr_getschedparam(const pthread_attr_t *restrict attr,struct sched_param *restrict param);
int pthread_attr_setschedparam(pthread_attr_t *restrict attr,const struct sched_param *restrict param);
函數功能:
這兩個函數分別用來設置和得到線程的調度參數。
這兩個函數具有兩個參數,第1個參數是指向屬性對象的指針,第2個參數是sched_param結構或指向該結構的指針。
結構sched_param的子成員sched_priority控制一個優先權值,大的優先權值對應高的優先權。系統支持的最大和最小優先權值可以用sched_get_priority_max函數和sched_get_priority_min函數分別得到。
注意:如果不是編寫實時程序,不建議修改線程的優先級。因為,調度策略是一件非常復雜的事情,如果不正確使用會導致程序錯誤,從而導致死鎖等問題。如:在多線程應用程序中為線程設置不同的優先級別,有可能因為共享資源而導致優先級倒置。
6) 線程屬性的堆棧大小
函數原型:
#include <pthread.h>
int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,size_t *restrict stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
函數功能:
這兩個函數用於獲取和設置堆棧大小屬性。stacksize屬性定義系統分配的棧大小(以字節為單位),size不應小於系統定義的最小棧大小。
7) 線程屬性的堆棧地址
函數原型:
#include <pthread.h>
int pthread_attr_getscope(const pthread_attr_t * attr, int * scope );
int pthread_attr_setscope(pthread_attr_t*, int scope);
函數功能:
這兩個函數分別用來設置和得到線程的作用域。attr是線程屬性變量,scope是線程的作用域。若成功返回0,若失敗返回-1。作用域控制線程是否在進程內或在系統級上競爭資源,可能的值是:PTHREAD_SCOPE_PROCESS(進程內競爭資源)、PTHREAD_SCOPE_SYSTEM(系統級競爭資源)。
16.2線程
1) 線程的創建
函數原型:
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *(*start_routine)(void*), void *restrict arg);
函數功能:
pthread_create是創建線程函數。若成功則返回0,否則返回出錯編號。thread為指向線程標識符的指針。attr用來設置線程屬性。第三個參數是線程運行函數的起始地址。arg是運行函數的參數。
2) 線程的終結
函數原型:
#include <pthread.h>
void pthread_exit(void *value_ptr);
函數功能:
使用函數pthread_exit()退出線程,這是線程的主動行為。
3) 線程的分離
函數原型:
#include <pthread.h>
int pthread_detach(pthread_t thread);
函數功能:
該函數將子線程的狀態設置為分離的,則該線程運行結束后會自動釋放所有資源。
4) 等待線程結束
函數原型:
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);
函數功能:
pthread_join()使一個線程等待另一個線程結束。代碼中如果沒有pthread_join主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join后,主線程會一直等待直到等待的線程結束自己才結束,使創建的線程有機會執行。
5) 獲得線程自身ID
函數原型:
#include <pthread.h>
pthread_t pthread_self(void);
函數功能:
該函數獲得線程自身的ID。pthread_t的類型為unsigned long int,所以在打印的時候要使用%lu方式,否則將產生奇怪的結果。
6) 比較線程ID
函數原型:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
函數功能:
比較兩個線程的ID是否一樣,如果一樣,返回非0,否則返回0。
7) 動態包比較
函數原型:
#include <pthread.h>
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;
函數功能:
該函數使用初值為PTHREAD_ONCE_INIT的once_control變量保證init_routine()函數在本進程執行序列中僅執行一次。
8) 獲取/設置動態線程調度參數
函數原型:
#include <pthread.h>
int pthread_getschedparam(pthread_t thread, int *restrict policy,struct sched_param *restrict param);
int pthread_setschedparam(pthread_t thread, int policy,const struct sched_param *param);
函數功能:
前面的那些函數pthread_attr_setschedparam()與pthread_attr_getschedparam()只能通過線程的屬性對象pthread_attr_t來設置線程的調度策略和優先級,不能夠直接設置正在運行的線程的調度策略和優先級,而這兩個函數可以直接設置。
注意,當pthread_setschedparam()函數的參數 policy == SCHED_RR 或者 SCHED_FIFO 時,程序必須要在超級用戶下運行。
16.3進程原語與線程原語的比較
進程原語 |
線程原語 |
描述 |
fork |
pthread_create |
創建新的控制流 |
exit |
pthread_exit |
從現有的控制流中退出 |
waitpid |
pthread_join |
從控制流中得到退出狀態 |
atexit |
pthread_cleanup_push |
注冊在退出控制流時調用的函數 |
getpid |
pthread_self |
獲取控制流的ID |
abort |
pthread_cancel |
請求控制流的非正常退出 |
在默認情況下,線程的終止狀態會保存到對該線程調用pthread_join(),如果線程已經處於分離狀態,線程的底層存儲資源可以再線程終止時立即被收回。當線程被分離時,並不能用pthread_join()函數等待它的終止狀態。對分離狀態的線程進行pthread_join()的調用可以產生失敗,返回EINVAL。pthread_detach()調用可以用於是線程進入分離狀態。[apue]
十七 條件變量
17.1條件變量
條件變量時線程可用的另一種同步機制。條件變量給多個線程提供了一個會合的場所。條件變量與互斥量一起使用時,允許線程以無競爭的方式等待特定的條件發生。
17.2條件變量屬性
1) 屬性初始化與刪除
函數原型:
#include <pthread.h>
int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);
函數功能:
這兩個函數用於創建和銷毀條件變量屬性對象。若成功返回0,若失敗返回錯誤編號。一旦某個條件變量對象被初始化了,我們就可以利用下面函數來查看或修改特定屬性了。
2) 進程共享條件變量屬性
函數原型:
#include <pthread.h>
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr,int *restrict pshared);
int pthread_condattr_setpshared(pthread_condattr_t *attr,int pshared);
函數功能:
這兩個函數用來獲得或者設置進程共享條件變量屬性。
17.3條件變量
1) 初始化與刪除
函數原型:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
函數功能:
條件變量使用之前必須首先進行初始化,pthread_cond_t數據類型代表的條件變量可以用兩種方式進行初始化,可以把常量PTHREAD_COND_INITIALIZER賦給靜態變量分配的條件變量,但是如果條件變量時動態分配的,可以使用pthread_cond_init函數進行初始化。在釋放底層的內存空間之前,可以使用pthread_cond_destroy()函數對條件變量進行去除初始化。
2) 信號量與廣播
函數原型:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
函數功能:
pthread_cond_signal()作用是發送一個信號給另外一個正在處於阻塞等待狀態的線程,使其脫離阻塞狀態,繼續執行.如果沒有線程處在阻塞等待狀態,pthread_cond_signal也會成功返回。應該在互斥量被鎖定后調用。若不止一個線程阻塞在條件變量上,則應用pthread_cond_broadcast()向其它所以線程發生信號
3) 等待(阻塞)
函數原型:
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
函數功能:
使用pthread_cond_wait()等待條件變為真,如果再給定的時間內條件不能滿足,那么會生成一個代表出錯碼的返回變量。傳遞給pthread_cond_wait()的互斥量對條件進行保護,調用者把鎖住的互斥量傳給函數,函數把調用線程放到等待條件的線程列表上,然后對互斥量解鎖,這兩個操作室原子操作。這樣就關閉了條件檢查的線程和線程進入休眠狀態等待條件改變着兩個操作之間的時間通道,這樣線程就不會錯過條件的任何變化。pthread_cond_wait()返回時,互斥量再次被鎖住。
pthread_cond_timedwait()函數的工作方式與pthread_cond_wait()相似,只是多了一個timeout。timeout值指定了等待等待的時間。
十八 附錄
18.1UNIX系統所需要的頭文件
Posix標准C頭文件(24項)
<assert.h> ---------------------- 驗證程序斷言
<complex.h> ---------------------- 支持復數算術運算
<ctype.h> ---------------------- 字符類型
<errno.h> ---------------------- 出錯碼
<fenv.h> ---------------------- 浮點環境
<float.h> ---------------------- 浮點常量
<inttypes.h> ---------------------- 整型格式轉換
<iso646.h> ---------------------- 替代關系操作符宏
<limits.h> ---------------------- 實現常量
<locale.h> ---------------------- 局部類別
<math.h> ---------------------- 數學常量
<setjmp.h> ---------------------- 非局部goto
<signal.h> ---------------------- 信號
<stdarg.h> ---------------------- 可變參數表
<stdbool.h> ---------------------- 布爾類型和值
<stddef.h> ---------------------- 標准定義
<stdint.h> ---------------------- 整型
<stdio.h> ---------------------- 標准I/O庫
<stdlib.h> ---------------------- 實用程序庫函數
<string.h> ---------------------- 字符串操作
<tgmath.h> ---------------------- 通用類型數學宏
<time.h> ---------------------- 時間和日期
<wchar.h> ---------------------- 擴展的多字節和寬字符支持
<wctype.h> ---------------------- 寬字符分類和映射支持
POSIX標准定義的基本頭文件(26項)
<dirent.h> ---------------------- 目錄項
<fcntl.h> ---------------------- 文件控制
<fnmatch.h> ---------------------- 文件名匹配類型
<glob.h> ---------------------- 路徑名模式匹配類型
<grp.h> ---------------------- 組文件
<netdb.h> ---------------------- 網絡數據庫操作
<pwd.h> ---------------------- 口令文件
<regex.h> ---------------------- 正則表達式
<tar.h> ---------------------- tar歸檔值
<termios.h> ---------------------- 終端I/O
<unistd.h> ---------------------- 符號常量
<utime.h> ---------------------- 文件時間
<wordexp.h> ---------------------- 字擴展類型
<arpa/inet.h> ---------------------- Internet定義
<net/if.h> ---------------------- 套接字本地接口
<netinet/in.h> ---------------------- Internet地址族
<netinet/tcp.h> ---------------------- 傳輸控制協議定義
<sys/mman.h> ---------------------- 內存管理聲明
<sys/select.h> ---------------------- select函數
<sys/socket.h> ---------------------- 套接字接口
<sys/stat.h> ---------------------- 文件狀態
<sys/times.h> ---------------------- 進程時間
<sys/types.h> ---------------------- 基本系統數據類型
<sys/un.h> ---------------------- UNIX域套接字定義
<sys/utsname.h> ---------------------- 系統名
<sys/wait.h> ---------------------- 進程控制
POSIX標准定義的XSI擴展頭文件(26項)
<cpio.h> ---------------------- cpio歸檔值
<dlfcn.h> ---------------------- 動態鏈接
<fmtmsg.h> ---------------------- 消息顯示結構
<ftw.h> ---------------------- 文件樹漫游
<iconv.h> ---------------------- 代碼集轉換實用程序
<langinfo.h> ---------------------- 語言信息常量
<libgen.h> ---------------------- 模式匹配函數定義
<monetary.h> ---------------------- 貨幣類型
<ndbm.h> ---------------------- 數據庫操作
<nl_types.h> ---------------------- 消息類別
<poll.h> ---------------------- 輪詢函數
<search.h> ---------------------- 搜索表
<strings.h> ---------------------- 字符串操作
<syslog.h> ---------------------- 系統出錯日志記錄
<ucontext.h> ---------------------- 用戶上下文
<ulimit.h> ---------------------- 用戶限制
<utmpx.h> ---------------------- 用戶帳戶數據庫
<sys/ipc.h> ---------------------- IPC
<sys/msg.h> ---------------------- 消息隊列
<sys/resource.h> ------------------- 資源操作
<sys/sem.h> ---------------------- 信號量
<sys/shm.h> ---------------------- 共享存儲
<sys/statvfs.h> ---------------------- 文件系統信息
<sys/time.h> ---------------------- 時間類型
<sys/timeb.h> ---------------------- 附加的日期和時間定義
<sys/uio.h> ---------------------- 矢量I/O操作
POSIX標准定義的可選頭文件(8項)
<aio.h> ---------------------- 異步I/O
<mqueue.h> ---------------------- 消息隊列
<pthread.h> ---------------------- 線程
<sched.h> ---------------------- 執行調度
<semaphore.h> ---------------------- 信號量
<spawn.h> ---------------------- 實時spawn接口
<stropts.h> ---------------------- XSI STREAMS接口
<trace.h> ---------------------- 時間跟蹤
---------------------------------------------------------------------------------------------------
參考文獻
[1] W.Richard Stevens, Stephen A.Rago. UNIX環境高級編程(第2版).
[2] David R.Butenhof. POSIX多線程程序設計
[3] the User’s Guide for the POSIX API support provided in RTEMS
[4] Standard for Information Technology–Portable Operating System Interface(POSIX®)
[5] http://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html#phtread_ref
[6] http://www.cnblogs.com/jishume/articles/2216054.html
[7] http://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html#icomments