一.文件IO和標準IO特色linux
庫函數:方便程序員開發,可能會存在一些問題(C庫除外)。程序員
系統調用:有內核提供的函數接口, 更加的穩定高效。shell
文件IO:無緩衝/低級磁盤IO編程
無緩衝:增長磁盤的訪問次數,不利於保護磁盤。數組
優勢:能夠保證數據的實時寫入。緩存
IO的操做方式:看程序的應用場合。session
標準IO:帶緩衝的/高級磁盤IO多線程
緩衝IO就是能夠減小對磁盤的訪問次數,提升系統效率,有利於保護磁盤。異步
缺點:數據不會實時寫入。socket
標準IO的緩衝方式:全緩衝、行緩衝、無緩衝。
全緩衝:默認對文件的操做都是全緩衝。當填滿標準I/O緩衝區後才進行實際I/O操做
數據什麼時候寫入磁盤:
一、緩衝區滿了。
二、程序正常退出(ctrl +c, 斷電,屬於不正常退出)。
三、執行刷新操做,fflush()
行緩衝:與終端相關的都是行緩衝
刷新條件:
一、緩衝區滿了。
二、程序正常退出(ctrl +c, 斷電,屬於不正常退出)。
三、執行刷新操做,fflush()
四、遇到\n就會刷新。
無緩衝:標準出錯,好比人機交互。不對I/O的操做進行緩衝,即對流的讀寫時會馬上操做實際的文件。
二.標準I/O庫的流(stream)
標準I/O庫的全部操做都是圍繞流(stream)來進行。
FILE 類型在 /usr/include/libio.h中。描述了與文件相關的信息。
在標準I/O中,流用FILE *來描述。
如何得到文件流:
標準IO中,默認開啓三個流, stdin stdout stderr
fprintf函數:向一個流中寫入數據。
int fprintf(FILE *stream, const char *format, ...);
stream:格式化輸出的流。
fprintf(stdout, "helloworld\n") === printf("helloworld\n");
fprintf(xxx, "helloworld\n");
⑴獲取文件流
FILE *fopen (const char *path, const char *mode);
功能:得到流
參數:path: 要打開的文件的路徑、名字, "printf.c"
mode:
r:表示打開的文件只能進行讀操做, 並且必須保證該文件已經存在。
如: fopen("printf.c", "r");
r+:表示打開的文件可讀可寫操做, 並且必須保證該文件已經存在。
如: fopen("printf.c", "r+");
W:表示打開的文件只能進行寫操做,可是若是文件已經存在,那麼會將文件內容清空。
若是文件不存在,那麼會建立一個文件。
如: fopen("file", "w");
w+: 表示打開的文件可讀可寫操做,可是若是文件已經存在,那麼會將文件內容清空。
若是文件不存在,那麼會建立一個文件。
a: 表示以追加的方式對文件進行只寫操做,若是文件已經存在,那麼會保留文件內容;
若是文件不存在,那麼會建立一個文件。
a+: 表示以追加的方式對文件進行讀寫操做,若是文件已經存在,那麼會保留文件內容;
若是文件不存在,那麼會建立一個文件。
返回值:FILE* , 若是出錯,返回NULL。
⑵關閉文件流
關閉一個打開的流:若是沒有顯式的關閉時,程序結束後,系統會關閉該流。
fclose(FILE*);成功返回0。
限制:就是不能多重關閉一個流。
問題①:測試在一個程序中最多能夠得到多少個流(1024)。
得到數值多少。
經過循環fopen實現。
⑶
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE* restrict fp)
功能:就是對stdin stdout進行重定向。將文件流fp重定向到文件pathname中
⑷刷新文件流
fflush(FILE *)
刷新流。
fflush(stdout); 指定要刷新stdout
fflush(NULL);刷新當前程序中全部的流。
⑸流操做:
每次一個字符的I/O:
讀函數:
int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar(void);
功能:就是從一個流中讀入一個字符。
返回值:讀到的字符, EOF結束。
ctrl + D 模擬EOF
問題②:實現一個簡單的cat命令 getc讀到文件結尾返回EOF
a、fopen得到文件流
b、循環的getc/fgetc來讀取這個流。
printf()
c、getc讀到文件結尾返回EOF
d、fclose(fp);
問題③:統計一個文件有多少行?
統計'\n'
每次一個字符的寫:
int putc(int c, FILE *stream);
int fputc(int c, FILE *stream);
int putchar(int c);
功能:就是向一個流中寫入一個字符。
返回:寫入的字符,出錯返回EOF
問題④:使用fgetc fputc實現cat命令
問題⑸:使用fgetc、fputc實現文件拷貝。
file 拷貝一份file1
a、讀源文件、寫目標文件
b、須要讀、寫兩個流
c、選擇對應的打開模式
判斷:文件結尾或者出錯
int feof(FILE *stream);
判斷是不是到達文件末尾
返回:非0 是到達文件末尾
int ferror(FILE *stream);
判斷是不是出錯
返回 非0 是出錯
int clearerr(FILE *stream);
清空文件操做標誌, 一旦清空,那麼就沒辦法用feof和ferror測試。
每次一行的IO操做:
讀操做:
char *gets(char *s);//禁用, 不會檢查數組長度。
char *fgets(char *s, int size, FILE * stream);
功能:就是從一個流中讀入一個字符串。
參數:s:就是存放讀到的字符串
size: 表示用戶但願讀到的字節個數
stream:要讀的流
返回值:讀到的字符串,出錯返回NULL。
fgets的特色:
①、執行一次最多能夠讀到 size - 1個字符,最後一個位置存放'\0';
fgets讀到的都是字符串。
②、fgets函數讀到 \n會馬上返回。
③、當從終端上讀入時, 若是讀入的字節個數小於 size -1, 那麼'\n‘也會被讀走。
若是讀入的字節個數大於 size -1, 那麼'\n‘就不會被讀走。
問題⑥:使用fgets和printf來實現cat命令
fgets讀到文件結尾返回NULL。
每次一行的寫函數:
int puts(const char *s);
功能:向標準輸出寫入數據
特色:自帶換行符'\n’;
只會輸出第一個'\0'以前的內容。
int fputs(const char *s, FILE * stream);
功能:向一個流中寫入數據, fputs沒有自帶換行符。
返回:EOF失敗, 非負表明成功。
特色:只會輸出第一個'\0'以前的內容。
問題⑦:使用fgets fputs實現文件拷貝?
直接IO方式:按記錄個數進行操做。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:從一個流中讀數據
參數:ptr 存放都到的數據。
size: 每一條記錄的字節個數。
nmemb: 執行一次fread最多要讀的記錄的個數。
最多能夠讀到字節個數 = size * nmemb;
stream: 要讀的流。
每一條記錄:能夠是任何數據,好比struct 、int、 字符串。
返回值:表示正確讀到的記錄個數。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
功能:向一個流中寫數據
參數:ptr 存放要寫入的數據。
size: 每一條記錄的字節個數。
nmemb: 執行一次fread最多要讀的記錄的個數。
最多能夠寫入字節個數 = size * nmemb;
stream: 要寫的流。
每一條記錄:能夠是任何數據,好比struct 、int、 字符串。
返回值:表示正確寫入的記錄個數。
對文件的偏移 和定位操做:
int fseek(FILE *stream, long offset, int whence);
功能:對一個文件的訪問位置進行偏移。
stream:要進行操做的流
offset: 偏移量
whence:要偏移的參考點,基準點。
SEEK_SET 表明文件的起始位置
SEEK_END 表明文件的結尾
SEEK_CUR 表明文件當前的訪問位置。
fseek(fp, 0, SEEK_END); 表明偏移到文件末尾位置
fseek(fp, 0, SEEK_SET); 表明偏移到文件開始位置
fseek(fp, 100, SEEK_SET); 表示向文件結尾的方向偏移100個字節
fseek(fp, -100, SEEK_END); 表示向文件開頭的方向偏移100個字節
返回值:成功返回0, 失敗-1.
long ftell(FILE *stream);
功能:就是定位當前的訪問位置
返回值:返回從開頭到如今位置的字節個數。
void rewind(FILE *stream);
功能:將文件位置偏移到開頭。
+---------------------------------------------------------------------------+
做業:
編程讀寫一個文件test.txt,每隔1秒向文件中寫入一行數據,相似這樣:
1, 2007-7-30 15:16:42
2, 2007-7-30 15:16:43
該程序應該無限循環,直到按Ctrl-C中斷程序。
再次啓動程序寫文件時能夠追加到原文件以後,而且序號可以接續上次的序號,好比:
1, 2007-7-30 15:16:42
2, 2007-7-30 15:16:43
3, 2007-7-30 15:19:02
4, 2007-7-30 15:19:03
5, 2007-7-30 15:19:04
提示:
要追加寫入文件,同時要讀取該文件的內容以決定下一個序號是幾,應該用什麼模式打開文件?
首先判斷一下打開的文件是否爲新文件,若是是新文件,就從序號1開始寫入;若是不是新文件,則統計原來有多少行,好比有n行,而後從序號n+1開始寫入。之後每寫一行就把行號加1。
獲取當前的系統時間須要調用函數time(),獲得的結果是一個time_t類型,其實就是一個大整數,其值表示從UTC時間1970年1月1日00:00:00(稱爲UNIX的Epoch時間)到當前時刻的秒鐘數。而後調用localtime()將time_t所表示的UTC時間轉換爲本地時間(咱們是+8區,比UTC多8個小時)並轉成struct tm類型,該類型的各數據成員分別表示年月日時分秒,請本身寫出轉換格式的代碼,不要使用ctime()或asctime()函數。具體用法請查閱man page。time和localtime函數須要頭文件time.h。
調用sleep(n)可以使程序睡眠n秒,該函數須要頭文件unistd.h。
time()得到自 1970-1-1 0:0:0: 距離當前系統的秒數。
time_t mytime;
mytime = time()
localtime()用來將秒數轉換成對應的時間格式。
struct tm *localtime(const time_t *timep);
struct tm * mytm;
mytm = localtime(&mytime);
sleep(1);睡眠
fprintf(fp, "");
出錯判斷:
strerror() - 映射errno對應的錯誤信息
char *strerror(int errnum);
perror() – 輸出用戶信息及errno對應的錯誤信息
void perror(const char *s);
自帶換行符。
三.文件IO:系統調用
默認打開文件描述符:標準輸入 標準輸出 標準出錯
0 1 2
STDIN_FILENO STDOUT_FILENO STDERR_FILENO
文件描述符是一個非負整數。
int open(const char *pathname, int flags, mode_t mode)
功能:就是打開一個文件,得到一個文件描述符。
參數:pathname 文件名
flags:
O_RDONLY 表示對文件只讀
O_WRONLY 表示對文件只寫
O_RDWR 表示對文件可讀可寫 ====》這三個標誌位是互斥的,有且只能有一個。
O_CREAT 表示文件不存在時,會建立文件。
O_EXCL 配合O_CREAT使用,當文件已經存在時還要建立,那麼就會出錯。
O_TRUNC 如文件已經存在,那麼打開文件時先刪除文件中原有數據。
O_APPEND 以添加方式打開文件,因此對文件的寫操做都在文件的末尾進行。
如: open("file", O_WRONLY|O_TRUNC);
mode: 只有添加了O_CREAT 標誌位時,才須要使用。
指定建立的文件的權限。
S_IRWXU S_IRUSR S_IWUSR S_IXUSR S_IRWXG S_IRGRP S_IWGRP S_IXGRP S_IRWXO S_IROTH S_IWOTH S_IXOTH
如:open("file", O_WRONLY|O_CREAT|O_EXCL, 0666);
返回值:獲得的當前程序中最小的未使用的非負整數文件描述符。
open函數建立的文件權限: 指定權限 & (~umask)
好比: 0666 & (~0002)
問題:測試在一個程序中最多能夠得到多少個文件描述符?
關閉文件描述符:
close(fd);
讀寫文件:
ssize_t read(int fd, void *buf, size_t count);
功能:從文件描述符中讀取數據
參數:fd 要讀的文件描述符
buf 存放讀取到的數據
count 指望執行一次read要讀取的字節個數。
返回值:就是實際讀到的字節個數。
讀到文件末尾返回0.
特色:read讀取數據和’\n’, ‘\0’沒有任何關係。
ssize_t write(int fd, const void *buf, size_t count);
功能:向文件描述符中寫入數據
參數: fd 要寫的文件描述符
buf 存放要寫入到的數據
count 指望執行一次write要寫入的字節個數。
返回值:就是實際寫入的字節個數。
特色:write 讀取數據和'\n‘, '\0'沒有任何關係
od -c 以字符形式顯示文件內容。
問題:使用read write函數實現文件拷貝?
當read和write配合使用時,write要寫入的字節個數必須是read實際讀到的字節個數。
bytes = read(fd_r, buf, sizeof(buf));
write(fd_w, buf, bytes);
lseek:
off_t lseek(int fd, off_t offset, int whence);
功能:就是對文件訪問位置進行定位和偏移
參數:fd 要操做的描述符
offset 指定的偏移量
whence 偏移的參考點
SEEK_SET
SEEK_END
SEEK_CUR
返回值:就是偏移以後的位置。
如: lseek(fd, 0, SEEK_END);偏移到文件末尾, 返回值就是文件的大小(字節)。
lseek(fd, 0, SEEK_SET); 偏移到文件開頭。
lseek(fd, 100, SEEK_SET);向文件末尾方向偏移100個字節
lseek(fd, -100, SEEK_END);向文件開頭方向偏移100個字節
lseek(fd, 0, SEEK_CUR);返回的當前的訪問位置。
限制: lseek()只對常規文件有效,對socket、管道、FIFO等進行lseek()操做失敗。
產生空洞文件:能夠起到搶佔磁盤空間的做用。
在文件末尾使用lseek繼續向後進行偏移時,而且在偏移只有須要寫入數據(文件大小會增長)
在文件末尾使用lseek繼續向後進行偏移時 ,若是偏移以後沒有寫入任何數據,那麼文件大小不會變。
lseek和O_APPEND:
當使用O_APPEND標誌位進行寫操做時, lseek的偏移對於寫操做無效, 只對讀操做有效。
補充:
獲取文件屬性:
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
參數 path: 要操做的文件
buf: 存放獲得文件屬性。
目錄操做相關:
DIR *opendir(const char *name);
功能:就是得到一個結構體指針。
struct dirent *readdir(DIR *dirp);
功能:讀取一個目錄
返回值:
int closedir(DIR *dirp);
關閉一個目錄流
四.靜態庫和動態庫的分析
本質上來講庫是一種可執行代碼的二進制形式。
linux下的庫有兩種:靜態庫和共享庫(動態庫)
靜態庫在程序編譯時會被鏈接到目標代碼中:程序運行時將再也不須要該靜態庫,所以體積較大。
優勢:程序運行時將再也不須要該靜態庫
缺點:可執行文件的體積較大。
相同的庫可能會須要被屢次加載。
靜態庫: libxxxxx.a
動態庫:動態庫在程序編譯時並不會被鏈接到目標代碼中,
優勢: 在程序運行時動態庫纔會被載入內存,所以代碼體積較小。
缺點: 所以在程序運行時還須要動態庫存在。
靜態庫的製做:將功能函數編譯成庫。
一、先生成目標文件
gcc -c -Wall fun.c -o fun.o
二、ar crs libfun.a fun.o
將fun.o文件 打包生成libfun.a的靜態庫
庫的命名:lib庫名.a
使用:
-L:指定庫的路徑
-l :指定須要鏈接的庫的名字
gcc test.c -o test -L . -lfun
動態庫的製做和使用:
一、須要生成目錄文件
gcc -c -fPIC -Wall fun.c -o fun.o
fPIC:說明庫能夠被加載到內存的任意位置
二、 gcc -Wl,-soname,libfun.so -shared fun.o -o libfun.so.1
-Wl,-soname,libfun.so 須要鏈接的庫
libfun.so.1 實際生成的庫。
庫的命名:lib庫名.so
三、 ln -s 絕對路徑/libfun.so.1 libfun.so
四、gcc test.c -o test -L . -lfun
共享庫的加載方法:
一、動態庫須要被放置到/usr/lib 或者 /lib目錄下。
只須要將軟鏈接移動過去。
二、將庫的路徑添加到系統環境變量中
LD_LIBRARY_PATH
exprot LD_LIBRARY_PATH=庫的路徑
三、將庫的路徑添加到 /etc/ld.so.conf/xxx.conf 的配置文件中
sudo ldconfig 來重啓配置文件
用戶id轉換成用戶名
struct passwd *getpwuid(uid_t uid);
passwd-> pw_name
組id轉換成組名
struct group *getgrgid(gid_t gid);
group->gr_name
char *ctime(const time_t *timep);
返回值:"Wed Jun 30 21:49:08 1993\n"
printf("%.12s", buf + 4);
五.進程
1.特色:
動態、佔用內存資源、cpu資源、有生命週期、具備獨立的IO狀態。
內核中有struct task_struct 結構體來記錄一個進程的全部信息。
程序:靜態的、佔用磁盤空間。
PID: 惟一的標識一個進程
⒉ 進程的類型:
交互進程
守護進程:該類進程在後臺運行。它通常在Linux啓動時開始執行,系統關閉時才結束。
進程的時間片:內核會爲每一個進程分配時間片。
進程都是宏觀並行,微觀串行。
進程的狀態:運行態、就緒態、 阻塞態(睡眠等待)
進程運行與操做系統之上:
進程有用戶空間模式、內核空間模式。
32位的操做系統上:進程的尋址空間4G(虛擬地址)
一般的空間劃分: 3G(用戶空間) 1G(內核空間)
進程啓動方式: 手動啓動、 調度啓動 (/etc/crontab 定時運行腳本)
3.進程經常使用命令:
ps ajx
ps aux
"?" 就是守護進程, 不受終端的控制。
stat: 狀態
S: interruptible sleep (waiting for an event to complete)可中斷的睡眠狀態
R: running or runnable (on run queue)可執行狀態
Z: 退出狀態,成爲殭屍進程
D:uninterruptible sleep (usually IO)不可中斷的睡眠狀態
T:stopped, either by a job control signal or because it is being traced暫停或跟蹤狀態
X:dead (should never be seen)退出狀態,進程即將被銷燬
對於BSD格式,傳統的狀態關鍵字符:
<:high-priority (not nice to other users)
N: low-priority (nice to other users)
L: has pages locked into memory
s: is a session leader
l: is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
+:is in the foreground process group前臺進程
top:動態顯示系統資源使用狀況
shift + <
shift + >
q: 結束
nice 按用戶指定的優先級運行進程
nice的值 -20 ~ 19(優先級最低)
nice ./a.out 默認nice值是10.
nice --11 ./a.out 將a.out的nice值設置爲 -11
nice -11 ./a.out 將a.out的nice值設置爲 11
renice 改變正在運行進程的優先級
renice num pid 如: renice -9 1200 , 改變進程的nicenice值爲-9.
kill: 給進程發送信號。
kill -l 列出當前系統支持的信號類型。
kill -9 pid
bg 將掛起的進程在後臺執行
fg 把掛起的進程放到前臺運行
ctrl + Z 用來掛起一個進程。
+-------------------------------------------------+
ctags的使用:
sudo ctags -R 建立索引文件tags
vi -t pid_t
ctrl + ] 光標的位置,前進查找該單詞
ctrl + O/T 回退
+-------------------------------------------------+
4.進程:建立、控制、銷燬、通訊機制。
pid_t fork(void);
功能:建立一個子進程
返回值:fork函數會返回兩個值。
若是返回值 0: 表示進入的是子進程的代碼
若是返回值 大於0 : 表示進入的是父進程的代碼
返回-1, 表示建立子進程失敗。
fork函數的返回順序不必定。
現代版的fork特色:這就是著名的「寫操做時拷貝」(copy-on-write)技術。
若是其中任何一個進程試圖修改數據,那麼進程纔會具備各自不一樣的物理空間。
pid_t getpid(void);得到當前進程的pid號
pid_t getppid(void);得到當前進程的父進程的pid號。
注意: 必需要注意兩個函數的使用場合。
exec函數族 :一系列函數
功能:用於執行一個可執行文件。
int execl(const char *path, const char *arg, ...);
參數:path: 可執行文件路徑
arg: 傳遞給可執行文件的參數。
第一個參數必須和可執行文件的名字同樣。
最後一個參數必須是NULL。
好比: execl("/bin/ls", "ls", "-l", "-a", NULL);
返回值:成功返回0, 失敗返回-1.
int execlp(const char *file, const char *arg, ...);
如: execlp("ls", "ls", "-l", "-a", NULL);
int execle(const char *path, const char *arg, ..., char * const envp[]);
如:char *env[3] = {"PATH=/bin/", NULL};
execle("a.out", "a.out", env);
int execv(const char *path, char *const argv[]);
如:char *arg[4] = {"ls", "-l", "-a", NULL};
execv("/bin/ls", arg);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
l: list,列表, 就是以列表的形式傳參。
v: 傳遞參數時, 使用數組傳遞。
p: 表示能夠執行存在於系統環境變量中的可執行文件。
e:環境變量的意思
void exit(int status);
功能:正常退出一個進程。
參數: status: 用來傳遞進程的退出狀態。
exit(0);一般表示正常退出
exit(1);表示失敗退出。
exit 在退出進程時會刷新標準IO的緩衝區
void _exit(int status);
功能:正常退出一個進程。
參數: status: 用來傳遞進程的退出狀態。
_exit 在退出進程時會不會刷新標準IO的緩衝區
孤兒進程:父進程先退出,子進程仍然執行。
這樣會致使子進程被系統的1號進程收養。
殭屍進程:父進程仍然運行,子進程先退出。
當父進程退出時,殭屍進程也會消失。
處理殭屍進程的方法:
wait 、waitpid 用來處理退出的子進程。
第一種方法:
pid_t wait(int *status);
功能:就是阻塞等待當前進程中任意一個子進程退出
參數: status:用來保存退出進程的退出狀態。
返回值:成功就返回退出進程的pid
失敗,返回-1.
exit(status);
能夠經過 WEXITSTAT(status)來獲得子進程退出時的真實狀態。
第二種方法:
pid_t waitpid(pid_t pid, int *status, int options);
參數:pid:
經常使用: pid>0:只等待進程ID等於pid的子進程,無論已經有其餘子進程
經常使用: pid=-1:等待任何一個子進程退出,此時和wait做用同樣。
pid=0:等待其組ID等於調用進程的組ID的任一子進程。
pid < -1: :等待其組ID等於pid的絕對值的任一子進程。
status: 等價於wait中的status
options:
經常使用: WNOHANG:若由pid指定的子進程並不馬上退出,則waitpid不阻塞等待,此時返回值爲0 。
WUNTRACED:若是子進程進入暫停執行則立刻返回,但終止狀態不予理睬。
WCONTINUED:
返回值:若是有子進程退出,那麼返回退出的pid號
使用選項WNOHANG且沒有子進程結束時:0
調用失敗,返回-1.
wait(status) ==== waitpid(-1, status, 0);
第三種方法:???
問題:使用父子進程實現文件拷貝?
父進程拷貝前一半, 子進程拷貝後一半。
一、fork產生一個子進程
二、open得到源文件、目標文件描述符
三、統計源文件的字節數
四、實現爲目標文件分配與源文件大小相同的空間
五、在子進程的代碼中先關閉以前繼承過來的描述符,而後從新打開兩個文件,得到本身的描述符。
六、兩個進程在目標文件的不一樣位置開始拷貝。
read write函數實現拷貝。
七、父進程要等待子進程退出,避免殭屍進程。
守護進程
建立方法⑴步驟:
①、建立子進程,父進程退出 (先得到孤兒進程)
②、在子進程中建立新會話
setsid();
建立一個新會話,而且當前進程變爲會話組組長。
函數可以使進程徹底獨立出來,從而脫離全部其餘進程的控制。
③、改變當前目錄爲根目錄
chdir("/"); 一般將守護進程的工做目錄設置爲根目錄。
④、重設文件權限掩碼
umask(0);
⑤、關閉不須要的文件描述符
int file_num = getdtablesize(); 得到最大的描述符值 + 1;
for (fd = 0; fd < fdtablesize; fd++)
close(fd);
建立方法⑵:daemon()函數
六.線程:
是一種輕量級的進程
使用多線程的好處:
(1)大大提升了任務切換的效率;
(2)避免了額外的TLB & cache的刷新
TLB(Translation Lookaside Buffer)傳輸後備緩衝器是一個內存管理單元用於改進虛擬地址到物理地址轉換速度的緩存;TLB是一個小的,虛擬尋址的緩存,其中每一行都保存着一個由單個PTE組成的塊。若是沒有TLB,則每次取數據都須要兩次訪問內存,即查頁表得到物理地址和取數據。
高速緩衝存儲器是存在於主存與CPU之間的一級存儲器, 由靜態存儲芯片(SRAM)組成,容量比較小但速度比主存高得多, 接近於CPU的速度。
Linux裏一樣用task_struct來描述一個線程。線程和進程都參與統一的調度
一個進程中的多個線程:共享一部分資源的, 也有本身的私有資源。
線程基本操做:
a.建立線程
b.刪除線程
c.控制線程
線程頭文件:#include <pthread.h>
多線程經過第三方的線程庫來實現
gcc pthread_create.c -lpthread
線程操做函數:
(1) 建立線程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:就是建立一個線程。
參數:thread: 線程的標識符。
attr: 用來設置線程的屬性(一般不須要設置,直接設置爲NULL)attribute
start_routine: 回調函數,就是線程要執行的函數。
arg:傳遞給 start_routine的參數
返回:成功返回0, 失敗返回非負的errno值。
(2) 回收(釋放)線程相關資源
int pthread_join(pthread_t thread, void **retval);
功能:就是阻塞等待一個線程的退出,回收其資源。
參數:thread:指定要等待退出的線程id。
retval: 接收退出線程的狀態。
返回:成功返回0, 失敗返回非負的errno值。
(3) 退出線程
void pthread_exit(void *retval);
功能:線程退出
參數:retval 退出狀態。
線程間同步和互斥機制:
信號量
互斥鎖
條件變量
互斥鎖:
(1) 初始化互斥鎖
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
功能:就是初始化一把鎖
參數:mutex: 是鎖的標識符。
attr: 用來設置線程鎖的屬性(一般爲NULL)
返回:成功返回0, 失敗返回非負的errno值。
用法:pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
(2) 互斥鎖加鎖
int pthread_mutex_lock(pthread_mutex_t *mutex)
功能:加鎖,是阻塞等待,直到該鎖可用爲止。
參數:就是初始化後的鎖。
返回:成功:0
出錯:-1
(3) 互斥鎖解鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex)
功能:解鎖
參數:就是初始化後的鎖。
返回:成功:0
出錯:-1
(4)互斥鎖銷燬
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:就是銷燬一把鎖
參數:mutex,將要被銷燬的鎖。
返回:成功:0
出錯:-1
信號量:
基於POSIX的無名信號量:
頭文件:#include <semaphore.h>
(1) 信號量初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能: 信號量值的初始化
參數: sem: 信號量的標識符
pshared: 一般爲0, 表示該信號量應用於線程之間。
1, 表示該信號量應用於進程之間。
value:信號量的初始值。
(2) 申請信號量資源(阻塞)
int sem_wait(sem_t *sem);
功能:用於阻塞申請信號量資源, P操做。
每執行一次,信號量的值減 1.
參數:要申請的信號量
(3) 釋放資源
int sem_post(sem_t *sem);
功能:釋放資源 , V操做
每執行一次,信號量的值加1.
(4) 申請信號量資源(非阻塞)
int sem_trywait(sem_t *sem);
功能:非阻塞申請資源。
(5) 獲取當前信號量的值
int sem_getvalue(sem_t *sem, int *svalue);
功能:得到當前信號值的值。
參數:sem, 要操做的信號量
svalue, 存放獲得的值。
(6) 信號量銷燬
int sem_destroy(sem_t *sem);
功能:銷燬一個信號量
條件變量:睡眠、喚醒
說明:條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動做:一個線程等待"條件變量的條件成立"而掛起;另外一個線程使"條件成立"(給出條件成立信號)。爲了防止競爭,條件變量的使用老是和一個互斥鎖結合在一塊兒。
(1) 使線程睡眠----等待
說明:必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()的競爭條件(Race Condition)。mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調用pthread_cond_wait()前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊列之前,mutex保持鎖定狀態,並在線程掛起進入等待前解鎖。在條件知足從而離開pthread_cond_wait()以前,mutex將被從新加鎖,以與進入pthread_cond_wait()前的加鎖動做對應。
函數聲明:
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
功能:使線程的睡眠函數
參數:cond :睡眠的條件。
mutex: 互斥鎖
特色:線程睡眠時須要獲得一把鎖,在睡眠以後有會將鎖釋放。
可是,當線程被喚醒時須要從新上鎖,若是該鎖已經被其餘線程佔用,那麼該線程繼續睡眠。
內部操做:
pthread_mutex_lock(&mutex)
pthread_cond_wait()
{
pthread_mutex_unloct(&mutex);
if(條件不知足)
睡覺
else
{
pthread_mutex_lock(&mutex);
return;
}
}
do something();
pthread_mutex_unlock(&mutex);
(2) 喚醒一個指定條件線程----激發
說明:激活一個等待該條件的線程,存在多個等待線程時按入隊順序激活其中一個。
int pthread_cond_signal(pthread_cond_t *cond);
功能:只能喚醒一個指定條件線程。
參數:cond
(3) 喚醒全部指定條件線程----激發
說明:激活全部等待線程
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:喚醒全部指定條件線程。
參數: cond
(4) 銷燬條件變量
說明:只有在沒有線程在該條件變量上等待的時候才能註銷這個條件變量,不然返回EBUSY。註銷動做只包括檢查是否有等待線程
int pthread_cond_destroy(pthread_cond_t *cond);
功能: 銷燬一個條件變量
(5)初始化條件變量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
功能:初始化一個條件變量。
參數:cond: 條件變量
attr:屬性,一般爲NULL.
臨界資源:多道程序系統中存在許多進程,它們共享各類資源,然而有不少資源一次只能供一個進程使用。一次僅容許一個進程使用的資源稱爲臨界資源。
七.進程間通訊機制:
⑴system V IPC 其通訊進程主要侷限在單個計算機內
⑵BSD 造成了基於套接字(socket)的進程間通訊機制
⑶POSIX 進程間通訊機制。
system V IPC:①共享內存(share memory)
②消息隊列(message queue)
③信號燈(semaphore)
傳統的進程間通訊方式:④無名管道(pipe)
⑤有名管道(fifo)
⑥信號(signal)
⑦套接字
㈠.無名管道:存在於內存中。
特色: 只能用於具備親緣關係的進程之間的通訊
半雙工的通訊模式,具備固定的讀端和寫端
管道能夠當作是一種特殊的文件,對於它的讀寫可使用文件IO如read、write函數。
int pipe(int fd[2])
功能:建立一個無名管道。
參數:fd.
fd[0] 是無名管道的讀端
fd[1] 是無名管道的寫端
管道的大小有限制:64KB
測試無名管道的大小?
無名管道的特性:當管道被填滿時, 寫操做會被阻塞即write函數不會返回。
pipe在父子進程之間通訊:
㈡.有名管道(fifo):能夠在沒有親緣關係的進程間通訊。
有名管道能夠經過路徑名來指出,而且在文件系統中可見。
有名管道的建立:雙向的讀寫。
第一種方法: mkfifo 命令,即mkfifo 文件名
第二種方法: int mkfifo(const char *filename,mode_t mode);
功能:建立有名管道
參數:filename:要建立的管道名;mode:管道的訪問權限
返回值:成功返回0, 失敗返回-1.
頭文件:#include <sys/types.h>
#include <sys/stat.h>
特色:
open函數打開fifo文件時:若是一個進程指定的O_RDONLY或者O_WRONLY,那麼open此時會阻塞,直到另一個進程以對應的方式打開爲止。
在一個進程中直接打開,能夠用O_RDWR標誌位。
fifo它是隨着進程維護,當全部操做該管道的進程都結束時,那麼管道中的數據就會被丟棄。
open("fifo", O_WRONLY|O_NONBLOCK);
O_NONBLOCK:以非阻塞的方式打開特殊文件, 之後對該文件的操做都是非阻塞模式。
㈢.信號 signal:
信號是在軟件層次上對中斷機制的一種模擬(內核模擬),是惟一一種異步通訊機制;信號是由內核產生。信號能夠直接進行用戶空間進程和內核進程之間的交互,內核進程也能夠利用它來通知用戶空間進程發生了哪些系列事件。內核進程能夠在任什麼時候刻發送信號給某一進程,無需知道該進程的狀態。若該進程處於未執行態,那麼內核就會保存該信號,直到該進程恢復執行態再傳給它;若信號被進程設置爲阻塞,那麼該信號的傳遞被延遲,直到其阻塞被取消才被傳遞給進程。
linux支持的信號類型:不可靠信號(有默認處理方式,也能夠自定義)、可靠信號(用戶自定義處理方式)。由命令kill –l在終端上顯示的序號1-31這31個信號爲不可靠信號;序號34-64這31個信號爲可靠信號。
經常使用信號:
不能夠被忽略、阻塞、處理:
SIGKILL:不能夠被忽略、阻塞、處理, 接收到該信號的進程只能退出。默認終止。
SIGSTOP:該信號用於暫停一個進程,且不能被阻塞、處理或忽略。
SIGALRM:該信號當一個定時器到時的時候發出, 默認操做時終止進程。
SIGCHLD:子進程改變狀態時,父進程會收到這個信號,默認忽略。
SIGABORT:該信號用於結束進程。默認終止。
SIGHUP:與終端相關,該信號在用戶終端鏈接結束時發出,一般在終端的控制進程結束時,通知同一會話內的各個進程與控制終端再也不聯繫。默認終止。
SIGILL:該信號在一個進程企圖執行一條非法指令時(可執行文本自己出現錯誤,或企圖執行數據段 堆棧溢出時)發出。默認終止。
SIGFPE:該信號在發生致命的運算錯誤時發出。默認終止。
按鍵產生信號:
SIGTSTP:該信號用於暫停交互進程,用戶可鍵入SUSP字符(一般是Ctrl+Z)發出這個信號。
SIGINT:該信號在用戶鍵入INTR字符(一般是Ctrl+C)時發出,終端驅動程序發送此信號並送到前臺進程中的每個進程。默認終止。
SIGQUIT:該信號和SIGINT相似,但由QUIT字符(一般是Ctrl+\)來控制。默認終止。
用戶進程對信號的響應方式:
①忽略信號:對信號不作任何處理,可是有兩個信號不能忽略:即SIGKILL及SIGSTOP。
②捕捉信號(用戶自定義):定義信號處理函數,當信號發生時,執行相應的處理函數。
③執行缺省操做:Linux對每種信號都規定了默認操做
與信號相關的函數:
⑴int kill(pid_t pid, int sig);
功能:給進程發送信號
參數:pid:正數:值就是指定要接收信號的進程的進程號
0:信號被髮送到全部和當前進程在同一個進程組的進程
-1:信號發給全部的進程表中的進程(除了進程號最大的進程外)
<-1:信號發送給進程阻號爲-pid的每個進程
sig: 要發送的信號類型
頭文件:#include<signal.h>
#include<sys/types.h>
kill(getpid(), xxx); 能夠給本身發送信號。
返回:成功返回0, 失敗返回-1。
若是sig的值小於0或sig大於64,那麼函數kill不能發送信號,執行出錯;若是sig等於0,那麼函數kill沒有發送信號(發送空信號),可是函數執行成功,不會報錯,這樣的操做能夠用於檢查一個進程或進程組是否存在。
⑵int raise(int sig);
功能:給自身發送信號
參數:sig:信號類型
返回值:成功:0 ,出錯:-1
頭文件:#include<signal.h>
⑶unsigned int alarm(unsigned int seconds)
該函數不會阻塞。
功能:給當前進程設置一個鬧鐘, 默認在定時時間到達以後進程會退出。
alarm: 在一個進程中只能有一個(最後一個)定時有效。
返回值:成功:若是調用此alarm()前,進程中已經設置了鬧鐘時間,則
返回上一個鬧鐘時間的剩餘時間,不然返回0。
失敗: 返回-1.
int pause(void);
功能:阻塞等待任意一個信號。
頭文件:#include <unistd.h>
對信號捕捉處理:
signal函數
sigaction函數
signal函數:
⑷typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:用於捕捉、處理信號。
參數: signum: 指定要捕捉的信號類型
handler; 表明對捕捉到的信號進行自定義處理。
SIG_IGN, 表示對捕捉到的信號進行忽略。
SIG_DFL, 表示對該信號進行默認處理。
signal(SIGINT, handler) == SIG_DFL
返回值: 成功返回上一次對信號的處理方式
失敗, SIG_ERR
頭文件:#include <signal.h>
system V 的IPC機制(3種):
①共享內存(效率最高)
②消息隊列
③信號燈集
ipcs: 用來查看system V的IPC機制標識符的命令
ipcs -m shared memory segments顯示當前系統中 共享內存的使用
-q message queues顯示當前系統中 消息隊列的使用
-s semaphores顯示當前系統中 信號燈集的使用
-a all (default)顯示當前系統中 共享內存,消息隊列,信號燈集的使用
-h 顯示幫助
-t time顯示時間
-p pid顯示進程號
-c creator顯示建立者
-l limits顯示容量限制,最大或最小
-u summary顯示使用狀態
ipcrm 用來刪除 當前系統中 system V的IPC機制標識符的命令
-m 刪除當前系統中 共享內存的標識符
-q 刪除當前系統中 消息隊列的標識符
-s 刪除當前系統中 信號燈集的標識符
如: ipcrm -m 共享內存的id
⑴.將文件名轉換成key值
說明:在IPC(InterProcess Communication)的通訊模式下,無論是使用消息隊列仍是共享內存,甚至是信號量,每一個IPC的對象(object)都有惟一的名字,稱爲「鍵」(key)。經過「鍵」,進程可以識別所用的對象。「鍵」與IPC對象的關係就如同文件名稱之於文件,經過文件名,進程可以讀寫文件內的數據,甚至多個進程可以共用一個文件。而在IPC的通信模式下,經過「鍵」的使用也使得一個IPC對象能爲多個進程所共用。
key_t ftok(const char *pathname, int proj_id);
功能:得到一個key值。
參數: pathname 文件名,文件索引號,通常由命令ls –i 查詢文件索引號
proj_id 就是一個整數(低八爲不能爲0),因爲該int型大小範圍0-255,因此通常傳入字符。
頭文件:#include <sys/types.h>
#include <sys/ipc.h>
如:key_t key;
key = ftok(".", 'a');
㈣.共享內存:
內存模型:在 Linux 系統中,每一個進程的虛擬內存是被分爲許多頁面的。這些內存頁面中包含了實際的數據。每一個進程都會維護一個從內存地址到虛擬內存頁面之間的映射關係。儘管每一個進程都有本身的內存地址,不一樣的進程能夠同時將同一個內存頁面映射到本身的地址空間中,從而達到共享內存的目的。
分配一個新的共享內存塊會建立新的內存頁面。由於全部進程都但願共享對同一塊內存的訪問,只應由一個進程建立一塊新的共享內存。再次分配一塊已經存在的內存塊不會建立新的頁面,而只是會返回一個標識該內存塊的標識符。一個進程如需使用這個共享內存塊,則首先須要將它綁定到本身的地址空間中。這樣會建立一個從進程自己虛擬地址到共享頁面的映射關係。當對共享內存的使用結束以後,這個映射關係將被刪除。當再也沒有進程須要使用這個共享內存塊的時候,必須有一個(且只能是一個)進程負責釋放這個被共享的內存頁面。
全部共享內存塊的大小都必須是系統頁面大小的整數倍。系統頁面大小指的是系統中單個內存頁面包含的字節數。在 Linux 系統中,內存頁面大小是4KB,不過您仍然應該經過調用 getpagesize 獲取這個值。
共享存儲器的執行方式:是將一個儲存器區段標記爲共用,這時各進程能夠把這個區段映射到該進程自己的虛擬地址裏。創建共享存儲器可經過shmget系統調用,shmget執行後,核心程序就保留一塊指定大小的空間,同時關於此共享存儲器的一切數據,如區段的長度,區段的存取權,區段創建者的進程識別碼等存入一個叫shmid_ds的結構。如今共享存儲器雖然已經創建了,但是仍沒法連上它,這時就須經過shmat系統調用獲得一個指向共享存儲器基址的指針,經過此指針,就能夠如同於操做通常存儲器似的取用共享存儲器。shmdt進行相反的工做,用來脫離已連上的共享存儲器。(數據保護)
⑵建立/打開共享內存:
int shmget(key_t key, size_t size, int shmflg);
功能:分配得到一片共享內存,實際只告訴內核,而內核並無分配,只是贊成你獲得一個毫無心義的標識符。
參數:key, 用來使用不一樣進程使用相同的共享內存。
IPC_PRIVATE:表示申請的共享內存是私用的。
key = ftok(xxx,yyy); 須要經過ftok函數來得到。
size:進程要申請的共享內存的大小(字節)若是一段進程只申請一塊只有一個字節的內存,內存也會分配整整一頁(在i386機器中一頁的缺省大小PACE_SIZE=4096字節)這樣,新建立的共享內存的大小其實是從size這個參數調整而來的頁面大小。即若是size爲1至4096,則實際申請到的共享內存大小爲4K(一頁);4097到8192,則實際申請到的共享內存大小爲8K(兩頁)。size---/proc/sys/kernel/shmmax
shmflg: IPC_CREAT :這個標誌表示應建立一個新的共享內存塊。經過指定這個標誌,咱們能夠建立一個具備指定鍵值的新共享內存塊。
IPC_EXCL:這個標誌只能與 IPC_CREAT 同時使用。當指定這個標誌的時候,若是已有一個具備這個鍵值的共享內存塊存在,則shmget會調用失敗。
指定的共享內存的權限:IPC_CREAT|IPC_EXCL| 0666
返回值:成功返回 就是得到的共享內存的標識符。
失敗, 返回-1.
頭文件:#include <sys/ipc.h>
#include <sys/shm.h>
用法:
int shmid;
key = ftok(".", 'a');
shmid = shmget(key, 1000,IPC_CREAT|IPC_EXCL|0666); 建立
shmid = shmget(key, 1000,0666)打開共享內存:
⑶共享內存的映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
參數: shmid 共享內存標識符
shmaddr:一個指針,指向您但願用於映射該共享內存塊的進程內存地址,若爲 NULL表示讓系統爲用戶自動分配內存
非NULL表示用戶自定義空間
shmflg: SHM_RDONLY 表示對共享內存的操做,只能夠讀。
SHM_RND表示第二個參數指定的地址應被向下靠攏到內存頁面大小的整數倍
0 表示可讀可寫
返回值:成功返回映射後的共享內存塊對應的地址。
失敗返回NULL。
頭文件:#include <sys/ipc.h>
#include <sys/shm.h>
⑷解除映射:
int shmdt(const void *shmaddr);
功能:解除映射。
參數:就是shmat的返回值
成功返回0, 失敗返回-1.
頭文件:#include <sys/types.h>
#include <sys/shm.h>
⑸控制共享內存:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:共享內存的控制函數
參數:shmid: 要操做的共享內存的標識符
cmd: IPC_STAT (獲取對象屬性), 傳遞一個指向一個 struct shmid_ds 對象的指針做爲第三個參數
IPC_SET (設置對象屬性),第三個參數存放將要設置的屬性
IPC_RMID (刪除對象), 此時第三個參數爲NULL。
返回值:成功返回0, 失敗返回-1.
頭文件:#include <sys/ipc.h>
#include <sys/shm.h>
system函數:用於執行shell命令
int system(const char *command);
參數:command 要執行的命令
system("ipcs -m");
system V消息隊列:
#include <sys/msg.h>
一、消息隊列的建立、打開
int msgget(key_t key, int flag);
參數:key, ftok得到值。
flag: IPC_CREAT
IPC_EXCL
msgget(key, IPC_CREAT|IPC_EXCL|0666);
返回值:消息隊列的標識符。
二、int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:往一個消息隊裏中發送消息。
參數: msqid: 要操做的消息隊列的標識符--(對象)
msgp: 存放要發送的消息。指向要發送的消息所在的內存
msgp的定義要求: struc msgbuf
{
long type;//必須有,表示消息的類型
消息的數據
}
msgsz:表示發送的消息的正文的長度(除去消息類型以外)。
sizeof(struct msgbuf) - sizeof(long);
msgflg:決定了消息的發送是不是阻塞方式
IPC_NOWAIT 非阻塞方式發送
0 阻塞等待直到發送成功
返回值:成功返回0, 失敗返回-1
三、ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
功能:從消息隊列中讀取一條消息。
參數: msqid: 要讀取的消息隊列
msgp: 接收消息的緩衝區
struc msgbuf
{
long type;//必須有,表示消息的類型
消息的數據
}
msgsz: 要接收的消息的正文長度。
msgtyp: 要讀取的消息隊列中的消息類型。
0, 表示讀取消息隊列中的第一條消息。
> 0, 表示讀取消息隊列中類型爲msgtyp的消息。
< 0, 接收消息隊列中類型值不小於msgtyp的絕對值且類型值又最小的消息。
msgflg:決定了消息的接收是不是阻塞方式
IPC_NOWAIT 非阻塞方式接收
0 阻塞等待直到有對應消息
返回值:
失敗返回-1
成功返回讀取到消息的正文的字節個數。
四、int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:消息隊列的控制函數
參數:msgqid 要操做的消息隊列的標識符
cmd:
IPC_STAT (獲取對象屬性), 存放在第三參數中
IPC_SET (設置對象屬性),第三個參數存放將要設置的屬性
IPC_RMID (刪除對象), 此時第三個參數爲NULL。
成功返回0, 失敗返回-1.
system V的 信號燈集:以一種計數信號燈
#incldue <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:建立或者打開一個信號燈集合
參數: key ftok得到值。
nsems: 表示要申請的信號燈集合中包含的信號量的個數。
semflg: IPC_CREAT
IPC_EXCL
semget(key, 2, IPC_CREAT|IPC_EXCL|0666);
返回值:返回一個信號燈集合的標識符。
int semctl(int semid, int semnum, int cmd, ...);
功能:對信號燈集合的控制
參數: semid
semnum: 要操做該集合中信號量的編號, 信號量的編號從0開始。
cmd:
GETVAL:獲取信號燈的值
SETVAL:設置信號燈的值 semclt(semid, 0, SETVAL, 共用體);
IPC_RMID:從系統中刪除信號燈集合 semctl(semid, 0, IPC_RMID);
第四個參數:
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
返回值:成功返回0,失敗返回-1.
int semop ( int semid, struct sembuf *opsptr, size_t nops);
功能:信號燈 集合的PV操做。
參數:semid
struct sembuf{
unsigned short sem_num; /* semaphore number */ 要操做的信號量的編號。
short sem_op; /* semaphore operation */ 要執行的PV操做
short sem_flg;/* operation flags */
};
sem_num成員:要操做的信號量的編號。
semop成員: 0 等待,直到信號燈的值變成0
: > 0 表示信號量的值要增長semop個, 至關於V
: < 0 表示信號量的值要較少semop個, 至關於P
flag:
0, semop函數阻塞操做。
IPC_NOWAIT, semop函數阻塞操做。
SEM_UNDO(不經常使用)
nops:semop函數執行一次要操做的信號量的個數。
返回值:成功返回0,失敗返回-1.
問題:
使用syetem V的信號燈集合實現對共享內存的互斥操做?
要求:兩個C文件(一個讀內存、一個寫內存)
須要兩個信號量:一個控制讀內存, 要申請讀的信號量資源(0)。
一個控制寫內存 ,要申請寫的信號量資源(1)。
處理殭屍進程的第三種方法(可移植性較差):
SIGCHLD
signal(SIGCHLD, SIG_IGN);