【HUST】网安|操作系统实验|实验二 进程管理与死锁
目的
1)理解進程/線程的概念和應用編程過程;
2)理解進程/線程的同步機制和應用編程;
任務
1)在Linux下創建一對父子進程。
2)在Linux下創建2個線程A和B,循環輸出數據或字符串。
3)在Windows下創建線程A和B,循環輸出數據或字符串。
4)在Linux下創建一對父子進程,實驗wait同步函數。
5)在Windows下利用線程實現并發畫圓/畫方。
6)在Windows或Linux下利用線程實現“生產者-消費者”同步控制
7)在Linux下利用信號機制實現進程通信。
8)在Windows或Linux下模擬哲學家就餐,提供死鎖和非死鎖解法。
1/6/8選做,其余任選其一。
任務1 在Linux下創建一對父子進程。
1. 提示
提示1:分別輸出各自的進程號,父進程號和特別的提示字符串信息
提示2:讓父進程提前結束或后結束,觀察子進程的父進程ID。
提示3:使用PS命令查看進程列表信息,核對進程號,父進程號
2. 任務代碼
#include<unistd.h>
#include<stdio.h>
int main()
{
printf("'-'代表父進程輸出信息;'+'代表子進程輸出信息。\n");
pid_t p1=fork();
if(p1){
printf("- 1. 父進程進程號:%d\n",getpid());
printf("- 1. 創建的子進程進程號:%d\n",p1);
printf("- 3. 暫時掛起父進程以便ps檢查結果(10s):\n");
sleep(10);
printf("- 2. 父進程結束\n");
}else{
printf("+ 1. 子進程進程號:%d\n",getpid());
printf("+ 1. 父進程未結束時,子進程的父進程進程號:%d\n",getppid());
printf("+ 3. 暫時掛起子進程以便ps檢查結果(11s):\n");
sleep(11);
printf("+ 2. 父進程結束后,子進程的父進程進程號:%d\n",getppid());
printf("+ 3. 子進程繼續掛起,同時再用ps檢查結果(10s):\n");
sleep(10); //為了讓子進程更晚結束
printf("+ 3. 子進程結束\n");
}
return 0;
}
ps檢查指令(我用gcc編譯后的程序名稱叫做a.out):
ps -ef|grep a.out
3. 結果及說明
圖中輸出信息的標號分別對應不同的任務提示。
圖中ps結果的第一列為用戶名,第二列為pid,第三列為父進程pid。
- 第一次ps檢查時:父進程號為27236,子進程號為27237,且子進程的父進程號為27236。
- 第二次ps檢查時:父進程已經結束,子進程掛起,它的父進程號變為1。即交給init程序。
- 第三次ps檢查時:父子進程都結束,ps顯示沒有27236、27237的進程在運行。
但是程序似乎并沒有結束。為啥?
任務6 在Windows或Linux下利用線程實現“生產者-消費者”同步控制
1. 提示
提示1:使用數組(10個元素)代替緩沖區。2個輸入線程產生產品(隨機數)存到數組中;3個輸出線程從數組中取數輸出。
提示2: Windows使用臨界區對象和信號量對象,主要函數
EnterCriticalSection | LeaveCriticalSection | WaitForSingleObject | ReleaseSemaphore
提示3:Linux使用互斥鎖對象和輕量級信號量對象,主要函數:
sem_wait( ),sem_post( ),pthread_mutex_lock( ),
pthread_mutex_unlock( )
提示4:生產者1的數據:1000-1999 (每個數據隨機間隔100ms-1s),生
產者2的數據:2000-2999 (每個數據隨機間隔100ms-1s)
提示5:消費者每休眠100ms-1s的隨機時間消費一個數據。
提示6:屏幕打印(或日志文件記錄)每個數據的生產和消費記錄。
2. 任務代碼
Linux:
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<sys/syscall.h>
#include<semaphore.h>
int share[10]; //共享緩沖區
int indexc=0; //生產者處理的標號(臨界資源)
int indexp=0; //消費者處理的標號(臨界資源)
sem_t is_empty; //緩沖區中存在空位
sem_t is_full; //緩沖區已滿
pthread_mutex_t mutexc = PTHREAD_MUTEX_INITIALIZER; //互斥鎖
pthread_mutex_t mutexp = PTHREAD_MUTEX_INITIALIZER; //互斥鎖
void* consume(void* arg){
int add=(int)arg;
while(1){
sem_wait(&is_full);
pthread_mutex_lock(&mutexc);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int data=ts.tv_nsec%1000+add;
share[indexc]=data;
float sleepTime=0.001*(ts.tv_nsec%900)+0.1;
printf("生產者%ld填第%d塊:%d,即將沉睡%fs\n",syscall(SYS_gettid),indexc,data,sleepTime);
indexc++;
if(indexc>9)indexc-=10;
pthread_mutex_unlock(&mutexc);
sem_post(&is_empty);
sleep(sleepTime);
}
}
void* produce(void){
while(1){
sem_wait(&is_empty);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
float sleepTime=0.001*(ts.tv_nsec%900)+0.1;
pthread_mutex_lock(&mutexp);
printf("消費者%ld取第%d塊:%d, 即將沉睡%fs\n",syscall(SYS_gettid),indexp,share[indexp],sleepTime);
indexp++;
if(indexp>9)indexp-=10;
pthread_mutex_unlock(&mutexp);
sem_post(&is_full);
sleep(sleepTime);
}
}
int main(){
sem_init(&is_empty,0,0);//初值為0,用于同步生產者和消費者
sem_init(&is_full,0,9); //初值為9,表示初始緩沖區填10個滿
pthread_t idc1,idc2;
pthread_t idp1,idp2,idp3;
pthread_create(&idc1,NULL,(void*)consume,(void*)1000);
pthread_create(&idc2,NULL,(void*)consume,(void*)2000);
pthread_create(&idp1,NULL,(void*)produce,NULL);
pthread_create(&idp2,NULL,(void*)produce,NULL);
pthread_create(&idp3,NULL,(void*)produce,NULL);
pthread_join(idc1,NULL);
pthread_join(idc2,NULL);
pthread_join(idp1,NULL);
pthread_join(idp2,NULL);
pthread_join(idp3,NULL);
return 0;
}
3. 結果及說明
圖中的生產者是29108、29109,消費者是29110、29111、29112。沉睡時間為100ms到1s,填入數據隨機。
使用了兩個信號量is_empty/is_full,兩個互斥鎖mutexc和mutexp。
其中信號量用于監測緩沖區是否還能繼續填入或已滿,互斥鎖分別用于生產者、消費者的互斥使用臨界資源indexc和indexp操作。
任務8 在Windows或Linux下模擬哲學家就餐,提供死鎖和非死鎖解法。
1. 提示
提示1:同時提供提供可能會帶來死鎖的解法和不可能死鎖的解法。
提示2:可能會帶來死鎖的解法參見課件。Windows嘗試使用臨界區對象(EnterCriticalSection,LeaveCriticalSection);Linux嘗試使用互斥鎖(pthread_mutex_lock, pthread_mutex_unlock)
提示3:完全不可能產生死鎖的解法,例如:嘗試拿取兩只筷子,兩只都能拿則拿,否則都不拿。 Windows 嘗試使用
WaitForMultipleObjects, WaitForSingleObject和互斥量對象
ReleaseMutex等相關函數) Linux嘗試使用互斥鎖pthread_mutex_lock,pthread_mutex_trylock等函數。
提示4:[可選]圖形界面顯示哲學家取筷,吃飯,放筷,思考等狀態。
提示5:為增強隨機性,各狀態間維持100ms-500ms內的隨機時長。
2. 死鎖任務
死鎖代碼
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<sys/syscall.h>
#include<semaphore.h>
#define thinker_num 5
pthread_mutex_t s[thinker_num]; //筷子
float getRandTime(){ //100ms~500ms
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
return 0.001*(ts.tv_nsec%400)+0.1;
}
void* thinker(void* arg){
int index=(int)arg;
int index2=(index+1)%thinker_num;
float sleepTime;
while(1){
sleepTime=getRandTime();
printf("哲學家%ld正在思考,需時%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
sleepTime=getRandTime();
printf("哲學家%ld正在休息,需時%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
pthread_mutex_lock(&s[index]);
printf("哲學家%ld拿起筷子%d\n",syscall(SYS_gettid),index);
pthread_mutex_lock(&s[index2]);
sleepTime=getRandTime();
printf("哲學家%ld拿起筷子%d,%d,開始吃飯,需時%fs\n",syscall(SYS_gettid),index,index2,sleepTime);
sleep(sleepTime);
pthread_mutex_unlock(&s[index2]);
printf("哲學家%ld放下筷子%d\n",syscall(SYS_gettid),index2);
pthread_mutex_unlock(&s[index]);
printf("哲學家%ld放下筷子%d,%d\n",syscall(SYS_gettid),index,index2);
}
}
int main(){
pthread_t id[thinker_num];
for(int i=0;i<thinker_num;++i)
pthread_create(&id[i],NULL,(void*)thinker,(void*)i);
for(int i=0;i<thinker_num;++i)
pthread_join(id[i],NULL);
return 0;
}
運行結果
解釋如圖。
3. 不死鎖任務
不死鎖代碼
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<sys/syscall.h>
#include<semaphore.h>
#define thinker_num 5
pthread_mutex_t s[thinker_num]; //筷子
sem_t s_full; //最多(thinker-1)個人同時開始吃飯
float getRandTime(){ //100ms~500ms
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
return 0.001*(ts.tv_nsec%400)+0.1;
}
void* thinker(void* arg){
int index=(int)arg;
int index2=(index+1)%thinker_num;
float sleepTime;
while(1){
sleepTime=getRandTime();
printf("哲學家%ld正在思考,需時%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
sleepTime=getRandTime();
printf("哲學家%ld正在休息,需時%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
sem_wait(&s_full);
pthread_mutex_lock(&s[index]);
printf("哲學家%ld拿起筷子%d\n",syscall(SYS_gettid),index);
pthread_mutex_lock(&s[index2]);
sleepTime=getRandTime();
printf("哲學家%ld拿起筷子%d,%d,開始吃飯,需時%fs\n",syscall(SYS_gettid),index,index2,sleepTime);
sleep(sleepTime);
pthread_mutex_unlock(&s[index2]);
printf("哲學家%ld放下筷子%d\n",syscall(SYS_gettid),index2);
pthread_mutex_unlock(&s[index]);
printf("哲學家%ld放下筷子%d,%d\n",syscall(SYS_gettid),index,index2);
sem_post(&s_full);
}
}
int main(){
sem_init(&s_full,0,thinker_num-2); //初值為3,說明最多4個同時開始拿筷子
pthread_t id[thinker_num];
for(int i=0;i<thinker_num;++i)
pthread_create(&id[i],NULL,(void*)thinker,(void*)i);
for(int i=0;i<thinker_num;++i)
pthread_join(id[i],NULL);
return 0;
}
運行結果
增設初值為3的信號量s_full(信號量為0到3時該線程都可以繼續執行),保證同一時間開始拿筷子的思考家少于5個,破壞死鎖必要條件中的環路條件。
任務5 在Windows下利用線程實現并發畫圓/畫方。
1. 提示
提示1:圓心,半徑,顏色,正方形中心,邊長,顏色自己確定。
提示2:圓和正方形邊界建議都取720個點。為直觀展示繪制過程,每個
點繪制后睡眠0.2秒~0.5秒。
提示3:建議使用VS和MFC或QT對話框類型程序來繪制窗口和圖形。
2. 任務代碼
環境:Windows10 、VS2019。
進入下面這個網站安裝圖形庫(天地良心,這是我見過的最好安的東西)。
https://www.easyx.cn
其實用原生也能畫,但easyx真的酷炫炸了!效果爆炸!還有好多有趣的范例!!!愛了!這可是C++啊!
#include <graphics.h>
#include <conio.h>
// 使用 Bresenham 畫圓法
DWORD Circle_Bresenham(LPVOID lpParam)
{
int x = 320, y = 240, r = 90, color = LIGHTBLUE;
int tx = 0, ty = r, d = 3 - 2 * r;
while (tx <= ty)
{
// 利用圓的八分對稱性畫點
putpixel(x + tx, y + ty, color);
putpixel(x + tx, y - ty, color);
putpixel(x - tx, y + ty, color);
putpixel(x - tx, y - ty, color);
putpixel(x + ty, y + tx, color);
putpixel(x + ty, y - tx, color);
putpixel(x - ty, y + tx, color);
putpixel(x - ty, y - tx, color);
if (d < 0) // 取上面的點
d += 4 * tx + 6;
else // 取下面的點
d += 4 * (tx - ty) + 10, ty--;
Sleep(100);
tx++;
}
return 0;
}
DWORD Square(LPVOID lpParam)
{
int x = 320, y = 240, r = 90, color = RED;
int tx = 0, ty = r;
while (tx <= ty)
{
// 利用正方形的八分對稱性畫點
putpixel(x + tx, y + ty, color);
putpixel(x + tx, y - ty, color);
putpixel(x - tx, y + ty, color);
putpixel(x - tx, y - ty, color);
putpixel(x + ty, y + tx, color);
putpixel(x + ty, y - tx, color);
putpixel(x - ty, y + tx, color);
putpixel(x - ty, y - tx, color);
Sleep(70); //保證圓和方基本上同時結束
tx++;
}
return 0;
}
// 主函數
int main()
{
HANDLE hThread[2];
DWORD ThreadID;
initgraph(640, 480);
// 測試畫圓
hThread[0] = CreateThread(NULL, 0, Circle_Bresenham, NULL, 0, &ThreadID);
// 測試畫方
hThread[1] = CreateThread(NULL, 0, Square, NULL, 0, &ThreadID);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
// 按任意鍵退出
_getch();
closegraph();
return 0;
}
3. 結果及說明
沒有什么特殊的,就是創建兩個線程然后描點。我沒按提示來。
繪畫過程引起極度舒適!!!
為獲得更好的視覺效果,使用正方形和圓的八方對稱性,四個邊八個方向輪流畫。
其中正方形和圓使用了不同的線程。
夾帶一張別的。好看!!充分體現了并發的相互覆蓋。
總結
以上是生活随笔為你收集整理的【HUST】网安|操作系统实验|实验二 进程管理与死锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: codeup之矩阵转置
- 下一篇: DNSPY调试引用dll