📄 设计报告.txt
字号:
任务一 实现批处理
一、基本信息
实践题目:实现批处理
二、实践内容简要描述
实践目标:
(1)了解Windows2000操作系统的基本结构
(2)学会在Win32环境下,通过函数 BOOL CreateProcess(LPCTSTR lpApplicationName,LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)创建进程。
(3)学会用CreateFile ReadFile读文件,用FormatMessage处理出错信息。
实践内容:
从batch文件中正确读入应用程序命令行及其参数,并依次为其创建相应进程。
三、实践报告主要内容
设计思路:
(1)先打开文件 :使用CreateFile打开一个文件,获得HANDLE hFile
(2)然后读取文件 :用ReadFile通过已经获得HANDLE hFile的读取文件,并赋给inBuffer
(3)格式或处理读取信息并创建进程 :将inBuffer 中的信息格式化提取并用CreateProcess创建进程
主要代码结构:
/* 1.create the file and get the handle*/
HANDLE hFile=CreateFile("batch",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
/* 2.judge if the file is open and get the error imformation */
if(hFile==INVALID_HANDLE_VALUE)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
fprintf(stdout,(char*)(lpMsgBuf));
ExitProcess(1);
}
/* 3.read file and detect the error infomation */
BOOL bResult;
char inBuffer[1000]; // pointer to buffer that receives data
DWORD nBytesToRead=900; // number of bytes to read
DWORD nBytesRead;
LPDWORD lpnBytesRead=&nBytesRead; // pointer to number of bytes read
bResult =ReadFile(
hFile, // handle of file to read
inBuffer, // pointer to buffer that receives data
nBytesToRead, // number of bytes to read
lpnBytesRead, // pointer to number of bytes read
NULL // pointer to structure for data
);
/* 4.get the command from the buffer */
count=0;
pos=0;
for (i=0;i<(int)nBytesRead;i++)
{
if(inBuffer[i]!=10 && inBuffer[i]!=13)
{
cmdLine[count][pos]=inBuffer[i];
pos++;
continue;
}
if(inBuffer[i]==10)
{
cmdLine[count][pos]=0;
pos=0;
count++;
}
}
/* 5.copy the command from command line parameters */
for ( i = 0; i < count ; i++) {
if(!CreateProcess(
lpApplicationName, /* File name of executable */
cmdLine[i], /* Command line */
processSA, /* Process inherited security */
threadSA, /* Thread inherited security */
shareRights, /* Right propagation */
creationMask, /* Various creation flags */
environment, /* Environment variabkesr */
curDir, /* Child's current directory */
&startInfo,
&processInfo
)
) {
fprintf(stderr,"CreatProcess failed on error %d\n",GetLastError());
ExitProcess(1);
};
fprintf(stdout,"The Child Process's PID: %d.\n", processInfo.dwProcessId);
fprintf(stdout,"The Parent Process finish.\n");
Sleep(500);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
};
四、实践结果
基本数据:
源程序代码行数 完成实践投入的总时间 编程调试时间 源程序文件
116 2.5小时 0.5小时 ex1.cpp
测试数据设计:
“batch”文件内容如下:
c:\windows\notepad.exe
c:\windows\system32\calc.exe
c:\windows\system32\mspaint.exe
测试结果分析:
程序能够依次打开notepad,calc和mspaint,并显示
The Child Process's PID: 1712.
The Parent Process finish.
The Child Process's PID: 3276.
The Parent Process finish.
The Child Process's PID: 3876.
The Parent Process finish.
五、实践体会
第1个任务比较简单,我参考样例程序并查阅有关资料,在这个实验上没有遇到比较棘手的问题,通过任务1,我了解了创建进程的方法,对创建进程CreateProcess()函数中的各个参数有了一个初步的认识。
任务二 软件方法解决临界区问题
一、基本信息
实践题目:软件方法解决临界区问题-兄弟问题
二、实践内容简要描述
实践目标:
用软件方法(Peterson算法或Dekker算法等)解决临界区问题-兄弟问题。
实践内容:
设置竞争条件:
定义两个全局变量:accnt1和accnt2,初值都为零;
创建两个线程acc1和acc2;
(1)获得一个随机数
(2)从accnt1减去这个随机数;
(3)将这个随机数加到accnt2中;
(4)正确的话,accnt1+accnt2=0。
设置条件使其不正确。
用软件方法实现协作线程,以解决以上临界区问题,即兄弟问题。
三、实践报告主要内容
设计思路:
选择算法:
Dekker算法——该算法主要用于解决两个线程的临界区问题,是典型的软件方法解决临界区问题的算法。
设计思路:
线程Pi结构如下:
do{
flag[i]=true;
while(flag[j]){
if(turn==j) {
flag[i] = false;
while(turn==j);
flag[i]=true;
}
}
critical section
turn=j;
flag[i]=false;
remainder section
}while(1);
每个在进入临界区前用entry section实行判断等待,离开临界区后发出信号通知别的线程可以进入临界区,这样可以防止两个线程同时访问临界区。
在程序中i表示自身线程序号,再用两个线程序号之和减去i即可表示另外那个线程的序号。
主要数据结构:
struct ThreadInfo
{
int serial;
double delay;
};//存放线程的ID及其执行延时(模拟兄弟在外创业的时间)
/*volatile*/ int accnt1 = 0; //借款帐户中金额
/*volatile*/ int accnt2 = 0; //还款帐户中金额
/*volatile*/ int accnt; //借款帐户与还款帐户金额总和,应保持为0
/* variables for Dekker's arithmetic */
BOOL flag[2]={false,false};// flag[i] = true表示线程Pi 准备进入临界区
int turn; // turn = i表示线程Pi允许在临界区执行
int i = m_serial; // 自身线程序号
int j = 1 - m_serial; // 另外那个线程序号
主要代码结构:
do{
entry section
critical section
exit section
remainder section
}while(1);
主要代码分析:
1.entry section用于在进入临界区时的等待
/* entry section begin */
flag[m_serial]=true;
turn=1-m_serial;
while (flag[1-m_serial] && turn==1-m_serial);
/* entry section end */
2.exit section用于离开临界区后的发送信号的工作,通知另外的线程可以进入临界区
/* exit section begin */
flag[m_serial]=false;
/* exit section end */
四、实践结果
基本数据:
源程序代码行数 完成实践投入的总时间 资料查阅时间 编程调试时间 源程序文件
196 0.5小时 0.3小时 0.2小时 ex2.cpp
测试数据设计:
“ex2.dat”文件内容如下:
0 1
0 1
测试结果分析:
运行结果图如下所示:
Now, We begin to read thread Information to thread_info array
I am thread 0 , I am doing 00000th step
Now the random number is 20571 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00000th step
Now the random number is 17781 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00001th step
Now the random number is 22438 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00001th step
Now the random number is 17547 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00002th step
Now the random number is 27554 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00002th step
Now the random number is 25718 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00003th step
Now the random number is 18778 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00003th step
Now the random number is 8063 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00004th step
Now the random number is 30162 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00004th step
Now the random number is 29448 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00005th step
Now the random number is 7046 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00005th step
Now the random number is 8096 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00006th step
Now the random number is 16582 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00006th step
Now the random number is 8806 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00007th step
Now the random number is 1534 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00007th step
Now the random number is 31418 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00008th step
Now the random number is 21293 ; and accnt1+accnt2 = 00000
I am thread 1 , I am doing 00008th step
Now the random number is 9811 ; and accnt1+accnt2 = 00000
I am thread 0 , I am doing 00009th step
Now the random number is 22034 ; and accnt1+accnt2 = 00000
At last of thread 0 accnt1+accnt2 = 00000
I am thread 1 , I am doing 00009th step
Now the random number is 20276 ; and accnt1+accnt2 = 00000
At last of thread 1 accnt1+accnt2 = 00000
All threads have finished Operating.
Press any key to finish this Program.
五、实践体会
解决线程同步问题的关键是避免两个以上线程同时访问临界区数据,在实验中,由于entry section 和exit section所放位置不对导致了很多的错误,将entry section放在读取帐户数据后会出现读取数据出错导致帐户总额不对,因为可能读取到的是脏数据,将读取帐户数据和对其进行修改的代码放在entry section和exit section之间,即可确保同时只能有一个线程对其访问和操作,从而正确解决了问题。
六、参考文献
1、MSDN
2、《操作系统概念(第六版 影印版)》,Abraham Silberschatz、Peter Baer Galvin、Greg Gagne,
高等教育出版社
附:源程序代码
#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include <fstream.h>
#include <stdio.h>
#include <time.h>
#define INTE_PER_SEC 500
#define MAX_THREAD_NUM 64
#define RIGHT_VERSION TRUE
#define WRONG_VERSION FALSE
struct ThreadInfo
{
int serial;
double delay;
};
/*volatile*/ int accnt1 = 0;
/*volatile*/ int accnt2 = 0;
/*volatile*/ int accnt;
BOOL flag[2]={false,false};
int turn;
void account( char* file,BOOL version);
void acc_right(void* p);
void acc_wrong(void* p);
////////////////////////////////////////////////////////
// main function
////////////////////////////////////////////////////////
int main( int agrc, char* argv[] )
{
char ch;
while ( TRUE )
{
// Clear screen
system( "cls" );
// display prompt info
printf("*********************************************\n");
printf(" 1.Start the right version(with the PETERSON algorithm)\n");
printf(" 2.Start the wrong version(without any algorithm of synchronization)\n");
printf(" 3.Exit to Windows\n");
printf("*********************************************\n");
printf("Input your choice(1,2or3): ");
// if the number inputed is error, retry!
do
{
ch = (char)_getch();
}while ( ch != '1' && ch != '2'&& ch != '3');
system ( "cls" );
if ( ch == '1')
account("ex2.dat",RIGHT_VERSION);
else if (ch =='2')
account("ex2.dat",WRONG_VERSION);
else if ( ch == '3')
return 0;
printf("\nPress any key to finish this Program. \n");
_getch();
} //end while
} //end main
void account( char* file,BOOL version)
{
DWORD n_thread = 0;
DWORD thread_ID;
DWORD wait_for_all;
// Tread Object Array
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
ifstream inFile;
inFile.open(file); //open file
printf( "Now, We begin to read thread Information to thread_info array \n\n" );
while ( inFile )
{
// read every thread info
inFile>>thread_info[n_thread].serial;
inFile>>thread_info[n_thread++].delay;
inFile.get();
} //end while
// initialize the data
srand((unsigned)time(NULL));
accnt1=0;
accnt2=0;
// Create all thread
if (version==RIGHT_VERSION)
{
for ( int i = 0; i < (int)(n_thread); i++)
{
// Create a thread
h_Thread[i] = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)(acc_right), &thread_info[i], 0, &thread_ID);
} //end for
} else
{
for ( int i = 0; i < (int)(n_thread); i++)
{
// Create a thread
h_Thread[i] = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)(acc_wrong), &thread_info[i], 0, &thread_ID);
} //end for
}
// Create thread
// waiting all thread will been finished
wait_for_all = WaitForMultipleObjects(n_thread,h_Thread,TRUE, -1);
printf("All threads have finished Operating.\n");
}// end account
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -