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