第7章 Linux編程環(huán)境之進程控制與進程間通信_第1頁
第7章 Linux編程環(huán)境之進程控制與進程間通信_第2頁
第7章 Linux編程環(huán)境之進程控制與進程間通信_第3頁
第7章 Linux編程環(huán)境之進程控制與進程間通信_第4頁
第7章 Linux編程環(huán)境之進程控制與進程間通信_第5頁
已閱讀5頁,還剩106頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領

文檔簡介

1、第7章 第1頁第第7章章 進程控制與進程間通信進程控制與進程間通信第7章 第2頁7.1 進程控制進程控制第7章 第3頁進程與程序進程與程序n 程序程序u指令和數(shù)據(jù)的集合 u存放在磁盤上的一個普通文件里u文件的i節(jié)點中標為可執(zhí)行,內(nèi)容符合系統(tǒng)要求n 進程進程u包括指令段、用戶數(shù)據(jù)段和系統(tǒng)數(shù)據(jù)段的執(zhí)行環(huán)境n 進程和程序的關系進程和程序的關系u程序用于初始化進程的指令段和用戶數(shù)據(jù)段,初始化后,進程和初始化它的程序之間無聯(lián)系u進程運行時磁盤上的程序文件不可修改/刪除u同時運行的多個進程可由同一程序初始化得到,進程之間沒什么聯(lián)系。內(nèi)核通過安排它們共享指令段以節(jié)省內(nèi)存,但這種安排對用戶來說是透明的第7章

2、第4頁進程的組成部分進程的組成部分(1) 四部分:指令段,數(shù)據(jù)段,堆棧段和系統(tǒng)數(shù)據(jù)四部分:指令段,數(shù)據(jù)段,堆棧段和系統(tǒng)數(shù)據(jù)n 指令段指令段(Text)u程序的CPU指令代碼,包括:主程序和子程序編譯后的CPU指令代碼,以及調(diào)用的庫函數(shù)代碼u指令段的大小固定不變,只讀n 用戶數(shù)據(jù)段用戶數(shù)據(jù)段u全局變量,靜態(tài)(static)變量,字符串常數(shù)u允許數(shù)據(jù)段增長和縮小,實現(xiàn)內(nèi)存的動態(tài)分配系統(tǒng)調(diào)用sbrk()允許編程調(diào)整數(shù)據(jù)段的大小內(nèi)存管理庫函數(shù),如:malloc(),free()第7章 第5頁進程的組成部分進程的組成部分(2)n 用戶堆棧段用戶堆棧段u程序執(zhí)行所需要的堆??臻g,實現(xiàn)函數(shù)的調(diào)用用于保存子程

3、序返回地址在函數(shù)和被調(diào)函數(shù)之間傳遞參數(shù)函數(shù)體內(nèi)部定義的變量(靜態(tài)變量除外)umain函數(shù)得到的命令行參數(shù)以及環(huán)境參數(shù)存放在堆棧的最底部main函數(shù)運行之前,這些部分就已經(jīng)被系統(tǒng)初始化u堆棧段的動態(tài)增長與增長限制n 系統(tǒng)數(shù)據(jù)段系統(tǒng)數(shù)據(jù)段u上述三部分在進程私有的獨立的邏輯地址空間內(nèi)u系統(tǒng)數(shù)據(jù)段是內(nèi)核內(nèi)的數(shù)據(jù),每個進程對應一套包括頁表和進程控制塊PCB第7章 第6頁進程邏輯地址空間的布局進程邏輯地址空間的布局 指令段初始化的只讀型數(shù)據(jù)初始化的讀寫型數(shù)據(jù)未初始化的數(shù)據(jù)動態(tài)分配得到的內(nèi)存命令行參數(shù)和環(huán)境參數(shù)工作堆棧用戶堆棧(空閑區(qū))數(shù)據(jù)段指令段低地址高地址棧底棧頂增長方向第7章 第7頁進程的系統(tǒng)數(shù)據(jù)進程

4、的系統(tǒng)數(shù)據(jù)在UNIX內(nèi)核中,含有進程的屬性,包括:u頁表u進程狀態(tài),優(yōu)先級信息u核心堆棧u當前目錄(記錄了當前目錄的i-節(jié)點),根目錄u打開的文件描述符表uumask值u進程PID,PPIDu進程主的實際UID/GID,有效UID/GIDu進程組組號第7章 第8頁user結(jié)構(gòu)和結(jié)構(gòu)和proc結(jié)構(gòu)結(jié)構(gòu)n 進程進程PCB被分為被分為user結(jié)構(gòu)和結(jié)構(gòu)和proc結(jié)構(gòu)兩部分結(jié)構(gòu)兩部分uuser結(jié)構(gòu)(約5000字節(jié)),進程運行時才需要的數(shù)據(jù)在user結(jié)構(gòu)核心態(tài)堆棧占用了較多空間uproc結(jié)構(gòu)(約300字節(jié)),進程不運行時也需要的管理信息存于proc結(jié)構(gòu)u用戶程序不能直接存取和修改進程的系統(tǒng)數(shù)據(jù)u系統(tǒng)調(diào)用

5、可用來訪問或修改這些屬性chdir, umask,open, closesetpgrp, getpid, getppid第7章 第9頁進程的基本狀態(tài)進程的基本狀態(tài)n 基本狀態(tài)基本狀態(tài)u進程創(chuàng)建之后,主要有運行狀態(tài)和睡眠狀態(tài)(也叫阻塞狀態(tài),等待狀態(tài),掛起狀態(tài),等等)u內(nèi)核總是在分時處理運行狀態(tài)的進程,而不顧那些處于睡眠狀態(tài)的進程u睡眠狀態(tài)的進程,在條件滿足后轉(zhuǎn)化為運行狀態(tài)u進程在睡眠時,不占用CPU時間注意:在編程時,程序不要處于忙等待狀態(tài)注意:在編程時,程序不要處于忙等待狀態(tài)第7章 第10頁進程的調(diào)度進程的調(diào)度n 調(diào)度優(yōu)先級調(diào)度優(yōu)先級u內(nèi)核將可運行進程按優(yōu)先級調(diào)度,高優(yōu)先級進程優(yōu)先u進程的優(yōu)先

6、級總在不停地發(fā)生變化u處于睡眠狀態(tài)的進程一旦被叫醒后,被賦以高優(yōu)先級,以保證人機會話操作和其它外設的響應速度u用戶程序用nice()系統(tǒng)調(diào)用有限地調(diào)整進程的優(yōu)先級第7章 第11頁用戶程序庫函數(shù)scanf() . len=read(.); .ttyintr() . if (.) wakeup(.); .read() . if(.)ttyread(.); .系統(tǒng)調(diào)用驅(qū)動程序(下半部分)驅(qū)動程序(上半部分)ttyread() . sleep(.); .main() . scanf(.); .睡眠狀態(tài)硬件中斷喚醒進程進程調(diào)度用戶態(tài)核心態(tài)scanf(%d, &n);程序在串口終端上執(zhí)行時操作員輸入程序在

7、串口終端上執(zhí)行時操作員輸入756然后按下回車然后按下回車第7章 第12頁time:進程執(zhí)行的時間進程執(zhí)行的時間n 進程執(zhí)行時間包括進程執(zhí)行時間包括u睡眠時間,CPU時間(用戶時間和系統(tǒng)時間)u外部命令/usr/bin/timeuB-shell和C-shell都有個內(nèi)部命令time/usr/bin/time find /usr -name *.c -printReal 6.06User 0.36System 2.13uC-shell: time find /usr -name *.h -print0.4u 6.2s 0:10 61% 4+28k 0+0io 0pf+0wn 與與CPU時間有關的命

8、令時間有關的命令vmstat$ vmstat 10 procs memory swap io system cpu r b w swpd free buff cache si so bi bo in cs us sy id 0 0 0 0 55916 6128 38156 0 0 439 118 146 180 8 15 76 0 0 0 0 55252 6160 38160 0 0 0 32 112 54 26 1 73第7章 第13頁系統(tǒng)調(diào)用系統(tǒng)調(diào)用times()n 系統(tǒng)調(diào)用系統(tǒng)調(diào)用times()當前進程CPU時間,已結(jié)束子進程占用過的CPU時間clock_t times(struct t

9、ms *buf);struct tms clock_t tms_utime; /* user CPU time */ clock_t tms_stime; /* system CPU time */ clock_t tms_cutime; /* user CPU time, terminated children */ clock_t tms_cstime; /* system CPU time, terminated children */;n clock()u返回times()的四個CPU時間的總和。單位是1/CLOCKS_PER_SEC秒n getrusage()函數(shù),函數(shù),times(

10、)函數(shù)的升級版本函數(shù)的升級版本u返回CPU時間,還返回表示資源使用狀況的另外14個值,包括內(nèi)存使用情況,I/O次數(shù),進程切換次數(shù)第7章 第14頁與時間有關的函數(shù)與時間有關的函數(shù)n 標準函數(shù)庫中標準函數(shù)庫中time():獲得當前時間坐標:獲得當前時間坐標u坐標0為1970年1月1日零點,單位:秒ut=time(0);和time(&t);都會使t值為當前時間坐標n 函數(shù)函數(shù)gettimeofday()u獲得當前時間坐標,坐標的0是1970年1月1日零點u可以精確到微秒s(10-6秒)n localtime()u將坐標值轉(zhuǎn)換為本地時區(qū)的年月日時分秒n mktimeu將年月日時分秒轉(zhuǎn)換為坐標值n ct

11、ime()和和asctime()u坐標值和年月日時分秒轉(zhuǎn)換為可讀字符串第7章 第15頁忙等待忙等待n 多任務系統(tǒng)中多任務系統(tǒng)中“忙等待忙等待”的程序是不可取的的程序是不可取的 1 #include 2 main(int argc, char *argv) 3 4 unsigned char buf1024; 5 int len, fd; 6 if ( (fd = open(argv1, O_RDONLY) 0)13 write(1, buf, len);14 15 u程序14行前增加sleep(1):1秒定時輪詢uselect()調(diào)用可將睡眠時間設為10毫秒級精度第7章 第16頁fork:創(chuàng)建

12、新進程:創(chuàng)建新進程n 功能功能ufork系統(tǒng)調(diào)用是創(chuàng)建新進程的唯一方式u原先的進程稱做“父進程”,新創(chuàng)建進程被稱作“子進程”u完全復制:新進程的指令,用戶數(shù)據(jù)段,堆棧段u部分復制:系統(tǒng)數(shù)據(jù)段n fork返回值:父子進程都收到返回值,但不相同返回值:父子進程都收到返回值,但不相同u返回值很關鍵,它用于區(qū)分父進程(返回值0,是子進程的PID)和子進程(返回值=0),失敗時返回-1n 內(nèi)核實現(xiàn)內(nèi)核實現(xiàn)u創(chuàng)建新的proc結(jié)構(gòu),復制父進程環(huán)境(包括user結(jié)構(gòu)和內(nèi)存資源)給子進程u父子進程可以共享程序和數(shù)據(jù)(例如:copy-on-write技術),但是系統(tǒng)核心的這些安排,對程序員透明第7章 第17頁fo

13、rk舉例舉例(1)執(zhí)行結(jié)果執(zhí)行結(jié)果1 ./fork1: BEGIN2 a+b=303 a+b=2304 ./fork1: END3 a+b=2304 ./fork1: ENDint a;int main(int argc, char *argv) int b; printf(1 %s: BEGINn, argv0); a = 10; b = 20; printf(2 a+b=%dn, a + b); fork(); a += 100; b += 100; printf(3 a+b=%dn, a + b); printf(4 %s: ENDn, argv0);第7章 第18頁fork舉例舉例(2

14、)執(zhí)行結(jié)果執(zhí)行結(jié)果HelloPID=18378 ppid=18377, a=6ByePID=18377 child=18378, a=6Byemain() int a, ret; printf(Hellon); a = 3; ret = fork(); a += 3; if (ret 0) printf(PID=%d child=%d, a=%dn, getpid(), ret, a); else if (ret = 0) printf(PID=%d ppid=%d, a=%dn, getpid(), getppid(), a); else perror(Create new process)

15、; printf(Byen); 第7章 第19頁命令命令psn 功能功能u查閱進程狀態(tài)(process status)(實際上就是將內(nèi)核中proc和user數(shù)組的內(nèi)容有選擇地打印出來)n 選項選項u用于控制列表的行數(shù)(進程范圍)和列數(shù)(每進程列出的屬性內(nèi)容)u無選項:只列出在當前終端上啟動的進程列出的項目有:PID,TTY,TIME,COMMANDue選項:列出系統(tǒng)中所有的進程(進程范圍)uf選項:以full格式列出每一個進程(控制列的數(shù)目)ul選項:以long格式列出每一個進程(控制列的數(shù)目)第7章 第20頁命令命令ps舉例舉例例:例:ps -efps -ef命令的輸出命令的輸出 UID P

16、ID PPID C STIME TTY TIME COMMANDroot 0 0 0 16:11:14 ? 0:00 schedroot 1 0 0 16:11:14 ? 0:01 /etc/init root 2 0 0 16:11:14 ? 0:00 vhandroot 3 0 0 16:11:14 ? 0:00 bdflushroot 6547 335 0 16:15:05 01 0:00 server 4 1 root 175 1 0 16:11:14 ? 0:00 inetd root 173 1 0 16:11:14 ? 0:00 syslogd root 296 1 0 16:1

17、1:21 ? 0:00 /tcb/files/no_luid/sdd root 6551 291 80 16:17:16 08 0:37 busy_check root 335 1 0 16:11:31 01 0:00 server_ctl root 337 1 0 16:11:33 01 0:05 server_ap root 353 1 0 16:12:01 12 0:00 client_ctl root 356 295 0 16:12:07 12 0:04 client_ap 1403第7章 第21頁命令命令ps列出的進程屬性列出的進程屬性uUID:用戶ID(注冊名)

18、uPID:進程IDuC:CPU占用指數(shù):最近一段時間(秒級別)進程占用CPU情況。不同系統(tǒng)算法不同,例如:正占CPU進程10ms加1,所有進程1秒衰減一半uPPID:父進程的PIDuSTIME:啟動時間uSZ:進程邏輯內(nèi)存大小(Size)uTTY:終端的名字 uCOMMAND:命令名uWCHAN:進程睡眠通道(Wait Channel)uTIME:累計執(zhí)行時間(占用CPU的時間) uPRI:優(yōu)先級uS:狀態(tài),S(Sleep),R(Run), Z(Zombie)第7章 第22頁命令行參數(shù)和環(huán)境參數(shù)命令行參數(shù)和環(huán)境參數(shù)n 位于進程堆棧底部的初始化數(shù)據(jù)位于進程堆棧底部的初始化數(shù)據(jù)n 訪問命令行參數(shù)的

19、方法(訪問命令行參數(shù)的方法(argc,argv)u查閱進程狀態(tài)(process status)(實際上就是將內(nèi)核中proc和user數(shù)組的內(nèi)容有選擇地打印出來)n 訪問環(huán)境參數(shù)的三種方法訪問環(huán)境參數(shù)的三種方法u通過C庫定義的外部變量environumain函數(shù)的第三個參數(shù)uGetenv庫函數(shù)調(diào)用第7章 第23頁訪問環(huán)境參數(shù)的三種方法訪問環(huán)境參數(shù)的三種方法main() extern char *environ; char *p; p = environ; while (*p) printf(”%sn”, *p+);main(int argc, char *argv, char *env) cha

20、r *p; p = env; while (*p) printf(”%sn”, *p+);main() char *p; p=getenv(”HOME”); if (p) printf (”%sn”);n 訪問環(huán)境參數(shù)的三種方法訪問環(huán)境參數(shù)的三種方法第7章 第24頁exec系統(tǒng)調(diào)用系統(tǒng)調(diào)用n 功能功能u用一個指定的程序文件,重新初始化一個進程u可指定新的命令行參數(shù)和環(huán)境參數(shù)(初始化堆棧底部)uexec不創(chuàng)建新進程,只是將當前進程重新初始化了指令段和用戶數(shù)據(jù)段,堆棧段以及CPU的PC指針n 6種格式種格式exec系統(tǒng)調(diào)用系統(tǒng)調(diào)用uexec前綴,后跟一至兩個字母 llist,vvector,een

21、v,ppathul與v:指定命令行參數(shù)的兩種方式,l以表的形式,v要事先組織成一個指針數(shù)組ue:需要指定envp來初始化進程。up:使用環(huán)境變量PATH查找可執(zhí)行文件u六種格式的區(qū)別:不同的參數(shù)方式初始化堆棧底部第7章 第25頁exec系統(tǒng)調(diào)用格式系統(tǒng)調(diào)用格式int execl(char *file,char *arg0,char *arg1,.,0);int execv(char *file, char *argv);int execle(char *file, char *arg0, char *arg1, ., 0, char *envp);int execve(char *file,

22、char *argv, char* envp);int execlp(char *file, char *arg0, char *arg1, ., 0);int execvp(char *file, char *argv);第7章 第26頁“僵尸僵尸”進程進程n “僵尸僵尸”進程進程(zombie或或defunct)u進程生命期結(jié)束時的特殊狀態(tài)系統(tǒng)已經(jīng)釋放了進程占用的包括內(nèi)存在內(nèi)的系統(tǒng)資源,但仍在內(nèi)核中保留進程的部分數(shù)據(jù)結(jié)構(gòu),記錄進程的終止狀態(tài),等待父進程來“收尸”父進程的“收尸”動作完成之后,“僵尸”進程不再存在u僵尸進程占用資源很少,僅占用內(nèi)核進程表資源過多的僵尸進程會導致系統(tǒng)有限數(shù)目的進

23、程表被用光u孤兒進程第7章 第27頁wait系統(tǒng)調(diào)用系統(tǒng)調(diào)用n 功能功能u等待進程的子進程終止u如果已經(jīng)有子進程終止,則立即返回n 函數(shù)原型函數(shù)原型#include #include pid_t wait(int *stat_loc); 函數(shù)返回值為已終止的子進程PIDu例 int status, pid; pid = wait(&status); status中含有子進程終止的原因 TERMSIG(status)為被殺信號 EXITSTATUS(status)為退出碼n waitpid()和和wait3() : wait系統(tǒng)調(diào)用的升級版本系統(tǒng)調(diào)用的升級版本第7章 第28頁字符串庫函數(shù)字符串庫

24、函數(shù)strtokchar char * *strtok(char strtok(char * *str, char , char * *tokens) )功能: 返回第一個單詞的首字節(jié)指針20 09 77 68 6f 20 20 61 6d 20 69 0a 0020 09 77 68 6f 20 20 61 6d 20 69 0a 00 t w h o a m i n 0s執(zhí)行執(zhí)行p=strtok(s, tn);p=strtok(s, tn);之后之后20 09 77 68 6f20 09 77 68 6f 0000 20 61 6d 20 69 0a 0020 61 6d 20 69 0a

25、 00s20 09 77 68 6f 00 20 61 6d 20 09 77 68 6f 00 20 61 6d 0000 69 0a 00 69 0a 00sp執(zhí)行執(zhí)行p=strtok(NULL, tn);p=strtok(NULL, tn);之后之后20 09 77 68 6f 00 20 61 6d20 09 77 68 6f 00 20 61 6d 0000 69 69 0000 00 00s執(zhí)行執(zhí)行p=strtok(NULL, tn);p=strtok(NULL, tn);之后之后pp第7章 第29頁最簡單的最簡單的shell: xsh0main() char buf256, *c

26、md, *argv100; int n, sv; for(;) printf(= ); if (fgets(buf, sizeof(buf), stdin) = NULL) exit(0); cmd = strtok(buf, tn); if (cmd != NULL) continue; if (strcmp(cmd, exit) = 0) exit(0); n = 0; argvn+ = cmd; while (argvn+ = strtok(NULL, tn); if (fork() = 0) execvp(cmd, argv); fprintf(stderr, * Bad comman

27、dn); exit(1); wait(&sv); 第7章 第30頁執(zhí)行執(zhí)行xsh0= who am iwho am iroot ttyp0 Nov 29 09:56= ps -fps -f UID PID PPID C STIME TTY TIME CMDroot 1012 1011 0 09:56:00 ttyp0 00:00:00 login -c -proot 1020 1012 0 09:56:14 ttyp0 00:00:00 -cshroot 1060 1020 2 10:36:37 ttyp0 00:00:00 xsh0root 1062 1060 4 10:36:46 ttyp

28、0 00:00:00 ps -f= ls -l ls -l * *.c.cls: *.c not found: No such file or directory (error 2)= mmxmmx* Bad command= ls -l xsh0ls -l xsh0-rwxr-r- 1 root other 43417 Dec 1 1998 xsh0= exitexit第7章 第31頁system:運行一個命令:運行一個命令 int system(char *string);n 庫函數(shù)system執(zhí)行用字符串傳遞的shell命令,可使用管道符和重定向n 庫函數(shù)system()是利用系統(tǒng)調(diào)用f

29、ork,exec,wait實現(xiàn)的第7章 第32頁system應用舉例應用舉例 #include main() char fname256, cmd256, buf256; FILE *f; tmpnam(fname); sprintf(cmd, netstat -rn %s, fname); printf(Execute %sn, cmd); system(cmd); f = fopen(fname, r); while (fgets(buf, sizeof buf, f) printf(%s, buf); fclose(f); printf(Remove file %sn, fname);

30、unlink(fname);第7章 第33頁7.2 進程與文件描述符進程與文件描述符第7章 第34頁活動文件目錄活動文件目錄AFDn 磁盤文件目錄磁盤文件目錄(分兩級分兩級)u文件名,i節(jié)點n 活動文件目錄活動文件目錄(分三級分三級)u文件描述符表FDT:每進程一張,user結(jié)構(gòu)中user結(jié)構(gòu)中整型數(shù)組u_ofile記錄進程打開的文件文件描述符fd是u_ofile數(shù)組的下標u系統(tǒng)文件表SFT:整個核心一張,file結(jié)構(gòu) struct file char f_flag; /* 讀、寫操作要求 */ char f_count; /* 引用計數(shù) */ int f_inode; /* 內(nèi)核中inode

31、數(shù)組的下標 */ long f_offset2; /* 文件讀/寫位置指針 */ ;u活動i節(jié)點表:整個核心一張,inode結(jié)構(gòu)內(nèi)存中inode表是外存中inode的緩沖 第7章 第35頁活動文件目錄活動文件目錄AFD(圖圖)0 01 12 23 3FDTFDT進程進程A A0 01 12 23 3進程進程B B0 01 12 23 3進程進程C CSFTSFTInodeInode第7章 第36頁文件描述符的繼承與關閉文件描述符的繼承與關閉n fork創(chuàng)建的子進程繼承父進程的文件描述符表創(chuàng)建的子進程繼承父進程的文件描述符表n 父進程在父進程在fork前打開的文件,父子進程有相同的前打開的文件,

32、父子進程有相同的文件偏移文件偏移父進程父進程父進程父進程子進程子進程forkfork前前forkfork后后第7章 第37頁例例:文件描述符的繼承與關閉文件描述符的繼承與關閉(1)#include void main() int fd; static char *str = F1:ABCDn; fd = open(abc, O_CREAT | O_WRONLY, 0666); write(fd, str, strlen(str); if (fork() 0) write(fd, str, strlen(str); close(fd); else char fdstr16; sprintf(fd

33、str, %d, fd); execlp(f2, f2, fdstr, 0); printf(failed to start f2n); exit(0); 第7章 第38頁例例:文件描述符的繼承與關閉文件描述符的繼承與關閉(2)n文件文件f2.cf2.cint main(int argc, char *argv) int fd; static char *str=F2:WXYZn; fd = strtol(argv1, 0, 0); write(fd, str, strlen(str); close(fd);第7章 第39頁close-on-exec標志標志n 文件文件close-on-exe

34、c標志標志u文件描述符fd設置了close-on-exec標志,執(zhí)行exec系統(tǒng)調(diào)用時,系統(tǒng)會自動關閉這些文件n 函數(shù)函數(shù)#include int fcntl (fd, cmd, arg);cmd: F_GETFD 獲取文件fd的控制字 控制字的比特0(FD_CLOEXEC): close-on-exec標志位 flag = fcntl(fd, F_GETFD, 0);cmd: F_SETFD 設置文件fd的控制字 fcntl(fd, F_SETFD, flag);n 例例flags = fcntl(fd, F_GETFD, 0);flags |= FD_CLOEXEC;fcntl(fd, F

35、_SETFD, flags);第7章 第40頁文件描述符的復制文件描述符的復制n 系統(tǒng)調(diào)用系統(tǒng)調(diào)用 int dup2(int fd1, int fd2);n 功能功能復制文件描述符fd1到fd2ufd2可以是空閑的文件描述符u如果fd2是已打開文件,則關閉已打開文件fd2fd2fd1fd1第7章 第41頁xsh1:輸入輸出重定向輸入輸出重定向(1) char buf256, *cmd, *argv100, *infile, *outfile; int n, sv; for (;) printf(= ); if (fgets(buf, sizeof(buf), stdin) = NULL) ex

36、it(0); infile = strstr(buf, ); if (infile) *infile = 0; infile = strtok(infile + 1, tn); if (outfile) *outfile = 0; outfile = strtok(outfile + 1, tn); cmd = strtok(buf, tn); if (cmd = NULL) continue; if(strcmp(cmd,exit)=0) exit(0); n=0; argvn+=cmd; while(argvn+=strtok(NULL, tn); 第7章 第42頁xsh1:輸入輸出重定向

37、輸入輸出重定向(2) if (fork() = 0) int fd0 = -1, fd1 = -1; if (infile != NULL) fd0 = open(infile, O_RDONLY); if (outfile != NULL) fd1 = open(outfile, O_CREAT | O_WRONLY, 0666); if(fd0 != -1) dup2(fd0, 0); if(fd1 != -1) dup2(fd1, 1); close(fd0); close(fd1); execvp(cmd, argv); fprintf(stderr, * Bad commandn);

38、 exit(1); wait(&sv); 第7章 第43頁管道操作管道操作(1)n 創(chuàng)建管道創(chuàng)建管道 int pipe(int pfd2)u創(chuàng)建一個管道,pfd0和pfd1分別為管道兩端的文件描述字,pfd0用于讀,pfd1用于寫n 管道寫管道寫ret = write(pfd1, buf, n)u若管道已滿,則被阻塞,直到管道另一端read將已進入管道的數(shù)據(jù)取走為止u管道容量:某一有限值,如8192字節(jié)第7章 第44頁管道操作管道操作(2)n 管道讀管道讀ret = read(pfd0, buf, n)u若管道寫端已關閉,則返回0u若管道為空,且寫端文件描述字未關閉,則被阻塞u若管道不為空(設

39、管道中實際有m個字節(jié))nm,則讀m個;如果nm則讀取n個u實際讀取的數(shù)目作為read的返回值。u注意:管道是無記錄邊界的字節(jié)流通信n 關閉管道關閉管道closeu關閉寫端則讀端read調(diào)用返回0。u關閉讀端則寫端write導致進程收到SIGPIPE信號(默認處理是終止進程,該信號可以被捕捉)寫端write調(diào)用返回-1,errno被設為EPIPE第7章 第45頁父子進程管道通信父子進程管道通信n 父進程父進程pfd0pfd1pfd0pfd1pfd0pfd1pfd0pfd1pfd0pfd1n fork后后n 父進程關閉讀端,父進程關閉讀端,子進程關閉寫端子進程關閉寫端第7章 第46頁管道通信:寫端

40、管道通信:寫端/* File name : pwrite.c */#define syserr(str) perror(str); exit(1); main() int pfd2; char fdstr10, *message = Send/Receive data using pipe; if (pipe(pfd) = -1) syserr(Create pipe); switch(fork() case -1: syserr(fork); case 0: /* child */ close(pfd1); sprintf(fdstr, %d, pfd0); execlp(./pread,

41、pread, fdstr, 0); syserr(Execute pread file); break; default: /* parent */ close(pfd0); if(write(pfd1, message, strlen(message) + 1) = -1) perror(Write pipe); break; 第7章 第47頁管道通信:讀端管道通信:讀端/* File name : pread.c */#define syserr(str) perror(str); exit(1); main(int argc, char *argv) int fd, nbyte, i;

42、char buf1024; fd = strtol(argv1, 0, 0); nbyte = read(fd, buf, sizeof(buf); switch (nbyte) case -1: syserr(Read pipe); break; case 0: printf(Pipe closedn); break; default: printf(%d bytes %sn, nbyte, buf); break; exit(0);第7章 第48頁管道通信:應注意的問題管道通信:應注意的問題n 管道傳輸是一個無記錄邊界的字節(jié)流管道傳輸是一個無記錄邊界的字節(jié)流u寫端一次write所發(fā)數(shù)據(jù)讀端

43、可能需多次read才能讀取u寫端多次write所發(fā)數(shù)據(jù)讀端可能一次read就全部讀出n 父子進程需要雙向通信時,應采用兩個管道父子進程需要雙向通信時,應采用兩個管道u用一個管道,進程可能會收到自己剛寫到管道去的數(shù)據(jù)u增加其他同步方式太復雜n 父子進程使用兩個管道傳遞數(shù)據(jù),有可能死鎖父子進程使用兩個管道傳遞數(shù)據(jù),有可能死鎖u父進程因輸出管道滿而寫,導致被阻塞u子進程因要向父進程寫回足夠多的數(shù)據(jù)而導致寫也被阻塞,這時死鎖發(fā)生u多進程通信問題必須仔細分析流量控制和死鎖問題n 管道的缺點:沒有記錄邊界管道的缺點:沒有記錄邊界第7章 第49頁命名管道命名管道n pipe創(chuàng)建的管道的缺點創(chuàng)建的管道的缺點只

44、限于同祖先進程間通信n 命名管道命名管道允許不相干的進程(沒有共同的祖先)訪問FIFO管道n 命名管道的創(chuàng)建命名管道的創(chuàng)建u用命令 mknod pipe0 pu創(chuàng)建一個文件,名字為pipe0u用ls -1列出時,文件類型為pn 發(fā)送者發(fā)送者fd = open(”pipe0”, O_WRONLY);write(fd, buf, len);n 接收者接收者fd = open(”pipe0”, O_RDONLY);read(fd, buf, sizeof(buf);第7章 第50頁xsh2:管道管道(1)main() char buf256, *buf2, *cmd, *cmd2, *argv64,

45、 *argv264; int n, sv, fd2; for (;) printf(= ); if (fgets(buf, sizeof(buf), stdin) = NULL) exit(0); buf2 = strstr(buf, |); if (!buf2) exit(0); *buf2+ = 0; cmd = strtok(buf, tn); if (!cmd) exit(1); n = 0; argvn+ = cmd; while (argvn+ = strtok(NULL, tn); cmd2 = strtok(buf2, tn); if (!cmd2) exit(1); n =

46、0; argv2n+ = cmd2; while (argv2n+ = strtok(NULL, tn);第7章 第51頁xsh2:管道管道(2) pipe(fd); if (fork() = 0) dup2(fd0, 0); close(fd0); close(fd1); execvp(cmd2, argv2); fprintf(stderr, * bad commandn); exit(1); else if (fork() = 0) dup2(fd1, 1); close(fd0); close(fd1); execvp(cmd, argv); fprintf(stderr, * bad

47、 commandn); exit(1); close(fd0); close(fd1); wait(&sv); wait(&sv); 第7章 第52頁第二次上機作業(yè)第二次上機作業(yè)使用使用fork(), exec(), dup2(), pipe() ,open()系統(tǒng)調(diào)系統(tǒng)調(diào)用完成與下列用完成與下列shell命令等價的功能。命令等價的功能。grep v usr result.txt第7章 第53頁7.3 信號信號第7章 第54頁命令命令killn 用法與功能用法與功能kill signal PID-listkill命令用于向進程發(fā)送一個信號n 舉例舉例ukill 1275向進程1275的進程發(fā)送

48、信號,默認信號為15(SIGTERM),一般會導致進程死亡ukill -9 1326向進程1326發(fā)送信號9(SIGKILL),導致進程死亡第7章 第55頁進程組進程組n 進程組進程組u進程在其proc結(jié)構(gòu)中有p_pgrp域up_pgrp都相同的進程構(gòu)成一個“進程組”u如果p_pgrp=p_pid則該進程是組長usetsid()系統(tǒng)調(diào)用將proc中的p_pgrp改為進程自己的PID,從而脫離原進程組,成為新進程組的組長 ufork創(chuàng)建的進程繼承父進程p_pgrp,與父進程同組n 舉例舉例ukill命令的PID為0時,向與本進程同組的所有進程發(fā)送信號第7章 第56頁信號機制信號機制n 功能功能u

49、信號是送到進程的“軟件中斷”,通知進程出現(xiàn)了非正常事件n 信號的產(chǎn)生信號的產(chǎn)生u進程自己或者其他進程發(fā)出的使用kill()或者alarm()調(diào)用u核心產(chǎn)生信號段違例信號SIGSEGV:當進程試圖存取它的地址空間以外的存貯單元時,內(nèi)核向進程發(fā)送段違例信號浮點溢出信號SIGFPE:零做除數(shù)時,內(nèi)核向進程發(fā)送浮點溢出信號信號SIGPIPE:關閉管道讀端則寫端write導致進程收到信號SIGPIPE第7章 第57頁信號類型信號類型n 在在文件中定義宏文件中定義宏SIGTERM 軟件終止信號。用kill命令時產(chǎn)生SIGHUP 掛斷。當從注冊shell中l(wèi)ogout時, 同一進程組的所有進程都收到SIGH

50、UPSIGINT 中斷。用戶按Del鍵或Ctrl-C鍵時產(chǎn)生SIGQUIT 退出。按Ctrl-時產(chǎn)生,產(chǎn)生core文件SIGALRM 鬧鐘信號。計時器時間到,與alarm()有關SIGCLD 進程的一個子進程終止。SIGKILL 無條件終止,該信號不能被捕獲或忽略。SIGUSR1,SIGUSR2 用戶定義的信號SIGFPE 浮點溢出SIGILL 非法指令 SIGSEGV 段違例第7章 第58頁進程對信號的處理進程對信號的處理n 進程對到達的信號可以在下列處理中選取一種進程對到達的信號可以在下列處理中選取一種u信號被忽略u設置為缺省處理方式u信號被捕捉用戶在自己的程序中事先定義好一個函數(shù),當信號

51、發(fā)生后就去執(zhí)行這一函數(shù)n 信號被設為缺省處理方式信號被設為缺省處理方式 signal(SIGINT,SIG_DFL);n 信號被忽略信號被忽略 signal(SIGINT,SIG_IGN);在執(zhí)行了這個調(diào)用后,進程就不再收到SIGINT信號注意:被忽略了的信號作為進程的一種屬性會被它的子進程所繼承第7章 第59頁信號被忽略:舉例信號被忽略:舉例/* File name : abc.c */#include main() signal(SIGTERM, SIG_IGN); if (fork() for(;) sleep(100); else execlp(”xyz”, ”xyz”, 0);/*

52、File name : xyz.c */main() int i = 0; for (;) printf(”%dn”, i+); sleep(1); n 單獨運行xyz時,使用kill -15命令可殺死該進程n 由abc進程來啟動的xyz進程就不能用kill -15命令殺死第7章 第60頁信號的捕捉信號的捕捉n 信號被捕捉并由一個用戶函數(shù)來處理信號被捕捉并由一個用戶函數(shù)來處理n 信號到達時,這個函數(shù)將被調(diào)用來處理那個信號信號到達時,這個函數(shù)將被調(diào)用來處理那個信號#include sig_handle(int sig) printf(”HELLO! Signal %d catched.n”,si

53、g);main() signal(SIGINT, sig_handle); signal(SIGQUIT, sig_handle); for(;) sleep(500);n 注意:在信號的用戶函數(shù)被調(diào)用之前,自動被重置到注意:在信號的用戶函數(shù)被調(diào)用之前,自動被重置到它的缺省行為。它的缺省行為。第7章 第61頁僵尸進程僵尸進程n 僵尸子進程僵尸子進程u子進程終止,僵尸進程(defunct或zombie)出現(xiàn),父進程使用wait系統(tǒng)調(diào)用收尸后消除僵尸n 僵尸進程對資源的占用僵尸進程對資源的占用u僵尸進程不占用內(nèi)存資源等但占用內(nèi)核proc表項,僵尸進程太多會導致proc表耗盡而無法再創(chuàng)建新進程n 子

54、進程中止后的通知機制子進程中止后的通知機制u子進程中止后,系統(tǒng)會向父進程發(fā)送信號SIGCLDn 不導致僵尸子進程出現(xiàn)的方法不導致僵尸子進程出現(xiàn)的方法u忽略對SIGCLD信號的處理 signal(SIGCLD,SIG_IGN);u捕獲SIGCLD信號,執(zhí)行wait系統(tǒng)調(diào)用第7章 第62頁捕獲信號捕獲信號SIGCLDSIGCLDint subprocess_die(int sig) int pid, status; pid = wait(&status); . . . signal(SIGCLD, subprocess_die); int main() signal(SIGCLD, subproc

55、ess_die); . . .第7章 第63頁發(fā)送信號發(fā)送信號n 系統(tǒng)調(diào)用系統(tǒng)調(diào)用kill int kill(int pid, int sig) 返回值: 0-成功 -1-失敗n kill調(diào)用分幾種情況調(diào)用分幾種情況u當pid0時,向指定的進程發(fā)信號u當pid=0時,向與本進程同組的所有進程發(fā)信號u當pid0時,將時鐘設置成secs指定的秒數(shù)當secs=0時,關閉報警時鐘。第7章 第66頁alarm系統(tǒng)調(diào)用舉例系統(tǒng)調(diào)用舉例#include char cmd100;default_cmd(int sig) printf(CMD_An); strcpy(cmd, CMD_A);main() sig

56、nal(SIGALRM, default_cmd); alarm(5); printf(Input command : ); scanf(%s, cmd); alarm(0); printf(cmd=%sn, cmd); . .第7章 第67頁全局跳轉(zhuǎn)全局跳轉(zhuǎn)(1)main() int c; for (;) printf(”Input a command:”); c = getchar(); switch(c) case Q: return(0); break; case A: func_a(); break; case B: func_b(); break; . . . n 問題問題u用戶在

57、輸入了命令A后反悔,希望中止func_a()對命令A的處理,而要重新選擇另一條命令第7章 第68頁全局跳轉(zhuǎn)全局跳轉(zhuǎn)(2)#include void main_control(int sig) int c; signal(SIGINT, main_control); for (;) printf(”Input your choice:”); scanf(”%d”, &c); switch (c) case 0: return; break; case 1: func_1(); break; case 2: func_2(); break; . . main() main_control();第7

58、章 第69頁全局跳轉(zhuǎn)全局跳轉(zhuǎn)(3)n 上述程序存在的問題上述程序存在的問題u每次按下中斷鍵,程序停留在信號捕捉函數(shù)中,堆棧沒有清理,嵌套越來越深浪費越來越大umain_control()一旦返回,進程的執(zhí)行將彈回到剛才被SIGINT中斷的地方恢復剛才的執(zhí)行會讓用戶感到迷惑不解,剛才的動作已打斷而且又已經(jīng)開始了新的工作,可過一段時間后又開始運行uC語言中的goto語句只限于一個函數(shù)體內(nèi)使用,不能解決上述的問題第7章 第70頁全局跳轉(zhuǎn)全局跳轉(zhuǎn)(4)n 解決方法解決方法u把?;謴蜑榈谝淮握{(diào)用main_control()時的狀態(tài),再調(diào)用main_control()去重新執(zhí)行n 有兩個標準的子例程用于這

59、個目的有兩個標準的子例程用于這個目的#include int setjmp(jmp_buf jmpenv); /* 返回值為零,或者是longjmp提供的val */void longjmp(jmp_buf jmpenv, int val) /* val是提供給setjmp作返回值*/第7章 第71頁全局跳轉(zhuǎn)全局跳轉(zhuǎn)(5)#include #include static jmp_buf jmpbuf;void intr_proc(int sig) printf(”n.INTERRUPTED by signal %dn”, sig) longjmp(jmpbuf, 1);main() int c

60、; setjmp(jmpbuf); signal(SIGINT, intr_proc); for(;) printf(”Input a command:”); scanf(“%d”, &c); switch(c) case 0: return; break; case 1: func_1(); break; case 2: func_2(); break; 第7章 第72頁7.4 消息隊列消息隊列第7章 第73頁消息隊列的創(chuàng)建消息隊列的創(chuàng)建n 系統(tǒng)調(diào)用系統(tǒng)調(diào)用msgget #include #include #include int msgget(key_t key, int flags);u

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論