這學期感受最有難度的就是操做系統這門課了,之前Linux接觸的很少,雙學位跳級學習課程暫時尚未學完其中的基礎課。因此可能錯誤多一些。
併發
一、 相關頭文件函數
<unistd.h> <sys/types.h> <sys/wait.h> <stdio.h> <stdlib.h>學習
二、 函數說明測試
l fork(創建一個新的進程)this
定義函數 pid_t fork(void);操作系統
函數說明 fork()會產生一個新的子進程,其子進程會複製父進程的數據與堆棧空間,並繼承父進程的用戶代碼,組代碼,環境變量、已打開的文件代碼、工做目錄和資源限制等。線程
返回值 若是fork()成功則在父進程會返回新創建的子進程代碼(PID),而在新創建的子進程中則返回0。若是fork 失敗則直接返回-1,失敗緣由存於errno中。指針
l waitpid(等待子進程中斷或結束)blog
定義函數 pid_t waitpid(pid_t pid,int * status,int options);繼承
函數調用 waitpid(pid, NULL, 0);
函數說明 waitpid()會暫時中止目前進程的執行,直到有信號來到或子進程結束。若是在調用waitpid()時子進程已經結束,則wait()會當即返回子進程結束狀態值。子進程的結束狀態值會由參數status返回,而子進程的進程識別碼也會一快返回。若是不在乎結束狀態值,則參數status能夠設成NULL。參數pid爲欲等待的子進程識別碼,其餘數值意義以下:
pid<-1 等待進程組識別碼爲pid絕對值的任何子進程。
pid=-1 等待任何子進程,至關於wait()。
pid=0 等待進程組識別碼與目前進程相同的任何子進程。
pid>0 等待任何子進程識別碼爲pid的子進程。
返回值 若是執行成功則返回子進程識別碼(PID),若是有錯誤發生則返回-1。失敗緣由存於errno
l getpid(取得進程識別碼)
定義函數 pid_t getpid(void);
函數說明 getpid()用來取得目前進程的進程識別碼,許多程序利用取到的此值來創建臨時文件,以免臨時文件相同帶來的問題。
返回值 目前進程的進程識別碼
l exit(正常結束進程)
定義函數 void exit(int status);
函數說明 exit()用來正常終結目前進程的執行,並把參數status返回給父進程,而進程全部的緩衝區數據會自動寫回並關閉未關閉的文件。
l execl(執行文件)
定義函數 int execl(const char * path,const char * arg,....);
函數說明 execl()用來執行參數path字符串所表明的文件路徑,接下來的參數表明執行該文件時傳遞過去的argv(0)、argv[1]……,最後一個參數必須用空指針(NULL)做結束。
返回值 若是執行成功則函數不會返回,執行失敗則直接返回-1,失敗緣由存於errno中。
調用ls命令範例: execl("/bin/ls", "/bin/ls", "-l" , "/etc", NULL);
安裝VMware Workstation Pro,下載Fedora Linux鏡像文件,安裝成功後以下圖。打開終端,輸入命令su進入管理員帳戶,輸入yum install gcc 安裝gcc編譯器,測試編譯環境正常;
單個子進程的建立分析:經過gcc first.c進行編譯,在.a/.out執行輸出,例程剛開始輸出父進程pid 2218
#include<unistd.h>
#include<stdarg.h>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int tprintf (const char*fmt, ...);
int main(void)
{
int i=0 ,j=0;
pid_t pid;
printf("Hello from Parent Process, PID is %d.\n", getpid());
pid = fork();
if(pid == 0) //child process
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process %d. %d times\n", getpid(), i+1);
sleep(1);
}
}
else if(pid != -1) //parent process
{
tprintf("Parent forked one child process--%d.\n",pid);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid,NULL,0);
tprintf("Child Process has exited.\n");
tprintf("Parent had exited.\n");
}
else
{
tprintf("Everything was done without error.\n");
}
return 0;
}
waitpid()會暫時中止目前進程的執行,直到有信號來到或子進程結束。若是在調用waitpid()時子進程已經結束,則wait()會當即返回子進程結束狀態值。子進程的結束狀態值會由參數status返回,而子進程的進程識別碼也會一快返回。若是不在乎結束狀態值,則參數status能夠設成NULL。
int tprintf(const char*fmt, ...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime(&tsec);
printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
當pid=-1時建立進程失敗輸出錯誤時間、PID等錯誤信息。最後運行結果以下:
在子進程中調用外部命令;
#include<unistd.h>
#include<stdarg.h>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int tprintf (const char*fmt, ...);
int main(void)
{
pid_t pid;
pid = fork();
if(pid == 0) //child process
{
sleep(5);
tprintf("Hello from Child Process!\n");
tprintf("I am calling exec.\n");
execl("/bin/ps","-a",NULL);
tprintf("You should never see this because the child is already gone.\n");
}
int execl(const char * path,const char * arg,....)用來執行參數path字符串所表明的文件路徑,接下來的參數表明執行該文件時傳遞過去的argv(0)、argv[1]……,最後一個參數必須用空指針(NULL)做結束。返回值:若是執行成功則函數不會返回,執行失敗則直接返回-1,失敗緣由存於errno中;
else if(pid != -1) //parent process
{
tprintf("Hello from Parent,pid %d.\n", getpid());
sleep(1);
tprintf("Parent forked process %d.\n",pid);
sleep(1);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid,NULL,0);
tprintf("Parent had exited.\n");
}
sleep是讓線程指定休眠時間,而後繼續工做 wait則是等待,直到有線程通知notify()喚醒他纔會從新工做;
else
{
tprintf("Everything was done without error.\n");
}
return 0;
}
int tprintf(const char*fmt, ...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime(&tsec);
printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
編寫一段程序實現如下功能:
使用系統調用fork()建立兩個子進程
各個子進程顯示和輸出一些提示信息和本身的進程標識符。
父進程顯示本身的進程ID和一些提示信息,而後調用waitpid()等待多個子進程結束,並在子進程結束後顯示輸出提示信息表示程序結束。
#include<unistd.h>
#include <stdarg.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int tprintf(const char*fmt,...);
int main()
{
int i=0,j=0;
pid_t pid1;
pid_t pid2;
printf("Hello from Parent Process,PID is %d.\n",getpid());
pid1 = fork();
if(pid1 == 0)
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process one %d. %d times\n",getpid(),i+1);
sleep(1);
}
}
else if(pid1 !=-1)
{
tprintf("Part forked one child process--%d.\n",pid1);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid1,NULL,0);
pid2 = fork();
if(pid2 == 0)
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process two %d. %d times\n",getpid(),i+1);
sleep(1);
}
}
else if(pid2 !=-1)
{
tprintf("Part forked one child process--%d.\n",pid2);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid2,NULL,0);
tprintf("Child Process has exited.\n");
tprintf("Parent had exited.\n");
}
}
else tprintf("Everything was done without error.\n");
return 0;
}
int tprintf(const char*fmt,...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime (&tsec);
printf("%02d:%02d:%02d:%5d",tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec,
getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
經過父進程建立一個子進程,運行一個子進程,再建立一個子進程,再運行一個子進程的方式,輸出各個進程的標識碼,能夠獲得正確的結果。而當兩個子進程同時fork()時,會產生3個子進程,這是應爲2個fork()放在一塊兒時,會多fork()一個子進程。
建立多個(3個以上)進程併發運行,控制好各個子進程輸出本身的進程標識符和一些提示信息
#include<unistd.h>
#include<stdarg.h>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int tprintf (const char*fmt, ...);
int main(void)
{
int i=0 ,j=0;
pid_t pid,pid1,pid2;
printf("Hello from Parent Process, PID is %d.\n", getpid());
pid = fork();//fork1
if(pid == 0) //child process 1
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process 11111 %d. %d times\n", getpid(), i+1);
sleep(5);
}
}
else if(pid != -1) //parent process
{
tprintf("Parent forked 111 child process--%d.\n",pid);
pid1 = fork();//fork2
if(pid1 == 0) //child process 2
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process 22222 %d. %d times\n", getpid(), i+1);
sleep(5);
}
}
else if(pid1 != -1) //parent process
{
tprintf("Parent forked 222 child process--%d.\n",pid);
pid2 = fork();//fork3
if(pid2 == 0) //child process 3
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process 33333 %d. %d times\n", getpid(), i+1);
sleep(5);
}
}
else if(pid2 != -1) //parent process
{
tprintf("Parent forked 333 child process--%d.\n",pid);
//wait for children
pid_t temp_p;
while((temp_p = waitpid(-1, NULL, 0)) > 0)
{
tprintf("child had exited %d \n",temp_p);
}
tprintf("Parent had exited.\n");
}
else
{
tprintf("3333 Everything was done without error.\n");
}
}
else
{
tprintf("2222 Everything was done without error.\n");
}
}
else
{
tprintf("1111 Everything was done without error.\n");
}
return 0;
}
int tprintf(const char*fmt, ...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime(&tsec);
printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
輸出結果與設想中的順序不一樣。由於佔用內存的進程不會由於wait pid中的PID參數而固定不變,而是三個子進程會按照第一次運行的順序繼續運行,也就是第一次運行的順序固定以後,後面的運行順序再也不改變。所以輸出順序結果以下圖:
做者:Nathaneko