首页 › Category Archives › Visual Studio

(C语言)多进程实现消费者生产者pv操作(Windows和Linux版本)

多进程实现消费者生产者问题

一,实验目的
1,了解生产者消费者的互斥与同步问题
2,掌握Windows和Linux的进程通信方法

二,实验要求
完成Windows版本和Linux版本。
一个大小为3的缓冲区,初始为空。
2个生产者
随机等待一段时间,往缓冲区添加数据,
若缓冲区已满,等待消费者取走数据后再添加
重复6次

3个消费者
随机等待一段时间,从缓冲区读取数据
若缓冲区为空,等待生产者添加数据后再读取
重复4次

显示每次添加和读取数据的时间及缓冲区的状态

三,实验环境
Windows版本:Windows 10 64位系统,Dev-cpp编译器
Linux版本:Fedora29版本,gcc环境 vim文本编辑器

四,实验代码结构
1),pv操作伪代码:
array[3]:interger//缓冲区定义,大小为三
int empty=3,full=0;
int mutex=1;
i=0,j=0//缓冲区指针
x,y:item //产品变量
生产者: 消费者:
begin:
produce a product to x;
P(empty);
P(mutex);
array[i]=x;
ii=(i+1)%3;
V(full);
V(mutex);
,,,,,, ………,
End

消费者:

2)实验代码分析

Windows版本:

思路分析:Windows创建多进程使用creatprocess()函数调用自己,通过多次创建得到两个生产者进程三个消费者进程,在之中运行相应的生产者函数,消费者函数。在通过传入参数不同,来辨别是第一次主进程还是生产者进程,消费者进程。通过构建共享内存区进行进程间通信。

①多进程创建

② 构建共享内存区,再将文件映射到本进程,初始化

③在主创建进程间信号量full empty

分别在生产者消费者进程创建互斥访问量mutex

④同过argv量的不同判断进程归属

⑤运行结果:

全部代码见后

Linux版本:

思路分析:Linux使用fork进行多进程创建,分别在进程中运行消费者函数,生产者函数。建立共享主存区很信号量在进程建进行通信和缓存访问

②建立共享主存并进行映射

③创建进程间信号量full,empty和互斥量 mutex,并初始化

④实验结果:

五, 实验总结

本次实验获得圆满成功。

本次实验通过分别编写Windows和Linux版本的多进程实现消费者和生产者问题,了解生产者消费者的互斥与同步问题,掌握Windows和Linux的进程通信方法,也同时加强自己对多进程操作的理解。

代码:
Windows版本:

//实验三生产者消费者 
#include <windows.h>
#include <stdio.h>
#include <time.h>


HANDLE handleOfProcess[5];

struct buf
{
    char buffer[3];
    int write;
    int read;
};

int rand_1()
{
    return rand() % 100 + 1000;
}

char rand_char()
{
    return rand() % 26 + 'A';
}

void StartClone(int nCloneID)
{
    TCHAR szFilename[MAX_PATH];
    GetModuleFileName(NULL, szFilename, MAX_PATH);

    TCHAR szCmdLine[MAX_PATH];
    sprintf(szCmdLine, "\"%s\" %d", szFilename, nCloneID);
    //printf("%s\n",szCmdLine);

    STARTUPINFO si;
    ZeroMemory(reinterpret_cast<void*>(&si), sizeof(si));

    si.cb = sizeof(si);
    PROCESS_INFORMATION pi;

    BOOL bCreateOK = CreateProcess(
        szFilename,
        szCmdLine,
        NULL,
        NULL,
        FALSE,
        CREATE_DEFAULT_ERROR_MODE,
        NULL,
        NULL,
        &si,
        &pi);
    if (bCreateOK)
        handleOfProcess[nCloneID] = pi.hProcess;
    else
    {
        printf("Error in create process!\n");
        exit(0);
    }
}

void pro()//生产者
{
    HANDLE mutex = CreateMutex(NULL, FALSE, "MYMUTEX");
    HANDLE empty = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "MYEMPTY");
    HANDLE full = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "MYFULL");

    HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "myfilemap");
    LPVOID Data = MapViewOfFile(//文件映射
        hMap,
        FILE_MAP_ALL_ACCESS,
        0,
        0,
        0);
    struct buf* pint = reinterpret_cast<struct buf*>(Data);

    for (int i = 0; i < 6; i++)
    {
        WaitForSingleObject(empty, INFINITE);
        //sleep
        srand((unsigned)time(0));
        int tim = rand_1();
        Sleep(tim);

        WaitForSingleObject(mutex, INFINITE);

        //code
        pint->buffer[pint->write] = rand_char();
        pint->write = (pint->write + 1) % 3;


        ReleaseMutex(mutex);
        ReleaseSemaphore(full, 1, NULL);

        SYSTEMTIME syst;
        time_t t = time(0);
        GetSystemTime(&syst);
        char tmpBuf[10];
        strftime(tmpBuf, 10, "%H:%M:%S", localtime(&t));
        printf("生产者%d向缓冲区写入数据:\t%c\t%c\t%c\t@%s.%d\n",
            (int)GetCurrentProcessId(), pint->buffer[0], pint->buffer[1],
            pint->buffer[2], tmpBuf, syst.wMilliseconds);
        fflush(stdout);
    }
    UnmapViewOfFile(Data);//解除映射
    Data = NULL;
    CloseHandle(mutex);
    CloseHandle(empty);
    CloseHandle(full);
}

void con()//消费者
{
    HANDLE mutex = CreateMutex(NULL, FALSE, "MYMUTEX");
    HANDLE empty = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "MYEMPTY");
    HANDLE full = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "MYFULL");

    HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "myfilemap");
    LPVOID Data = MapViewOfFile(//文件映射
        hMap,
        FILE_MAP_ALL_ACCESS,
        0,
        0,
        0);
    struct buf* pint = reinterpret_cast<struct buf*>(Data);

    for (int i = 0; i < 4; i++)
    {
        WaitForSingleObject(full, INFINITE);
        //sleep
        srand((unsigned)time(0));
        int tim = rand_1();
        Sleep(tim);

        WaitForSingleObject(mutex, INFINITE);

        pint->buffer[pint->read] = ' ';
        pint->read = (pint->read + 1) % 3;

        ReleaseMutex(mutex);
        ReleaseSemaphore(empty, 1, NULL);
        //code

        time_t t = time(0);
        char tmpBuf[10];
        SYSTEMTIME syst;
        GetSystemTime(&syst);
        strftime(tmpBuf, 10, "%H:%M:%S", localtime(&t));

        printf("消费者%d从缓冲区读取数据:\t%c\t%c\t%c\t@%s.%d\n",
            (int)GetCurrentProcessId(), pint->buffer[0], pint->buffer[1],
            pint->buffer[2], tmpBuf, syst.wMilliseconds);
        fflush(stdout);

    }
    UnmapViewOfFile(Data);//解除映射
    Data = NULL;
    CloseHandle(mutex);
    CloseHandle(empty);
    CloseHandle(full);
}

int main(int argc, char* argv[])
{
    int nCloneID = 20;
    if (argc > 1)
    {
        sscanf(argv[1], "%d", &nCloneID);
    }

    if (nCloneID < 2)//生产者进程
    {
        pro();
    }
    else if (nCloneID < 5)//消费者进程
    {
        con();
    }
    else//主进程
    {
        HANDLE hMap = CreateFileMapping(
            NULL,
            NULL,
            PAGE_READWRITE,
            0,
            sizeof(struct buf),
            "myfilemap");

        if (hMap != INVALID_HANDLE_VALUE)
        {
            LPVOID Data = MapViewOfFile(//文件映射
                hMap,
                FILE_MAP_ALL_ACCESS,
                0,
                0,
                0);

            if (Data != NULL)
            {
                ZeroMemory(Data, sizeof(struct buf));
            }
            struct buf* pnData = reinterpret_cast<struct buf*>(Data);
            pnData->read = 0;
            pnData->write = 0;
            memset(pnData->buffer, 0, sizeof(pnData->buffer));
            UnmapViewOfFile(Data);//解除映射
            Data = NULL;
        }

        HANDLE empty = CreateSemaphore(NULL, 3, 3, "MYEMPTY");
        HANDLE full = CreateSemaphore(NULL, 0, 3, "MYFULL");
        for (int i = 0; i < 5; i++)//创建子进程
            StartClone(i);
        WaitForMultipleObjects(5, handleOfProcess, TRUE, INFINITE);
        CloseHandle(empty);
        CloseHandle(full);
    }
}

Linux版本:

//实验三:生产者消费者                                                                                                                                                                                             
#include<string.h>
#include <sys/time.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>
#include<sys/select.h>
#include<sys/wait.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<time.h>
#define SEM_ID1 225
#define SEM_ID2 97
#define SEM_ID3 234
#define SHMKEY 75

struct buf {
    char buffer[3];
    int read;
    int write;
};

int rand_1()
{
    return rand() % 300;
}

void sleep_ms(int s)
{
    usleep(s * 10000);
}

char* cur_time()
{
    time_t timep;
    time(&timep);
    return ctime(&timep);
}
char rand_char()
{
    return rand() % 26 + 'A';
}
void P(int s)//p操作 
{
    struct sembuf sem_op;
    sem_op.sem_num = 0;
    sem_op.sem_op = -1;
    sem_op.sem_flg = 0;
    semop(s, &sem_op, 1);
}
void V(int s)//v操作 
{
    struct sembuf sem_op;
    sem_op.sem_num = 0;
    sem_op.sem_op = 1;
    sem_op.sem_flg = 0;
    semop(s, &sem_op, 1);

}
void pro()//生产者
{
    int tim, shmid, i = 6;
    int sem_mutex, sem_empty, sem_full;
    void* addr;
    struct buf* pint;
    struct sembuf sem_op;
    struct timeval tv;
    sem_mutex = semget(SEM_ID1, 1, 0600);
    sem_empty = semget(SEM_ID2, 1, 0600);
    sem_full = semget(SEM_ID3, 1, 0600);
    shmid = shmget(SHMKEY, sizeof(struct buf), 0777);
    addr = shmat(shmid, 0, 0);
    while (i--)
    {
        gettimeofday(&tv, NULL);
        srand((unsigned)tv.tv_usec);
        tim = rand_1();
        sleep_ms(tim);
        //P(empty)
        P(sem_empty);
        //P(mutex)
        P(sem_mutex);

        pint = (struct buf*)addr;

        // pint[semctl(sem_full,0,GETVAL)]=time;
        pint->buffer[pint->write] = rand_char();
        pint->write = (pint->write + 1) % 3;

        printf("当前生产者进程:%d 写入数据:\t%c\t%c\t%c\t@%lds%ldus\n",
            getpid(), pint->buffer[0], pint->buffer[1],
            pint->buffer[2], tv.tv_sec, tv.tv_usec);


        //V(mutex)
        V(sem_mutex);
        //V(full)
        V(sem_full);
    }
    shmdt(addr);
}
void con()//消费者
{
    int tim, shmid, i = 4;
    int sem_mutex, sem_empty, sem_full;
    void* addr;
    struct buf* pint;
    struct sembuf sem_op;
    struct timeval tv;
    sem_mutex = semget(SEM_ID1, 1, 0600);
    sem_empty = semget(SEM_ID2, 1, 0600);
    sem_full = semget(SEM_ID3, 1, 0600);
    shmid = shmget(SHMKEY, sizeof(struct buf), 0777);
    addr = shmat(shmid, 0, 0);
    while (i--)
    {
        gettimeofday(&tv, NULL);
        srand((unsigned)tv.tv_usec);
        tim = rand_1();
        sleep_ms(tim);
        //P(full)
        P(sem_full);
        //P(mutex)
        P(sem_mutex);

        pint = (struct buf*)addr;

        pint->buffer[pint->read] = ' ';
        pint->read = (pint->read + 1) % 3;

        printf("当前消费者进程:%d 读取数据:\t%c\t%c\t%c\t@%ldst%ldms\n",
            getpid(), pint->buffer[0], pint->buffer[1],
            pint->buffer[2], tv.tv_sec, tv.tv_usec);

        //V(mutex)
        V(sem_mutex);
        //V(empty)
        V(sem_empty);

    }
    shmdt(addr);
}

int main()
{
    int sem_mutex, sem_empty, sem_full, shmid;
    void* addr;
    union semun {
        int val;
    }empty, full, mutex;

    //建立信号量 
    sem_mutex = semget(SEM_ID1, 1, IPC_CREAT | 0600);
    sem_empty = semget(SEM_ID2, 1, IPC_CREAT | 0600);
    sem_full = semget(SEM_ID3, 1, IPC_CREAT | 0600);

    full.val = 0;
    empty.val = 3;
    mutex.val = 1;

    semctl(sem_mutex, 0, SETVAL, mutex);
    semctl(sem_empty, 0, SETVAL, empty);
    semctl(sem_full, 0, SETVAL, full);

    //建立共享内存并进行映射 
    shmid = shmget(SHMKEY, sizeof(struct buf), 0777 | IPC_CREAT);
    if (-1 == shmid)
    {
        printf("建立共享内存失败\n");
        exit(0);
    }
    addr = shmat(shmid, 0, 0);
    memset(addr, 0, sizeof(struct buf));

    //执行生产者进程 
    for (int i = 0; i < 2; i++)
        if (fork() == 0)
        {
            pro();
            exit(0);
        }
    //执行消费者进程 
    for (int i = 0; i < 3; i++)
        if (fork() == 0)
        {
            con();
            exit(0);
        }

    while (-1 != wait(0));
    semctl(sem_mutex, 0, IPC_RMID);
    semctl(sem_empty, 0, IPC_RMID);
    semctl(sem_full, 0, IPC_RMID);
    shmdt(addr);
}

vc++6.0 远程调试

VS2005 vs2008 vs2010 vs2012 远程调试上篇已经讲过,这里再讲下VC++6.0开发环境下的远程调试能力,仅需下面4步即可,更方便的就接着后面的5-6步。

因为目标程序需要在有采集卡等相关硬件支持下的工控机上工作,工控机上面一般没有调试环境也不会放置源代码(出于保密)。假如工控机上的目标程序运行中出了问题,就需要有调试环境却无法安装采集卡等硬件的笔记本上进行远程调试。 

一。远程调试条件

1.两者之间使用有线或无线连网。

2.目标机器与本机的调试机器连接通讯,需要四个文件:MSVCMON.EXE、DM.DLL、TLN0T.DLL和MSDIS110.DLL。它们可在VC6安装文件夹的Common/MSDEV98/Bin下面找到。
继续阅读 »

VS2005 vs2008 vs2010 vs2012 远程调试

一 远程端的设置

1. 远程机器端 的安全选项
在远程机器上,开始——设置——控制面板——管理工具——本地安全策略——本地策略——安全选项,在右侧找到网络访问:本地帐户的共享和安全模式,双击它,将“进来宾 – 本地用户以来宾身份验证”,改为:“经典 – 本地用户以自己的身份验证”  

2.远程端 运行VS 远程调试监视器
将本地文件夹下远程调试监视器所在文件夹(具体位置见后面 四。注意事项)下的所有文件,全部复制到远程计算机中,比如到 E:\remote\x86下。这里以vs2005为例,其余只是版本不同,操作都一样。
双击启动其中的msvsmon.exe,在其菜单,工具——选项 中,更改身份验证方式。
2.1  选择 无身份验证(仅限本机),并勾选“允许任何用户进行调试”。“服务器名”将显示 tu:4015, 其中的tu是远程计算机的名称,4015是端口号。
2.2  选择 Windows身份验证。“服务器名”,将显示 BT\lk@tu, 其中,BT 是局域网的域名,lk是登陆的用户名称,tu是远程计算机的名称。如无登录域,则显示为:lk@tu,其中,lk是登陆的用户名称,tu是远程计算机的名称。
继续阅读 »

Codeblocks更改编译器为VC++6.0

有时C/C++代码在Codeblocks上不能很好的运行,这是由于代码不标准造成的,Codeblocks编译器默认是GNU GCC,GCC遵循C++标准,而VC++是不太一样的。这篇文章介绍在Codeblocks下如何编译、调试VC 6.0的项目。(有错误的话请指正^_^)
条件
1、安装好Codeblocks16.01带MinGW
2、安装好VC++6.0(路径C:\Program Files (x86)\Microsoft Visual Studio)
步骤
1、打开Codeblocks,选择”Settings” – Compiler”,按图中步骤进行,C:\Program Files (x86)\Microsoft Visual Studio\VC98在VC++安装目录内

继续阅读 »

对大端小端的研究

简单的来说,通俗理解:
比如数据0x12345678
大端:地址由低到高,先放数据的高位,就是0x12,0x34,0x56,0x78;
小端:地址由低到高,先放数据的低位,就是0x78,0x56,0x34,0x12;
继续阅读 »

_beginthread和CreatThread的区别

我们知道在Windows下创建一个线程的方法有两种,一种就是调用Windows API CreateThread()来创建线程;另外一种就是调用MSVC CRT的函数_beginthread()或_beginthreadex()来创建线程。相应的退出线程也有两个函数Windows API的ExitThread()和CRT的_endthread()。这两套函数都是用来创建和退出线程的,它们有什么区别呢?

很多开发者不清楚这两者之间的关系,他们随意选一个函数来用,发现也没有什么大问题,于是就忙于解决更为紧迫的任务去了,而没有对它们进行深究。等到有一天忽然发现一个程序运行时间很长的时候会有细微的内存泄露,开发者绝对不会想到是因为这两套函数用混的结果。
继续阅读 »

C++中GB2312字符串和UTF-8之间的转换

在编程过程中需要对字符串进行不同的转换,特别是Gb2312和Utf-8直接的转换。在几个开源的魔兽私服中,很多都是老外开发的,而暴雪为了能 够兼容世界上的各个字符集也使用了UTF-8。在中国使用VS(VS2005以上版本)开发基本都是使用Gb2312的Unicode字符集,所以当在编 程过程中就需要进行字符转换,这样才能兼容游戏,否则就是乱码。而在控制台显示字符串时,真好相反需要将UTF-8的字符串转换成Gb2312才能正常显 示。

为了解决这个问题,本人将其代码贴出来;其实很多地方都可以使用到字符串的编码转换,代码如下:

//UTF-8到GB2312的转换
char* U2G(const char* utf8)
{
	int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len+1];
	memset(wstr, 0, len+1);
	MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
	len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len+1];
	memset(str, 0, len+1);
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
	if(wstr)
		delete[] wstr;
	return str;
}
//GB2312到UTF-8的转换
char* G2U(const char* gb2312)
{
	int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len+1];
	memset(wstr, 0, len+1);
	MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len+1];
	memset(str, 0, len+1);
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
	if(wstr)
		delete[] wstr;
	return str;
}

无论是GB2312到UTF-8的转换,还是UTF-8到GB2312的转换,都需要注意的是在使用字符串后,需要删除字符串指针;

静态库与动态库相互调用

测试场景,Test,lib1,lib2,dll1,dll2,分为下面四种情况:
1、Test->lib1->lib2
lib1编译自己的代码,对lib2的部分,只需要lib2的头文件,对lib2的代码实现,使用占位符关联。
生成Test连接的时候,把lib1的代码实现包含进来,再递归,把lib1中关联lib2的代码实现也包含进来。
运行Test的时候,不再需要lib1和lib2。

2、Test->lib1->dll2
lib1编译自己的代码,对dll2的部分,只需要dll2的头文件,对dll2的代码实现,使用占位符关联。
生成Test连接的时候,把lib1的代码实现包含进来,但是,dll2中的代码不包含进来。
运行Test的时候,不需要lib1,但是需要dll2【不需要dll的lib文件】

3、Test->dll1->lib2
dll1编译自己的代码,对lib2的部分,需要lib2的头文件和实现,即lib2,把lib2的代码实现包含到dll1中
生成Test连接的时候,dll1中的代码不包含进来,并且不再需要lib2。
【可以这样测试,生成lib2,生成dll1,生成test.exe,删除lib2,删除test.exe,再生成test.exe,可以生成成功。说明生成test.exe,连接的时候根本不需要lib2,。也就是说,静态库会被连接到动态库或者exe中,但是不会被连接到其他的静态库中】
运行Test的时候,需要dll1,但是不需要lib2

4、Test->dll1->dll2
dll1编译自己的代码,对dll2的部分,需要dll2的头文件和lib文件,对dll2的代码实现,使用占位符关联。
生成Test连接的时候,Test需要dll1的lib,dll1需要dll2的lib,但是代码实现都不会包含在Test中。
运行Test的时候,需要dll1和dll2【不需要他们的lib】

VS2017多版本VC支持

前言

研究目标:VS2017作为目前最新的IDE,让其支持所有版本的VC编译环境

必要性:报建通依赖的特定版本的AutoCAD作为“宿主”环境,作为一个通用产品,就需要支持所有的AutoCAD版本。现在AutoCAD版本需要支持的版本有AutoCAD 2005-2015,其中从AutoCAD 2010开始支持x64,按照一般情况,就需要针对每一个版本安装一个对应的Visual Studio IDE版本,这样给“环境的搭建”、“项目工程的管理”带来很大的挑战和负担

难点:目前每个Visual Studio集成编译环境仅支持对应版本的工程,且作为微软闭源的产品,不支持人为配置
继续阅读 »

简单方法解决Vista/Win7/Win10无法接收WM_DROPFILES问题

今天真给这WM_DROPFILES郁闷了下,程序本来运行的好好的,后来改为以管理员身份运行后愣是没反应。没办法,程序必须要有一定的权限才能正常工作,偶菜鸟只能问人找资料了。

在MSDN上翻到了“Understanding and Working in Protected Mode Internet Explorer”,知道了Vista(当然包括Win7)introduced the UIPI which does not allow low right process to send message to the high right processes, 就不让你低权限的给高权限的发消息,~
里面有“Allowing Drag and Drop Operations in your Application”,可是,我按照去做了,往注册表加了那些东西,还是没反应,那个郁闷,囧~
再后来,Google到了CSDN,哈,还是那的答案给我解决了!简单~
加上
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
其中#define WM_COPYGLOBALDATA 0x0049,说是DDK里面的头文件有~
继续阅读 »