操系统原理课内实习二

文章详细介绍了如何在Windows环境下使用API遍历系统进程并以树形结构显示它们的父子关系,以及设计一个主进程创建子进程的程序,主进程使用共享内存初始化并等待子进程结束,子进程则读取共享内存进行计算。此外,还提出了一个基于线程的服务器和多进程客户端的通信模型,利用共享内存和信号量进行同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一题:遍历当前系统中的所有进程,并能用树状结构显示进程之间的父子关系

题目需求

在Windows操作系统中,每个运行的程序都是一个进程,每个进程都有一个唯一的标识符,叫做进程ID(Process ID)。进程之间也有一定的关系,比如一个进程可以创建另一个进程,这样就形成了父子关系。我们想要编写一个程序,能够遍历当前系统中的所有进程,并能用树状结构显示出它们之间的父子关系。

实现思路

为了实现这个功能,我们需要使用Windows API提供的一些函数和结构体。具体来说,我们需要以下几个步骤:

  1. 使用CreateToolhelp32Snapshot函数创建一个快照,包含当前系统中所有的进程信息。
  2. 使用Process32First和Process32Next函数遍历快照中的每个进程,并将它们的信息存储在自定义的ListNode结构体中。ListNode结构体包含以下几个字段:
    • th32ProcessID:存储进程ID。
    • th32ParentProcessID:存储父进程ID。
    • szExeFile:存储可执行文件路径。
    • nextList:存储子进程ID集合。
  3. 使用map容器将每个ListNode结构体按照th32ProcessID作为键值进行存储和查找。这样可以方便地根据任意一个进程ID找到对应的ListNode结构体。
  4. 使用递归函数showList来打印出树状结构。showList函数接受两个参数:当前要打印的节点ID和当前节点所在深度。showList函数首先检查当前节点是否已经被打印过(使用另一个map容器hashBool记录),如果没有,则按照深度打印出缩进符号和节点信息,然后遍历当前节点的nextList集合,对每个子节点递归调用showList函数。

细节设计

获得系统快照

// 获取进程快照
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return -1;
    }

    // 遍历快照中的进程信息
    PROCESSENTRY32 pi;
    pi.dwSize = sizeof(PROCESSENTRY32); //第一次使用必须初始化成员
    BOOL bRet = Process32First(hSnapshot, &pi);
    hashMap[-1].th32ParentProcessID = -1;
    while (bRet)
    {
    	//do something
    	bRet = Process32Next(hSnapshot, &pi);bRet = Process32Next(hSnapshot, &pi);
    }

数据结构

// 定义进程节点结构体
struct ListNode
{
    DWORD   th32ProcessID;         // 进程 ID
    DWORD   th32ParentProcessID;   // 父进程 ID
    CHAR    szExeFile[MAX_PATH];   // 进程路径
    set<DWORD> nextList;           // 子进程 ID 集合
};

// 定义进程节点哈希表和访问标记哈希表
map<DWORD, ListNode> hashMap;	//用于存储进程号对应的进程节点
map<DWORD, bool> hashBool;		//用于标记节点是否出现过

建立树形结构

遍历进程快照,对于每个进程建立进程节点,并将当前进程ID添加到其父进程节点的子进程ID集合中。当父进程不存在或尚未出现时,将-1作为父进程。

while (bRet)
    {
        if (hashMap.find(pi.th32ProcessID) == hashMap.end())
        {
            // 新建进程节点
            hashMap[pi.th32ProcessID].th32ProcessID = pi.th32ProcessID;
            hashMap[pi.th32ProcessID].th32ParentProcessID = pi.th32ParentProcessID;
            for (int i = 0; i < MAX_PATH; i++)
            {
                hashMap[pi.th32ProcessID].szExeFile[i] = pi.szExeFile[i];
            }

            // 新建父进程节点(如果不存在)
            if (hashMap.find(pi.th32ParentProcessID) == hashMap.end())
            {
                hashMap[pi.th32ParentProcessID].th32ParentProcessID = -1;
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
                hashMap[pi.th32ParentProcessID].th32ProcessID = pi.th32ParentProcessID;
                hashMap[-1].nextList.insert(pi.th32ParentProcessID);
            }
            // 否则将当前进程节点加入父进程节点的子进程集合
            else if(pi.th32ParentProcessID != pi.th32ProcessID)
            {
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
            }
        }
        else
        {
            for (int i = 0; i < MAX_PATH; i++)
            {
                hashMap[pi.th32ProcessID].szExeFile[i] = pi.szExeFile[i];
            }
            if (hashMap[pi.th32ProcessID].th32ParentProcessID == -1)
            {
                for (auto i = hashMap[-1].nextList.begin(); i != hashMap[-1].nextList.end(); i++)
                {
                    if (*i == pi.th32ProcessID)
                    {
                        hashMap[-1].nextList.erase(i);
                    }
                }
            }
            hashMap[pi.th32ProcessID].th32ParentProcessID = pi.th32ParentProcessID;
            if (hashMap.find(pi.th32ParentProcessID) == hashMap.end())
            {
                hashMap[pi.th32ParentProcessID].th32ParentProcessID = -1;
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
                hashMap[pi.th32ParentProcessID].th32ProcessID = pi.th32ParentProcessID;
                hashMap[-1].nextList.insert(pi.th32ParentProcessID);
            }
            else if (pi.th32ParentProcessID != pi.th32ProcessID)
            {
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
            }
        }
        if (pi.th32ParentProcessID == pi.th32ProcessID)
        {
            hashMap[pi.th32ProcessID].th32ParentProcessID = -1;
            hashMap[-1].nextList.insert(pi.th32ParentProcessID);
        }
        bRet = Process32Next(hSnapshot, &pi);
    }

递归以树形输出进程

main中遍历调用部分
// 遍历哈希表展示进程信息
    hashBool[-1] = true;
    for (auto i = hashMap[-1].nextList.begin(); i != hashMap[-1].nextList.end(); i++)
    {
        DWORD ID = *i;
        printf("进程ID = %d ,进程路径 = %s\r\n", hashMap[ID].th32ProcessID, hashMap[ID].szExeFile);
        
        for (auto j = hashMap[ID].nextList.begin(); j != hashMap[ID].nextList.end(); j++)
        {
            showList(*j, 1);
            printf("\n");
        }
        hashBool[ID] = true;
    }
递归函数
// 递归展示子进程信息
void showList(DWORD ID, int dep)
{
    if (hashBool.find(ID) != hashBool.end()) return;
    hashBool[ID] = true;
    string s = "";
    for (int i = 0; i < dep; i++)
    {
        s += "\t\t|";	//根据递归深度调整缩进
    }
    printf("%s\r\n", s.c_str());
    printf("%s--", s.c_str());
    printf("进程ID = %d ,进程路径 = %s\r\n", hashMap[ID].th32ProcessID, hashMap[ID].szExeFile);
    for (auto i = hashMap[ID].nextList.begin(); i != hashMap[ID].nextList.end(); i++)
    {
        showList(*i, dep + 1);
    }
}

实现代码

#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <Tlhelp32.h>
#include <set>
#include <map>
#include <string>
#include <sstream>
using namespace std;

// 定义进程节点结构体
struct ListNode
{
    DWORD   th32ProcessID;         // 进程 ID
    DWORD   th32ParentProcessID;   // 父进程 ID
    CHAR    szExeFile[MAX_PATH];   // 进程路径
    set<DWORD> nextList;           // 子进程 ID 集合
};

// 定义进程节点哈希表和访问标记哈希表
map<DWORD, ListNode> hashMap;
map<DWORD, bool> hashBool;

// 递归展示子进程信息
void showList(DWORD ID, int dep)
{
    if (hashBool.find(ID) != hashBool.end()) return;
    hashBool[ID] = true;
    string s = "";
    for (int i = 0; i < dep; i++)
    {
        s += "\t\t|";
    }
    printf("%s\r\n", s.c_str());
    printf("%s--", s.c_str());
    printf("进程ID = %d ,进程路径 = %s\r\n", hashMap[ID].th32ProcessID, hashMap[ID].szExeFile);
    for (auto i = hashMap[ID].nextList.begin(); i != hashMap[ID].nextList.end(); i++)
    {
        showList(*i, dep + 1);
    }
}

// 主函数
int main(int argc, char* argv[])
{
    // 获取进程快照
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return -1;
    }

    // 遍历快照中的进程信息
    PROCESSENTRY32 pi;
    pi.dwSize = sizeof(PROCESSENTRY32); //第一次使用必须初始化成员
    BOOL bRet = Process32First(hSnapshot, &pi);
    hashMap[-1].th32ParentProcessID = -1;
    while (bRet)
    {
        if (hashMap.find(pi.th32ProcessID) == hashMap.end())
        {
            // 新建进程节点
            hashMap[pi.th32ProcessID].th32ProcessID = pi.th32ProcessID;
            hashMap[pi.th32ProcessID].th32ParentProcessID = pi.th32ParentProcessID;
            for (int i = 0; i < MAX_PATH; i++)
            {
                hashMap[pi.th32ProcessID].szExeFile[i] = pi.szExeFile[i];
            }

            // 新建父进程节点(如果不存在)
            if (hashMap.find(pi.th32ParentProcessID) == hashMap.end())
            {
                hashMap[pi.th32ParentProcessID].th32ParentProcessID = -1;
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
                hashMap[pi.th32ParentProcessID].th32ProcessID = pi.th32ParentProcessID;
                hashMap[-1].nextList.insert(pi.th32ParentProcessID);
            }
            // 否则将当前进程节点加入父进程节点的子进程集合
            else if(pi.th32ParentProcessID != pi.th32ProcessID)
            {
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
            }
        }
        else
        {
            for (int i = 0; i < MAX_PATH; i++)
            {
                hashMap[pi.th32ProcessID].szExeFile[i] = pi.szExeFile[i];
            }
            if (hashMap[pi.th32ProcessID].th32ParentProcessID == -1)
            {
                for (auto i = hashMap[-1].nextList.begin(); i != hashMap[-1].nextList.end(); i++)
                {
                    if (*i == pi.th32ProcessID)
                    {
                        hashMap[-1].nextList.erase(i);
                    }
                }
            }
            hashMap[pi.th32ProcessID].th32ParentProcessID = pi.th32ParentProcessID;
            if (hashMap.find(pi.th32ParentProcessID) == hashMap.end())
            {
                hashMap[pi.th32ParentProcessID].th32ParentProcessID = -1;
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
                hashMap[pi.th32ParentProcessID].th32ProcessID = pi.th32ParentProcessID;
                hashMap[-1].nextList.insert(pi.th32ParentProcessID);
            }
            else if (pi.th32ParentProcessID != pi.th32ProcessID)
            {
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
            }
        }
        if (pi.th32ParentProcessID == pi.th32ProcessID)
        {
            hashMap[pi.th32ProcessID].th32ParentProcessID = -1;
            hashMap[-1].nextList.insert(pi.th32ParentProcessID);
        }
        bRet = Process32Next(hSnapshot, &pi);
    }

    // 遍历哈希表展示进程信息
    hashBool[-1] = true;
    for (auto i = hashMap[-1].nextList.begin(); i != hashMap[-1].nextList.end(); i++)
    {
        DWORD ID = *i;
        printf("进程ID = %d ,进程路径 = %s\r\n", hashMap[ID].th32ProcessID, hashMap[ID].szExeFile);
        
        for (auto j = hashMap[ID].nextList.begin(); j != hashMap[ID].nextList.end(); j++)
        {
            showList(*j, 1);
            printf("\n");
        }
        hashBool[ID] = true;
    }
    // 关闭进程快照句柄
    CloseHandle(hSnapshot);
    return 0;
}

运行效果

图1-1 控制台输出效果

easyX可视化版(选看)

除了控制台做树形输出,我还尝试做了一个用easyX包实现的可视化界面,原理一致,只不过输出做了可视化

#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <Tlhelp32.h>
#include <set>
#include <map>
#include <string>
#include <sstream>
#include <graphics.h>
#include <conio.h>

using namespace std;
struct ListNode
{
    DWORD   th32ProcessID;
    DWORD   th32ParentProcessID;
    CHAR    szExeFile[MAX_PATH];
    set<DWORD> nextList;
};
map<DWORD, ListNode> hashMap;
map<DWORD, bool> hashBool;
#define WHITE_SMOKE     0xF5F5F5
#define GAINSBORO       0xDCDCDC
#define DARKSLATEGREY   0xFFF598
#define PaleTurquoise 0x8B8B66
//将要输出到窗口上的进程节点链表
struct ShowList
{
    string text;	//进程路径
    int status;		//button状态
    IMAGE img;		//button图案
    ShowList* next;	//下一节点指针
    DWORD   th32ProcessID;
};

ShowList* headL = NULL;		//可见进程节点链表头指针
ShowList* tailL = NULL;		//可见进程节点链表尾指针
int My_SIZE;				//可见进程链表实际大小
const int MAX_SIZE = 20;;	//窗口可显示最大进程数
int viewHeight = 0;			//滚动条高度
int viewTop = 0;			//滚动条顶部位置
bool vviewTop = false;		//是否生成滚动条

//对于button:	X是未展开的父进程;对应状态0
//				Y是鼠标选中的未展开父进程;对应状态2
//				Z是子进程(没有子进程的进程);对应状态1
//				T是已展开的父进程;对应状态3
//				TZ是鼠标选中的已展开父进程;对应状态4
void setButtonX(IMAGE &img, string text)
{
    SetWorkingImage(&img);
    cleardevice();
    Resize(&img, 280, 40);
    setbkmode(TRANSPARENT);
    setlinecolor(WHITE);
    setfillcolor(GAINSBORO);
    solidrectangle(0, 0, 279, 39);
    setfillcolor(WHITE);
    fillrectangle(1, 1, 275, 35);
    rectangle(0, 0, 279, 39);
    RECT rec{ 0,0,279,39 };
    text = " > " + text;
    settextcolor(BLACK);
    drawtext(text.c_str(), &rec, DT_LEFT | DT_WORD_ELLIPSIS | DT_VCENTER | DT_SINGLELINE);
    SetWorkingImage();
}

void setButtonY(IMAGE& img, string text)
{
    SetWorkingImage(&img);
    cleardevice();
    Resize(&img, 280, 40);
    setbkmode(TRANSPARENT);
    setlinecolor(WHITE);
    setfillcolor(GAINSBORO);
    solidrectangle(0, 0, 279, 39);
    setlinecolor(BLACK);
    rectangle(0, 0, 279, 39);
    RECT rec{ 0,0,279,39 };
    text = " > " + text;
    settextcolor(BLACK);
    drawtext(text.c_str(), &rec, DT_LEFT | DT_WORD_ELLIPSIS | DT_VCENTER | DT_SINGLELINE);
    SetWorkingImage();
}

void setButtonZ(IMAGE& img, string text)
{
    SetWorkingImage(&img);
    cleardevice();
    Resize(&img, 280, 40);
    setbkmode(TRANSPARENT);
    setlinecolor(GAINSBORO);
    setfillcolor(DARKSLATEGREY);
    solidrectangle(0, 0, 279, 39);
    rectangle(0, 0, 279, 39);
    RECT rec{ 0,0,279,39 };
    text = " > " + text;
    settextcolor(BLACK);
    drawtext(text.c_str(), &rec, DT_LEFT | DT_WORD_ELLIPSIS | DT_VCENTER | DT_SINGLELINE);
    SetWorkingImage();
}

void setButtonT(IMAGE& img, string text)
{
    SetWorkingImage(&img);
    cleardevice();
    Resize(&img, 280, 40);
    setbkmode(TRANSPARENT);
    setlinecolor(WHITE);
    setfillcolor(PaleTurquoise);
    solidrectangle(0, 0, 279, 39);
    RECT rec{ 0,0,279,39 };
    text = " - " + text;
    settextcolor(BLACK);
    drawtext(text.c_str(), &rec, DT_LEFT | DT_WORD_ELLIPSIS | DT_VCENTER | DT_SINGLELINE);
    SetWorkingImage();
}

void setButtonTZ(IMAGE& img, string text)
{
    SetWorkingImage(&img);
    cleardevice();
    Resize(&img, 280, 40);
    setbkmode(TRANSPARENT);
    setlinecolor(WHITE);
    setfillcolor(CYAN);
    solidrectangle(0, 0, 279, 39);
    RECT rec{ 0,0,279,39 };
    text = " - " + text;
    settextcolor(BLACK);
    drawtext(text.c_str(), &rec, DT_LEFT | DT_WORD_ELLIPSIS | DT_VCENTER | DT_SINGLELINE);
    SetWorkingImage();
}

void dele(ShowList* p, int n)
{
    if (n <= 0) return;
    if (p == NULL) return;
    if (p->next == NULL) return;
    int cnt = 0;
    p->status = 0;
    setButtonX(p->img, p->text);
    while (p != NULL && p->next != NULL && cnt != n)
    {
        ShowList* temp = p->next;
        if (temp->status == 4 || temp->status == 3)
        {
            dele(temp, hashMap[temp->th32ProcessID].nextList.size());
        }
        p->next = temp->next;
        delete temp;
        My_SIZE--;
        cnt++;
    }
}

//更新窗口函数
void reDraw()
{
    BeginBatchDraw();
    SetWorkingImage();
    setbkcolor(WHITE);
    cleardevice();
    if (!vviewTop && My_SIZE > MAX_SIZE)
    {
        vviewTop = true;
        viewTop = 0;
        viewHeight = 40 * MAX_SIZE * MAX_SIZE / My_SIZE;
        initgraph(300, 40 * MAX_SIZE);
    }
    else if (vviewTop && My_SIZE <= MAX_SIZE)
    {
        vviewTop = false;
        viewTop = 0;
        viewHeight = 0;
        initgraph(280, 40 * MAX_SIZE);
    }
    if (vviewTop)
    {
        setfillcolor(0xD3D3D3);
        solidrectangle(280, 0, 299, 40 * MAX_SIZE - 1);
        setfillcolor(0x696969);
        solidrectangle(280, viewTop, 299, viewTop +  viewHeight);
    }
    int cnt(0);
    ShowList* p = headL;
    while(p!=NULL)
    {
        int y = cnt * 40 - viewTop * My_SIZE / MAX_SIZE;
        putimage(0, y, &p->img);
        p = p->next;
        cnt++;
    }
    EndBatchDraw();
}

//主函数
int main(int argc, char* argv[])
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return -1;
    }
    PROCESSENTRY32 pi;
    pi.dwSize = sizeof(PROCESSENTRY32); //第一次使用必须初始化成员
    BOOL bRet = Process32First(hSnapshot, &pi);
    hashMap[-1].th32ParentProcessID = -1;
    while (bRet)
    {
        if (hashMap.find(pi.th32ProcessID) == hashMap.end())
        {
            hashMap[pi.th32ProcessID].th32ProcessID = pi.th32ProcessID;
            hashMap[pi.th32ProcessID].th32ParentProcessID = pi.th32ParentProcessID;
            for (int i = 0; i < MAX_PATH; i++)
            {
                hashMap[pi.th32ProcessID].szExeFile[i] = pi.szExeFile[i];
            }
            if (hashMap.find(pi.th32ParentProcessID) == hashMap.end())
            {
                hashMap[pi.th32ParentProcessID].th32ParentProcessID = -1;
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
                hashMap[pi.th32ParentProcessID].th32ProcessID = pi.th32ParentProcessID;
                hashMap[-1].nextList.insert(pi.th32ParentProcessID);
            }
            else if (pi.th32ParentProcessID != pi.th32ProcessID)
            {
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
            }
        }
        else
        {
            for (int i = 0; i < MAX_PATH; i++)
            {
                hashMap[pi.th32ProcessID].szExeFile[i] = pi.szExeFile[i];
            }
            if (hashMap[pi.th32ProcessID].th32ParentProcessID == -1)
            {
                for (auto i = hashMap[-1].nextList.begin(); i != hashMap[-1].nextList.end(); i++)
                {
                    if (*i == pi.th32ProcessID)
                    {
                        hashMap[-1].nextList.erase(i);
                    }
                }
            }
            hashMap[pi.th32ProcessID].th32ParentProcessID = pi.th32ParentProcessID;
            if (hashMap.find(pi.th32ParentProcessID) == hashMap.end())
            {
                hashMap[pi.th32ParentProcessID].th32ParentProcessID = -1;
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
                hashMap[pi.th32ParentProcessID].th32ProcessID = pi.th32ParentProcessID;
                hashMap[-1].nextList.insert(pi.th32ParentProcessID);
            }
            else if (pi.th32ParentProcessID != pi.th32ProcessID)
            {
                hashMap[pi.th32ParentProcessID].nextList.insert(pi.th32ProcessID);
            }
        }
        if (pi.th32ParentProcessID == pi.th32ProcessID)
        {
            hashMap[pi.th32ProcessID].th32ParentProcessID = -1;
            hashMap[-1].nextList.insert(pi.th32ParentProcessID);
        }
        bRet = Process32Next(hSnapshot, &pi);
    }
    CloseHandle(hSnapshot);

    hashBool[-1] = true;
    My_SIZE = 0;
    for (auto i = hashMap[-1].nextList.begin(); i != hashMap[-1].nextList.end(); i++)
    {
        ShowList* newL = new ShowList();
        newL->next = NULL;
        newL->text = hashMap[*i].szExeFile;
        if (newL->text.empty())
        {
            stringstream ss;
            ss << "进程号:" << hashMap[*i].th32ProcessID;
            newL->text = ss.str();
        }
        newL->th32ProcessID = *i;
        if (hashMap[*i].nextList.size() > 0)
        {
            newL->status = 0;
            setButtonX(newL->img, newL->text);
        }
        else
        {
            newL->status = 1;
            setButtonY(newL->img, newL->text);
        }
        if (headL == NULL)
        {
            headL = newL;
            tailL = headL;
        }
        else
        {
            tailL->next = newL;
            tailL = newL;
        }
        My_SIZE++;
    }
    initgraph(280, 40 * MAX_SIZE);
    setbkcolor(WHITE);
    while (true)
    {
        reDraw();
        ExMessage m;
        SetWorkingImage();
        getmessage(&m, EM_MOUSE);
        {
            short x = m.x;
            short y = m.y;
            if (m.x < 0 || m.x >= 300 || m.y < 0)
            {
                continue;
            }
            else if (m.x >= 280 && m.x < 300 && vviewTop)
            {
                if (m.message == WM_LBUTTONDOWN)
                {
                    viewTop = y;
                }
                continue;
            }
            y += viewTop * My_SIZE / MAX_SIZE;
            int id = y / 40;
            switch (m.message)
            {
            case WM_MOUSEMOVE:
            case WM_LBUTTONDOWN:
            {
                int cnt(0);
                ShowList* p = headL;
                while (p != NULL && cnt != id)
                {
                    if (p->status == 2)
                    {
                        p->status = 0;
                        setButtonX(p->img, p->text);
                    }
                    else if (p->status == 4)
                    {
                        p->status = 3;
                        setButtonT(p->img, p->text);
                    }
                    cnt++;
                    p = p->next;
                }
                if (p != NULL)
                {
                    if (p->status == 0)
                    {
                        p->status = 2;
                        setButtonZ(p->img, p->text);
                    }
                    else if (p->status == 3)
                    {
                        p->status = 4;
                        setButtonTZ(p->img, p->text);
                    }
                }
                if(p!=NULL)
                    p = p->next;
                while (p != NULL)
                {
                    if (p->status == 2)
                    {
                        p->status = 0;
                        setButtonX(p->img, p->text);
                    }
                    else if (p->status == 4)
                    {
                        p->status = 3;
                        setButtonT(p->img, p->text);
                    }
                    p = p->next;
                }
            }break;
            case WM_LBUTTONUP:
            {
                int cnt(0);
                ShowList* p = headL;
                while (p != NULL && cnt != id)
                {
                    cnt++;
                    p = p->next;
                }
                if (p != NULL && (p->status == 2 || p->status == 0))
                {
                    int n = p->th32ProcessID;
                    p->status = 3;
                    setButtonT(p->img, p->text);
                    for (auto i = hashMap[n].nextList.begin(); i != hashMap[n].nextList.end(); i++)
                    {
                        ShowList* newL = new ShowList();
                        newL->next = p->next;
                        newL->text = hashMap[*i].szExeFile;
                        newL->th32ProcessID = *i;
                        if (hashMap[*i].nextList.size() > 0)
                        {
                            newL->status = 0;
                            setButtonX(newL->img, newL->text);
                        }
                        else
                        {
                            newL->status = 1;
                            setButtonY(newL->img, newL->text);
                        }
                        p->next = newL;
                        p = newL;
                        My_SIZE++;
                    }
                }
                else if (p != NULL && (p->status == 4 || p->status == 3))
                {
                    dele(p, hashMap[p->th32ProcessID].nextList.size());
                }
                if (p != NULL)
                    p = p->next;
                while (p != NULL)
                {
                    if (p->status == 2)
                    {
                        p->status = 0;
                        setButtonX(p->img, p->text);
                    }
                    else if (p->status == 4)
                    {
                        p->status = 3;
                        setButtonT(p->img, p->text);
                    }
                    p = p->next;
                }
            }break;
            }
        }
    }
    
    _getch();
    closegraph();
    return 0;
}

可视化效果

在这里插入图片描述

第二题:

设计一个程序,可以做为主进程和子进程两种模式运行,做为主进程创建共享内存并初始化,然后启动 3 个子进程,最后等待子进程结束,并输出共享内存中的内容;做为子进程,首先打开共享内存,然后以临界区的形式对共享内存中的值加 1,重复 10000 次。

程序设计思路

首先,我们需要了解一些基本概念:

  • 共享内存:共享内存是一种最快的进程间通信方式,它允许多个进程访问同一块物理内存空间。在Windows下,我们可以使用CreateFileMapping和OpenFileMapping函数来创建或打开一个命名的文件映射对象(也就是共享内存),然后使用MapViewOfFile函数来将文件映射对象映射到当前进程的地址空间。
  • 信号量:信号量是一种用于控制多个进程对共享资源的访问的同步机制。它维护了一个计数器,表示可用资源的数量。当一个进程想要使用资源时,它必须先获取信号量(即减少计数器),如果计数器为0,则表示没有可用资源,该进程必须等待;当一个进程使用完资源后,它必须释放信号量(即增加计数器),以便其他等待的进程可以继续使用资源。在Windows下,我们可以使用CreateSemaphore和OpenSemaphore函数来创建或打开一个命名的信号量对象,并使用WaitForSingleObject和ReleaseSemaphore函数来获取或释放信号量。
  • 临界区:临界区是指对共享资源进行访问或修改的代码段,在任意时刻只能有一个线程执行该代码段。如果有多个线程同时想要执行临界区代码,则必须按照某种规则排队等待。在本例中,我们使用信号量来实现临界区机制。

基于以上概念,我们可以设计如下算法:

  • 作为主进程时:
    • 创建一个命名的文件映射对象,并将其映射到当前地址空间。
    • 在映射地址处初始化一个整型变量为0。
    • 创建一个命名的信号量对象,并将其初始值设为1。
    • 循环3次:
      • 创建一个新的子进程,并传递参数“sub”。
      • 将新创建的子进程句柄保存到数组中。
    • 等待所有子进程结束。
    • 输出映射地址处的整型变量值。
    • 关闭文件映射对象、信号量对象、子进程句柄等资源。
  • 作为子进程时:
    • 打开已存在的文件映射对象,并将其映射到当前地址空间。
    • 打开已存在的信号量对象。
    • 循环10000次:
    • 获取信号量(即开始临界区)。
    • 将映射地址处将映射地址处的整型变量值加1。
    • 释放信号量(即结束临界区)。
    • 关闭文件映射对象、信号量对象等资源。

细节设计

同一程序主子进程结构

//主进程
if (argc == 1)
{
	...
}
//子进程
else
{
	...
}

主进程实现

HANDLE h = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, SHARED_MEM_SIZE, TEXT("myShareMemery"));
void* pt = MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0);	//尾填0扩展到末尾
*(int*)pt = 0;

HANDLE ph[3];
TCHAR szCommandLine[] = TEXT("Project2.exe sub");
for (int i = 0; i < 3; i++)
{
	STARTUPINFO si{ sizeof(si) };
	PROCESS_INFORMATION pi;
	CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
	ph[i] = pi.hProcess;
}
WaitForMultipleObjects(3, ph, TRUE, -1);
std::cout << "Content of ShareMemery:" << *(int*)pt;
UnmapViewOfFile(pt);
CloseHandle(h);

子进程实现

HANDLE hShare = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("myShareMemery"));
void* pt = MapViewOfFile(hShare, FILE_MAP_WRITE, 0, 0, 0);
HANDLE hCriticalSection = CreateSemaphore(NULL, 1, 1, TEXT("myCriticalSection"));
for (int i = 0; i < 10000; i++)
{
	WaitForSingleObject(hCriticalSection, -1);
	*(int*)pt += 1;
	ReleaseSemaphore(hCriticalSection, 1, NULL);
}
CloseHandle(hCriticalSection);
UnmapViewOfFile(hShare);
CloseHandle(hShare);

实现代码


#include <iostream>
#include <Windows.h>

const int SHARED_MEM_SIZE = 1024;
int main(int argc,char* argv[])
{
	if (argc == 1)
	{
		HANDLE h = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, SHARED_MEM_SIZE, TEXT("myShareMemery"));
		void* pt = MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0);	//尾填0扩展到末尾
		*(int*)pt = 0;

		HANDLE ph[3];
		TCHAR szCommandLine[] = TEXT("Project2.exe sub");
		for (int i = 0; i < 3; i++)
		{
			STARTUPINFO si{ sizeof(si) };
			PROCESS_INFORMATION pi;
			CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
			ph[i] = pi.hProcess;
		}
		WaitForMultipleObjects(3, ph, TRUE, -1);
		std::cout << "Content of ShareMemery:" << *(int*)pt;
		UnmapViewOfFile(pt);
		CloseHandle(h);
	}
	else
	{
		HANDLE hShare = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("myShareMemery"));
		void* pt = MapViewOfFile(hShare, FILE_MAP_WRITE, 0, 0, 0);
		HANDLE hCriticalSection = CreateSemaphore(NULL, 1, 1, TEXT("myCriticalSection"));
		for (int i = 0; i < 10000; i++)
		{
			WaitForSingleObject(hCriticalSection, -1);
			*(int*)pt += 1;
			ReleaseSemaphore(hCriticalSection, 1, NULL);
		}
		CloseHandle(hCriticalSection);
		UnmapViewOfFile(hShare);
		CloseHandle(hShare);
	}
	return 0;
}

运行结果

在这里插入图片描述

第三题

基于线程的服务器和多进程客户端,实现简单四则运算。可以通过网络通信也可以通过
共享内存结合信号量进行通迅,同时请求的客户端进程数可以超过最大 worker 数,当没
有请求时 worker 需要等待。

设计思路

我们首先定义了两个结构体,分别用于存储请求数据和结果数据:

struct RequestData{
int v; 		// 标志位,0表示空闲,1表示有请求
double a; 	// 操作数1
double b; 	// 操作数2
char op; 	// 运算符
};

struct ResultData{
int v; 			// 标志位,0表示空闲,1表示待计算,2表示有结果
double result; 	// 计算结果
};

然后我们定义了一个共享内存区域的头部结构体,用于记录当前的头尾索引:

struct IndexHead{
int headIndex; // 头索引,指向第一个有请求的位置
int tailIndex; // 尾索引,指向最后一个有请求的位置的下一个位置
};

我们假设共享内存区域可以容纳最多MAX_REQUESTS个请求数据和结果数据。因此共享内存区域的布局如下:

|| IndexHead || RequestData[0] | ResultData[0] || RequestData[1] | ResultData[1] || … || RequestData[MAX_REQUESTS-1] | ResultData[MAX_REQUESTS-1] ||

请求数据和结果数据存在同一块内存区域

我们还需要定义一个信号量workSem来控制对共享内存区域的访问。每次访问前需要等待信号量可用,并在访问后释放信号量。

服务器端

服务器端采用线程池的方式创建固定数量MAX_WORKERS的 worker 线程,每个 worker 线程负责处理一个请求。服务器端使用共享内存SHARED_MEM_NAME作为通讯缓冲区,其中包含以下三部分:

  • IndexHead:一个结构体,用于记录当前缓冲区中请求数据和结果数据的头尾索引。
  • RequestData:一个结构体数组,用于存储客户端发送的请求数据,每个元素包含一个标志位 v(0 表示空闲,1 表示已写入),两个操作数 a 和 b,以及一个操作符 op。
  • ResultData:一个结构体数组,用于存储服务器返回的结果数据,每个元素包含一个标志位 v(0 表示空闲,1 表示已写入,2 表示已读取),以及一个结果值 result。

服务器端还使用一个信号量SEM_NAME作为同步机制,用于控制对共享内存的访问。信号量的初始值为 1(表示有空闲资源),最大值为 MAX_REQUESTS(表示缓冲区大小)。

worker 线程的工作流程如下:

  • 等待信号量(WaitForSingleObject)
  • 打开共享内存并映射到本地地址空间(OpenFileMapping 和 MapViewOfFile)
  • 检查 IndexHead 中的头尾索引是否相等,如果相等,则表示缓冲区为空,没有请求需要处理,则释放共享内存并返回信号量,并重新等待信号量
  • 如果不相等,则表示缓冲区中有请求需要处理,则根据头索引从 RequestData 数组中读取一个请求数据,并将头索引加一取模 MAX_REQUESTS
  • 释放共享内存并返回信号量
  • 根据请求数据中的操作符 op 对操作数 a 和 b 进行四则运算,并将结果封装到 ResultData 结构体中
  • 等待信号量
  • 打开共享内存并映射到本地地址空间
  • 根据头索引从 ResultData 数组中写入一个结果数据,并将其标志位 v 设置为 2
  • 释放共享内存并返回信号量

主线程的工作流程如下:

  • 创建共享内存并设置其大小(CreateFileMapping)
  • 映射到本地地址空间并初始化 IndexHead 中的头尾索引为 0(MapViewOfFile 和 UnmapViewOfFile)
  • 创建信号量并设置其初始值为 1(CreateSemaphore)
  • 创建 MAX_WORKERS 个 worker 线程,并传入各自的 id (从 0 到 MAX_WORKERS - 1)
  • 返回信号量以唤醒第一个 worker 线程(ReleaseSemaphore)
  • 循环等待

客户端

客户端的主要逻辑如下:

  • 创建或打开信号量workSem
  • 循环执行以下步骤:
  • 从标准输入读取用户输入的两个操作数和一个运算符,并封装成RequestData结构体
  • 等待信号量可用,并打开共享内存区域,并映射到虚拟地址空间中
  • 检查当前是否有空闲位置可以放置请求数据(即头尾索引不相邻),如果没有,则释放资源并跳过本次循环
  • 否则,在尾索引对应的位置写入请求数据,并更新尾索引(取模MAX_REQUESTS
  • 释放资源并继续循环执行以下步骤:
  • 等待信号量可用,并打开共享内存区域,并映射到虚拟地址空间中
  • 检查当前是否有结果数据(即标志位为2),如果有,则输出结果,并将标志位置为0
  • 如果头索引等于当前位置,则更新头索引(取模MAX_REQUESTS),直到遇到非空闲位置或回到原点为止(这样可以清理已经处理过的请求)
  • 释放资源并跳出循环
  • 否则,释放资源并继续循环

实现代码

服务器端

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <sstream>
#include <Windows.h>
#include <sysinfoapi.h>

using namespace std;

// 客户端请求数据结构
struct RequestData {
    int v;        //标记内存空间是否可用
    double a;        // 操作数1
    double b;        // 操作数2
    char op;      // 操作符
};

// 服务器处理结果数据结构
struct ResultData {
    int v;        //标记内存空间是否可用
    double result;   // 计算结果
};

struct IndexHead
{
    int headIndex;
    int tailIndex;
};

const int MAX_WORKERS = 4;  // 最大worker数
const int MAX_REQUESTS = 20; // 最大请求队列长度
const int SHARED_MEM_SIZE = sizeof(IndexHead) + MAX_REQUESTS * sizeof(RequestData); // 共享内存大小

const TCHAR* SHARED_MEM_NAME = L"SharedMemory";
const TCHAR* SEM_NAME = L"WorkSemaphore";
HANDLE workSem;         // 请求信号量

vector<thread> workers;      // worker线程池

//worker线程函数
void worker(int id)
{
    while (true)
    {
        WaitForSingleObject(workSem, INFINITE);
        HANDLE hMemory = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_MEM_NAME);
        if (hMemory == nullptr)
        {
            printf("线程 %d,内存访问失败!\n",id);
            ReleaseSemaphore(workSem, 1, NULL);
            Sleep(1000);
            continue;
        }
        
        void* pData = MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, 0);
        if(pData == nullptr)
        {
            CloseHandle(hMemory);
            ReleaseSemaphore(workSem, 1, NULL);
            printf("线程 %d,建立映射失败!\n", id);
            Sleep(10);
            continue;
        }
        IndexHead* pIndex = (IndexHead*)pData;
        if(((IndexHead*)pIndex)->headIndex == ((IndexHead*)pIndex)->tailIndex)
        {
            UnmapViewOfFile(pData);
            CloseHandle(hMemory);
            ReleaseSemaphore(workSem, 1, NULL);
            Sleep(10);
            continue;
        }

        printf("线程 %d,正在响应\n", id);

        int id = ((IndexHead*)pIndex)->headIndex;
        ((IndexHead*)pIndex)->headIndex = (((IndexHead*)pIndex)->headIndex + 1) % MAX_REQUESTS;

        RequestData req;
        req.a = ((RequestData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id))->a;
        req.b = ((RequestData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id))->b;
        req.op = ((RequestData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id))->op;
        UnmapViewOfFile(pData);
        CloseHandle(hMemory);
        ReleaseSemaphore(workSem, 1, NULL);

        ResultData res;
        switch (req.op)
        {
        case '+':
        {
            res.result = req.a + req.b;
        }break;
        case '-':
        {
            res.result = req.a - req.b;
        }break;
        case '*':
        {
            res.result = req.a * req.b;
        }break;
        case '/':
        {
            if (req.b == 0)
            {
                res.result = 2147483647;
            }
            else
            {
                res.result = req.a / req.b;
            }
        }break;
        }

        WaitForSingleObject(workSem, INFINITE);
        hMemory = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_MEM_NAME);
        pData = MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, 0);
        ResultData* pRes = (ResultData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id);
        ((ResultData*)pRes)->result = res.result;
        ((ResultData*)pRes)->v = 2;
        UnmapViewOfFile(pData);
        CloseHandle(hMemory);
        ReleaseSemaphore(workSem, 1, NULL);
    }
}

int main()
{
    HANDLE hShare = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, SHARED_MEM_SIZE, SHARED_MEM_NAME);
    if (hShare == nullptr)
    {
        printf("共享内存创建失败!\n");
        return -1;
    }
    LARGE_INTEGER liFileSize;
    workSem = CreateSemaphore(NULL, 0, 1, SEM_NAME);
    if(workSem == nullptr)
    {
        CloseHandle(hShare);
        printf("信号量创建失败!\n");
        return -1;
    }
    void* pData = MapViewOfFile(hShare, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    if (pData == nullptr)
    {
        //UnmapViewOfFile(pData);
        CloseHandle(workSem);
        CloseHandle(hShare);
        printf("内存映射失败!\n");
        return -1;
    }
    ZeroMemory(pData, SHARED_MEM_SIZE);
    UnmapViewOfFile(pData);

    pData = MapViewOfFile(hShare, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(IndexHead));
    ((IndexHead*)pData)->headIndex = 0;
    ((IndexHead*)pData)->tailIndex = 0;
    UnmapViewOfFile(pData);

    for (int i = 0; i < MAX_WORKERS; i++)
    {
        workers.emplace_back(worker, i);
    }
    cout << "服务器已就绪!\n";
    ReleaseSemaphore(workSem, 1, NULL);
    while (true);
    CloseHandle(workSem);
    CloseHandle(hShare);
    return 0;
}

客户端

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <sstream>
#include <Windows.h>
#include <string>

using namespace std;

// 客户端请求数据结构
struct RequestData {
    int v;        //标记内存空间是否可用
    double a;        // 操作数1
    double b;        // 操作数2
    char op;      // 操作符
};

// 服务器处理结果数据结构
struct ResultData {
    int v;        //标记内存空间是否可用
    double result;   // 计算结果
};

struct IndexHead
{
    int headIndex;
    int tailIndex;
};

const int MAX_WORKERS = 4;  // 最大worker数
const int MAX_REQUESTS = 20; // 最大请求队列长度
const int SHARED_MEM_SIZE = sizeof(IndexHead) + MAX_REQUESTS * sizeof(RequestData); // 共享内存大小

const TCHAR* SHARED_MEM_NAME = L"SharedMemory";
const TCHAR* SEM_NAME = L"WorkSemaphore";
HANDLE workSem;         // 请求信号量

int main()
{
    workSem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, SEM_NAME);
    if (workSem == nullptr)
    {
       // CloseHandle(workSem);
        cerr <<"信号量打开失败,请检查服务器是否启动!\n";
        system("pause");
        return -1;
    }
    while (true)
    {
        RequestData req;
        printf("请输入想要计算的四则运算式(数字与符号之间请用空格隔开,不支持括号,仅支持单次 + - * /):\n");
        cin >> req.a >> req.op >> req.b;
        printf("正在请求服务器,请耐心等待...\n");
        WaitForSingleObject(workSem, INFINITE);
        HANDLE hMemory = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_MEM_NAME);
        if (hMemory == NULL)
        {
            printf("内存访问失败!\n");
            ReleaseSemaphore(workSem, 1, NULL);
            Sleep(1000);
            continue;
        }
        void* pData = MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, 0);
        if (pData == nullptr)
        {
            ReleaseSemaphore(workSem, 1, NULL);
            CloseHandle(hMemory);
            printf("内存映射失败!\n");
            continue;
        }
        if (((IndexHead*)pData)->headIndex == (((IndexHead*)pData)->tailIndex + 1) % MAX_REQUESTS)
        {
            UnmapViewOfFile(pData);
            ReleaseSemaphore(workSem, 1, NULL);
            CloseHandle(hMemory);
            Sleep(10);
            continue;
        }
        int id = ((IndexHead*)pData)->tailIndex;
        ((IndexHead*)pData)->tailIndex = (((IndexHead*)pData)->tailIndex + 1) % MAX_REQUESTS;

        RequestData* pReq = (RequestData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id);
        pReq->v = 1;
        pReq->a = req.a;
        pReq->b = req.b;
        pReq->op = req.op;
        UnmapViewOfFile(pData);
        CloseHandle(hMemory);
        ReleaseSemaphore(workSem, 1, NULL);

        Sleep(1000);

        while (true)
        {
            WaitForSingleObject(workSem, INFINITE);
            hMemory = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_MEM_NAME);
            pData = MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, 0);
            ResultData* pRes = (ResultData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id);
            if (pRes->v == 2)
            {
                cout << "结果为:" << pRes->result << "\n";
                pRes->v = 0;
                IndexHead *pIndex = (IndexHead*)pData;
                while (pIndex->headIndex == id)
                {
                    pRes = (ResultData*)((char*)pData + sizeof(IndexHead) + sizeof(RequestData) * id);
                    if (pRes->v == 0)
                    {
                        pIndex->headIndex = (pIndex->headIndex + 1) % MAX_REQUESTS;
                        id = pIndex->headIndex;
                    }
                    else
                    {
                        UnmapViewOfFile(pData);
                        break;
                    }
                }
                ReleaseSemaphore(workSem, 1, NULL);
                break;
            }
            else
            {
                UnmapViewOfFile(pData);
            }
            CloseHandle(hMemory);
            ReleaseSemaphore(workSem, 1, NULL);
            Sleep(100);
        }
    }
    CloseHandle(workSem);
    return 0;
}

运行效果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值