计算机操作系统实验 ( Win平台实现 )

前言

  1. 最近学校安排计算机操作系统实验, 在学习过程中整理了一些代码和内容, 再次记录和分享一下, 希望能帮到各位. 若有问题欢迎交流
  2. 代码运行环境是 vscode 配置的 C/C++ 环境和 DEV C++

实验一: 创建进程

在这里插入图片描述

所涉及函数

PS: win平台相关的函数都可以在微软官网查到具体内容和案例 ( 虽然有的跑不起来 )
在这里插入图片描述

子进程程序

在这里要注意, 先使用 DEV C++ 把子进程编译成可执行文件 ( .exe ), 父进程才能基于该文件创建子进程, 结合CreateProcess()函数中参数的定义即可知道.

#include<stdio.h>
#include<windows.h> 
int main()
{
	printf("子进程开始运行...\n\n");
	const char *something = "这是一句话。";
	FILE *fp;
	if(fp = fopen("D:\\test.txt","w+"))              //以可读写方式打开文件
	{
		printf("文件打开成功!\n\n");
		fwrite(something,strlen(something),1,fp);    //将something写入文件中 
		fwrite("\n这是另一句话。",strlen("\n这是另一句话。"),1,fp);    //将另一句话写入文件中 
		printf("写入数据成功!\n\n");
		fclose(fp);//关闭文件 
 		fp = fopen("D:\\test.txt","r");               //以只读方式打开文件 
 		printf("当前文件中的内容如下:\n");
		char ch=fgetc(fp);                       //取第一个字符 
		while(ch!=EOF)                        //当该字符不为文件结束标志时 
		{
			putchar(ch);                       //输出该字符 
			ch=fgetc(fp);                      //继续从文件中获取字符 
		}
		fclose(fp);                            //关闭文件 
	}
	else
		printf("创建文件失败!\n");
	printf("\n\n");
	system("pause");
	return 0;
}

父进程程序

#include<stdio.h>
#include<windows.h>
int main()
{
	STARTUPINFO sui;               //启动信息结构体 
	PROCESS_INFORMATION pi;      //在创建进程时相关的数据结构之一,该结构返回有关新进程及其主线程的信息。
	ZeroMemory(&sui,sizeof(sui));
	sui.cb = sizeof(STARTUPINFO);  //将cb成员设置为信息结构体的大小
	int sum = 0;
	char content[100] = "";          //初始化content字符数组用来存放文件内容 
	if(CreateProcess("E:\\Coding\\CandC++\\child.exe",NULL,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&sui,&pi))//创建进程 
	{
		printf("已创建一个子进程\n");
		for(int i = 1; i <= 100; i++)
		{
			sum = sum + i;       //求1-100之和 
			Sleep(5);            //延迟时间5ms
			printf("Now, sum = %d\n",sum);
		}
		WaitForSingleObject(pi.hProcess,INFINITE);  //一直等下去直到进程结束 
		FILE *fp = fopen("D:\\test.txt","r");
		fread(content,sizeof(char),100,fp);           //设置读取文件内容的相关参数 
		printf("子进程创建的文件内容如下:\n\n%s\n\n",content);
		fclose(fp);
	}
	else
		printf("创建子进程失败\n");
	printf("实验结束!");
	return 0;
}

实验二: 线程共享进程数据

在这里插入图片描述

所涉及函数

在这里插入图片描述
在这里插入图片描述

来自微软的代码

通过这个代码也能理解如何使用CreateThread()函数, 也能展示线程中间数据共享, 但确实内容太多了

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>

#define MAX_THREADS 3
#define BUF_SIZE 255

DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(LPCTSTR lpszFunction);

// Sample custom data structure for threads to use.
// This is passed by void pointer so it can be any data type
// that can be passed using a single void pointer (LPVOID).
typedef struct MyData {
    int val1;
    int val2;
} MYDATA, *PMYDATA;

static int key = 1;

int _tmain()
{
    PMYDATA pDataArray[MAX_THREADS];
    DWORD   dwThreadIdArray[MAX_THREADS];
    HANDLE  hThreadArray[MAX_THREADS]; 

    // Create MAX_THREADS worker threads.
	printf("main0:%d\n",key);
    for( int i=0; i<MAX_THREADS; i++ )
    {
        // Allocate memory for thread data.

        pDataArray[i] = (PMYDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                sizeof(MYDATA));

        if( pDataArray[i] == NULL )
        {
           // If the array allocation fails, the system is out of memory
           // so there is no point in trying to print an error message.
           // Just terminate execution.
            ExitProcess(2);
        }

        // Generate unique data for each thread to work with.

        pDataArray[i]->val1 = i;
        pDataArray[i]->val2 = i+100;

        // Create the thread to begin execution on its own.

        hThreadArray[i] = CreateThread( 
            NULL,                   // default security attributes
            0,                      // use default stack size  
            MyThreadFunction,       // thread function name
            pDataArray[i],          // argument to thread function 
            0,                      // use default creation flags 
            &dwThreadIdArray[i]);   // returns the thread identifier 


        // Check the return value for success.
        // If CreateThread fails, terminate execution. 
        // This will automatically clean up threads and memory. 

        if (hThreadArray[i] == NULL) 
        {
           ErrorHandler(TEXT("CreateThread"));
           ExitProcess(3);
        }
    } // End of main thread creation loop.

    // Wait until all threads have terminated.

    WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);

    // Close all thread handles and free memory allocations.

    for(int i=0; i<MAX_THREADS; i++)
    {
        CloseHandle(hThreadArray[i]);
        if(pDataArray[i] != NULL)
        {
            HeapFree(GetProcessHeap(), 0, pDataArray[i]);
            pDataArray[i] = NULL;    // Ensure address is not reused.
        }
    }
	printf("main1:%d", key);
    return 0;
}


DWORD WINAPI MyThreadFunction( LPVOID lpParam ) 
{ 
    HANDLE hStdout;
    PMYDATA pDataArray;

    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;

    // Make sure there is a console to receive output results. 

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if( hStdout == INVALID_HANDLE_VALUE )
        return 1;

    // Cast the parameter to the correct data type.
    // The pointer is known to be valid because 
    // it was checked for NULL before the thread was created.
 
    pDataArray = (PMYDATA)lpParam;

	key += 1;
	printf("thread:%d\n",key);
    // Print the parameter values using thread-safe functions.
	
    StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"), 
        pDataArray->val1, pDataArray->val2); 
    StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
    WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);

    return 0; 
} 



void ErrorHandler(LPCTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code.

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message.

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK); 

    // Free error-handling buffer allocations.

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}

课上给出的代码

自己确实没写出来, 也懒得拆解微软代码了

#include <windows.h>
#include <cstdio>
static int count; // 定义全局共享数据
// 定义线程功能函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	printf("线程运行!\n");
	// 修改进程中的变量
	for (count = 1; count <= 10; count += 2)
	{
		printf("线程:count=%d\n", count);	
	}
	printf("等待线程3秒\n");
	Sleep(3000);
	return 0;
}
int main()
{
	count = 20;
	printf("进程:count=%d\n", count);
	HANDLE hEvent = CreateThread(NULL, 0, ThreadProc, NULL, 0,
	NULL); // 创建线程
	WaitForSingleObject(hEvent, INFINITE);
	// 等待线程运行结束
	CloseHandle(hEvent);
	// 关闭线程句柄
	printf("线程结束!\n");
	printf("进程结束!\n");
	printf("进程:count=%d\n", count);
	return 0;
}

实验三: 信号通信

在这里插入图片描述

所涉及函数

在这里插入图片描述

子进程代码

在这里要注意, 先使用 DEV C++ 把子进程编译成可执行文件 ( .exe ), 父进程才能基于该文件创建子进程, 结合CreateProcess()函数中参数的定义即可知道.

#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
	HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, (LPCTSTR)"myEvent"); // 打开命名事件
	// 休眠1s
	Sleep(1000);
	char ch;
	cout << "Signal the event to Parent[y/n]" << endl;
	cin >> ch;
	if (ch == 'y') // 发送信号
	SetEvent(hEvent);
	// 休眠1s
	Sleep(1000);
	system("pause");
	return 0;

	return 0;
} 

父进程代码

#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
	STARTUPINFO sui;
	PROCESS_INFORMATION pi;
	ZeroMemory(&sui, sizeof(sui));
	sui.cb = sizeof(STARTUPINFO);

	if (!CreateProcess((LPCTSTR)"E:\\Coding\\CandC++\\test3\\child.exe", NULL, NULL, NULL,
	FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &sui, &pi))
		cout << "进程创建失败" << endl;	

	cout << "这是一个子进程" << endl;
	// 创建一个有名事件
	HANDLE hEvent = CreateEvent(NULL, FALSE, TRUE, (LPCTSTR) "myEvent");
	ResetEvent(hEvent); // 清除事件信号
	// 等待子进程信号5s
	int time = 5000;
	DWORD flag = WaitForSingleObject(hEvent, time);
	if (WAIT_FAILED == flag) // 等待信号函数失败
		cout << "等待事件信号失败" << endl;
	else if (WAIT_OBJECT_0 == flag) // 等到事件信号
		cout << "获得事件信号" << endl;
	else if (WAIT_TIMEOUT == flag) // 子进程超时
		cout << "等待子进程信号超时" << endl;
	return 0;
}

实验四: 匿名管道通信

在这里插入图片描述

所涉及函数

微软官网对函数的分析匿名管道通信
在这里插入图片描述
在这里插入图片描述

子进程代码

  • 在这里要注意, 先使用 DEV C++ 把子进程编译成可执行文件 ( .exe ), 父进程才能基于该文件创建子进程, 结合CreateProcess()函数中参数的定义即可知道.
  • 代码的参考了一下这位老哥的博客内容Windows进程间的通信, 大家可以去看看. 老哥使用的是 VS 写的, 是改了一下, 能用 DEV C++ 和 VSCode 跑.
  • 代码注释的地方是我当初想着看能不能反向传输, 但确实不得, 因为这函数的关系是父子进程继承, 不能反过来
// 子进程.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include <Windows.h>
#include <iostream>
using namespace std;

int main()
{
    const int nBufferLen = 256; 
    HANDLE hRead  = NULL;
    HANDLE hWrite = NULL;

    BOOL bRet = FALSE;

    //1. 获得匿名管道输入输出句柄
    hRead  = GetStdHandle(STD_INPUT_HANDLE);
    hWrite = GetStdHandle(STD_OUTPUT_HANDLE);

    char  szSendBuffer[] = "子进程写入管道成功!";
    // char  szSendBuffer[] = "1111";
    DWORD dwWriteLen = 0;

    //2. 写入数据
    bRet = WriteFile(hWrite, szSendBuffer, (DWORD)strlen(szSendBuffer), &dwWriteLen, NULL);
    if (!bRet)
    {
        system("pause");
        return -1;
    }

    Sleep(500);

    // //3. 读取数据
    // Sleep(4000);
    // char  szBuffer[nBufferLen] = { 0 };
    // DWORD dwReadLen = 0;
    // bRet = ReadFile(hRead, szBuffer, nBufferLen, &dwReadLen, NULL);
    // if ( !bRet)
    // {
    //     cout << "读取数据失败!" << endl;
    //     system("pause");
    //     return -1;
    // }
    // cout << "从父进程接收到到数据: " << szBuffer << endl;
    
    system("pause");
    return 0;
}

父进程代码

// 父进程.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include <Windows.h>
#include <iostream>
using namespace std;

int main()
{
    const int nBufferLen = 256; 
    //管道缓冲区的大小(以字节为单位)

    SECURITY_ATTRIBUTES sa;
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    /**
     * @brief 结构包含对象的安全描述符,并指定通过指定此结构检索的句柄是否可继承
     * 指向控制对对象的访问 的SECURITY_DESCRIPTOR 结构的指针。
     * 指向控制对对象的访问 的SECURITY_DESCRIPTOR 结构的指针。
     * 此结构的大小(以字节为单位)。 将此值设置为 SECURITY_ATTRIBUTES 结构的大小。
     */
    HANDLE hRead = NULL;
    // 指向接收管道读取句柄的变量的指针。
    HANDLE hWrite = NULL;
    // 指向接收管道写入句柄的变量的指针。

    BOOL bRet = FALSE;

    //1. 创建匿名管道
    bRet = CreatePipe(&hRead, &hWrite, &sa, 0);
    if (!bRet)
    {
        cout << "创建匿名管道失败!" << endl;
        system("pause");
        return -1;
    }

    //2. 创建子进程,并对子进程相关数据进行初始化(用匿名管道的读取写入句柄赋予子进程的输入输出句柄)
    STARTUPINFO sui;
    PROCESS_INFORMATION pi;
    ZeroMemory(&sui, sizeof(STARTUPINFO));
    sui.cb = sizeof(STARTUPINFO);
    sui.dwFlags = STARTF_USESTDHANDLES;
    // 设置 STARTUPINFO 结构的成员。
    // 此结构指定用于重定向的 STDIN 和 STDOUT 句柄。
    sui.hStdInput  = hRead;
    sui.hStdOutput = hWrite;
    sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    bRet = CreateProcess("E:\\Coding\\CandC++\\test4\\child2.exe", NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &sui, &pi);
    if ( !bRet)
    {
        cout << "创建子进程失败!" << endl;
        system("pause");
        return -1;
    }

    // 3. 关闭子进程相关句柄(进行句柄,进程主线程句柄)
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    Sleep(2000);
    //4. 读取数据
    char  szBuffer[nBufferLen] = { 0 };
    DWORD dwReadLen = 0;

    bRet = ReadFile(hRead, szBuffer, nBufferLen, &dwReadLen, NULL);
    if ( !bRet)
    {
        cout << "读取数据失败!" << endl;
        system("pause");
        return -1;
    }

    cout << "从子进程接收到到数据: " << szBuffer << endl;
    Sleep(2000);
    
    // //5. 写入数据
    // char szSendBuffer[] = "父进程写入管道成功!";
    // DWORD dwWriteLen = 0;
    // bRet = WriteFile(hWrite, szSendBuffer, (DWORD)strlen(szSendBuffer), &dwWriteLen, NULL);
    // if (!bRet)
    // {
    //     system("pause");
    //     return -1;
    // }

    printf("???");
    system("pause");
    return 0;
}

实验五: 命名管道通信

在这里插入图片描述

涉及函数

子进程代码

在这里要注意, 先使用 DEV C++ 把子进程编译成可执行文件 ( .exe ), 父进程才能基于该文件创建子进程, 结合CreateProcess()函数中参数的定义即可知道.

#include <windows.h> 
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>

#define BUFSIZE 512
using namespace std;

int main(){
   //等待连接命名管道
   HANDLE hNamedPipe;
   const char * pStr = "data from server";
   const char * pPipeName;
   if( !WaitNamedPipe("\\\\.\\pipe\\testspipe", NMPWAIT_WAIT_FOREVER) )
   {
      cout<<"命名管道实例不存在 ..."<< endl<< endl;
      return 0;
   }
   cout << "成功连接到服务器" << endl;
   //打开命名管道
   hNamedPipe = CreateFile("\\\\.\\pipe\\testspipe", GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
   if( INVALID_HANDLE_VALUE == hNamedPipe )
   {
      cout << "打开命名管道失败!!!" << endl << endl;
      return 0;
   }
   printf("Open!\n");

   DWORD dwWrite;
   //向命名管道中写入数据
   if( !WriteFile(hNamedPipe, pStr, strlen(pStr), &dwWrite, NULL) )
   {
      cout<<"写入数据失败 ..." << endl << endl;
      return 0;
   }
   cout<< "写入数据成功:: "<< pStr << endl << endl;

   Sleep(5000);
   
   char * pReadBuf;
   DWORD  dwRead;
   pReadBuf = new char[strlen(pStr) + 1];
   memset(pReadBuf, 0, strlen(pStr) + 1);
   //从命名管道中读取数据
   if( !ReadFile(hNamedPipe, pReadBuf, strlen(pStr), &dwRead, NULL) )
   {
         delete []pReadBuf;
         cout << "读取数据失败 ..."<< endl << endl;
         return 0;
   }
   cout<<"读取数据成功:: "<< pReadBuf << endl << endl;

   system("pause");
   return 0;
}

父进程代码

#include <stdio.h>
#include <Windows.h>
#include <iostream>
using namespace std;

int main(){
      HANDLE  hNamedPipe;
      OVERLAPPED ovlpd;

      BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
      SECURITY_ATTRIBUTES sa;

      sa.nLength = sizeof(SECURITY_ATTRIBUTES);
      sa.bInheritHandle = TRUE;
      sa.lpSecurityDescriptor = &sd;

      InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
      SetSecurityDescriptorDacl(&sd, TRUE, (PACL) 0, FALSE);
      //创建命名管道
      //这里创建的是双向模式且使用重叠模式(异步操作)的命名管道
      hNamedPipe = CreateNamedPipe("\\\\.\\pipe\\testspipe",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,0, 1, 1024, 1024, 0, &sa);
      if( INVALID_HANDLE_VALUE == hNamedPipe )
      {
            cout << GetLastError() << endl;
            hNamedPipe = NULL;
            cout << "创建命名管道失败!!!" << endl << endl;
            return 0;
      }
      printf("1\n");

      STARTUPINFO sui;               //启动信息结构体 
      PROCESS_INFORMATION pi;      //在创建进程时相关的数据结构之一,该结构返回有关新进程及其主线程的信息。
      ZeroMemory(&sui,sizeof(sui));
      sui.cb = sizeof(STARTUPINFO);  //将cb成员设置为信息结构体的大小
      int sum = 0;
      char content[100] = "";          //初始化content字符数组用来存放文件内容 
      if(!CreateProcess("E:\\Coding\\CandC++\\test5\\child1.exe",NULL,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&sui,&pi))//创建进程 
      {
            cout << "进程创建失败" << endl;
      }
      printf("3\n");
      printf("子进程创建\n");

      Sleep(5000);


      // cout << "等待客户端的连接" << endl;
      BOOL   fConnected = FALSE; 
      ConnectNamedPipe(hNamedPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 
      // printf("???\n");

      const char *pStr;
      char *            pReadBuf;
      DWORD            dwRead;
      pReadBuf = new char[strlen(pStr) + 1];
      memset(pReadBuf, 0, strlen(pStr) + 1);
      //从命名管道中读取数据
      if( !ReadFile(hNamedPipe, pReadBuf, strlen(pStr), &dwRead, NULL) )
      {
            delete []pReadBuf;
            cout<<"读取数据失败 ..."<< endl<< endl;
            return 0;
      }
      cout << "读取数据成功::"<< pReadBuf << endl<< endl;

      pStr = "data from client";
      const char *pPipeName;
      pPipeName = "\\\\.\\pipe\\testPipe";

      DWORD dwWrite;
      //向命名管道中写入数据
      if( !WriteFile(hNamedPipe, pStr, strlen(pStr), &dwWrite, NULL) )
      {
            cout << "写入数据失败 ..." << endl<< endl;
            return 0;
      }
      cout << "写入数据成功:: "<< pStr<< endl<< endl;

      CloseHandle(hNamedPipe);

      system("pause");

      return 0;
}

未完待续

  • 还有几个实验, 但是没急着补上, 等之后学到对应内容再把实验做一下
  • 个人感觉还是在 Linux 系统上做这些实验比较好
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值