两个进程的 WinApi SpinLock 方法

Posted

技术标签:

【中文标题】两个进程的 WinApi SpinLock 方法【英文标题】:WinApi SpinLock method for two Processes 【发布时间】:2020-11-27 00:55:17 【问题描述】:

我有一个关于 Win32 API 进程处理的问题。

我有两个进程。进程 1 在启动之前必须运行进程 2,它等待与进程 1 相同的资源。它是一个带有一些整数值的.txt 文件。这意味着应该首先启动 Process1 并运行 Process2。 Process1 必须在 Process2 之后完成。 它应该如下工作: 1.Process1 被创建。 2.进程1被阻塞。 3.Process 2被创建并执行。 4.进程1被解锁并执行。 5.流程2结束。 6.流程1结束。

我在这里搜索了一个与我类似的问题,我只找到了下面的链接,其中显示了 SpinLock 类:

C++11 Implementation of Spinlock using <atomic>

问题在于正确实现它,我已经从main() 函数中删除了我对SpinLock 方法的错误实现。

在实践中几乎找不到任何使用这种方法的例子,因此我问这个问题来看看它:

#include <iostream>
#include <Windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <pthread.h>
#include <atomic>
using namespace std;

class SpinLock 
    atomic_flag locked = ATOMIC_FLAG_INIT ;
public:
    void lock() 
        while (locked.test_and_set(memory_order_acquire))  ; 
    
    void unlock() 
        locked.clear(memory_order_release);
    
;

int main( int argc, TCHAR *argv[] )

    //process 1 generates N random values between 1 and 100,then saves it to txt file i argv[1] stores quanity of values, which will be saved to file
    STARTUPINFO si = ;
    si.cb = sizeof si;
    SpinLock SpinLockVar;
   PROCESS_INFORMATION pi = ;
   const TCHAR* target1 = _T("C:\\USERS\\Admin\\Documents\\File1.exe"); //process 1
   const TCHAR* target2 = _T("C:\\USERS\\Admin\\Documents\\File2.exe");
   //Process 1 , before starting generating values and saving them to file, runs Process2,which is awaiting for access to txt file (SPINLOCK ).
   //Process 1 is terminating after finishing Process 2

   if ( !CreateProcess(target1,GetCommandLine(), 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    
    else
    
        WaitForSingleObject(pi.hProcess, INFINITE);
        
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    
    if ( !CreateProcess(target2,0, 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    
    else
    
        WaitForSingleObject(pi.hProcess, INFINITE);
        /*
        if ( TerminateProcess(pi.hProcess, 0) ) // Evil
            cout << "Process terminated!";
        */
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    

    cin.sync();
    cin.ignore();
    
  

 
    return 0;

更新 我现在已经使用了互斥锁功能,它可以部分工作——它有一个互斥锁“自旋锁”机制,尽管它有时显示得很奇怪。我随机得到了我期望的结果,并且在运行我的程序后随机生成 - cmd 中的第一行来自运行 process2 的线程,第二行是 process1 的结果

请检查我的代码:

#include <windows.h>
#include <stdio.h>
#include <pthread.h>
#include <tchar.h>
#include <mutex>
#include <iostream>
HANDLE hMutex;

DWORD ThreadProc1(LPVOID* arg)  
  
       
      const TCHAR* target = _T("C:\\USERS\\Admin\\Documents\\File2.exe");
    PROCESS_INFORMATION pInfo;
    STARTUPINFO sInfo =  sizeof(pInfo) ;
    BOOL res = CreateProcess(target, 0, 0, FALSE, 0, 0, 0, 0, &sInfo, &pInfo); //process2
    if (!res) return 1;
      
      WaitForSingleObject(pInfo.hThread, INFINITE);
    CloseHandle(pInfo.hThread);
    CloseHandle(pInfo.hProcess);
      return TRUE;  
  

 
int main(void)  
  
     PROCESS_INFORMATION pInfo;
    STARTUPINFO sInfo =  sizeof(pInfo) ;
    const TCHAR* target = _T("C:\\USERS\\Admin\\Documents\\File1.exe");
      HANDLE hThreads;  
      DWORD threadID1; 

      
      hMutex=CreateMutex(NULL, FALSE, NULL); //create mutex(resources=1)
      WaitForSingleObject(hMutex, INFINITE); //process2 call WaitForSingleObject(hmutex) first to get mutex
      hThreads=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc1, &hMutex, 0, &threadID1); 
      WaitForSingleObject(hMutex,INFINITE);//process1 call WaitForSingleObject(hmutex) and block
      BOOL res = CreateProcess(target, GetCommandLine(), 0, FALSE, 0, 0, 0, 0, &sInfo, &pInfo);//process1
      if (!res) return 1;
      ReleaseMutex(hMutex);// process2 do file operations, and then release mutex
      WaitForSingleObject(hMutex,INFINITE);// process1 WaitForSingleObject(hmutex) unblock(resources -1),
      ReleaseMutex(hMutex);  // then release mutex(resources +1) 

      
      CloseHandle(hMutex);   
      WaitForSingleObject(hThreads,INFINITE); 
      CloseHandle(hThreads);    //process 1 closing thread after process 2 ends
     CloseHandle(pInfo.hProcess);
     CloseHandle(pInfo.hThread);
      return 0;
  

【问题讨论】:

需要命名互斥体,使其在进程边界之外可见。使用 CreateMutex()。 仅供参考,向pi.dwThreadId 发布WM_QUIT 消息是没有用的,因为拥有该线程的进程(由pi.hProcess 表示)在WaitForSingleObject(pi.hProcess, INFINITE) 退出时已经完全终止。等待进程HANDLE 的全部意义在于检测进程何时终止。 您提到process2必须在process1之前运行,但是您的代码显示您先运行process1,然后退出并运行process2。你能简单地改变顺序来解决问题而不使用自旋锁吗?因为这两个进程没有同时运行。 或者你想使用CreateFiledwShareMode参数(在2个进程中)来限制其他进程的访问。 实际上我删除了我对这些进程所做的所有更改,因为它们不正确,这就是为什么我有像 Process1,Process2 这样的命令 - 关键是,Process1 在开始之前必须执行 Process 2。我目前正在尝试Create Mutex.. 【参考方案1】:

首先,我认为您不需要使用互斥锁或自旋锁。您可以使用CREATE_SUSPENDED 创建 process1,创建 process2,等待 process2 退出,然后调用 ResumeThread(pi1.hThread),尽管第 4 步和第 5 步可能存在差异。

#include <windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])


    STARTUPINFO si1 =  0 , si2 =  0 ;
    si1.cb = sizeof si1;
    si2.cb = sizeof si2;
    PROCESS_INFORMATION pi1 =  0 , pi2 =  0 ;

    const TCHAR* target1 = _T("C:\\Users\\drakew\\source\\repos\\Project4\\Debug\\Project4.exe");
    const TCHAR* target2 = _T("C:\\Users\\drakew\\source\\repos\\Project6\\Debug\\Project6.exe");


    if (!CreateProcess(target1, 0, 0, FALSE, 0, CREATE_SUSPENDED, 0, 0, &si1, &pi1))
    
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    
    else
    
        printf("Process1 is created ...\n");
        printf("Process1 is blocked ...\n");
        if (!CreateProcess(target2, 0, 0, FALSE, 0, 0, 0, 0, &si2, &pi2))//Process 2 is created and excecuted ...
        
            cerr << "CreateProcess failed (" << GetLastError() << ").\n";
        
        else
        
            WaitForSingleObject(pi2.hProcess, INFINITE);
            printf("Process 2 ends ...\n");
            CloseHandle(pi2.hProcess);
            CloseHandle(pi2.hThread);
        
        ResumeThread(pi1.hThread); //Process 1 is unlocked and executed ...
        WaitForSingleObject(pi1.hProcess, INFINITE);
        printf("Process 1 ends ...\n");
        CloseHandle(pi1.hProcess);
        CloseHandle(pi1.hThread);
    
    cin.sync();
    cin.ignore();

    return 0;

那么,如果你想阻塞在process1的特定位置,那么你必须修改process1和process2来保持同步。

我使用事件来实现锁。

进程1:

#include <Windows.h>
#include <iostream>
using namespace std;
int main()

    HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE| SYNCHRONIZE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE| SYNCHRONIZE, FALSE, L"Global\\MyEvent2");
    printf("process1: step1 ...\n");
    SetEvent(hEvent1);
    printf("process1: step2 ...\n");
    DWORD dwWaitResult = WaitForSingleObject(hEvent2, INFINITE);
    switch (dwWaitResult)
    
    case WAIT_OBJECT_0:
        printf("process1: step4 ...\n");
        break;
    default:
        return FALSE;
    
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);

进程2:

#include <Windows.h>
#include <iostream>
using namespace std;
int main()

    HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, L"Global\\MyEvent2");

    DWORD dwWaitResult = WaitForSingleObject(hEvent1, INFINITE);
    switch (dwWaitResult)
    
    case WAIT_OBJECT_0:
        printf("process2: step3 ...\n");
        SetEvent(hEvent2);
        break;
    default:
        return FALSE;
    
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
    return 1;

主进程:

#include <windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <mutex>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])


    STARTUPINFO si1 =  0 , si2 =  0 ;
    si1.cb = sizeof si1;
    si2.cb = sizeof si2;
    PROCESS_INFORMATION pi1 =  0 , pi2 =  0 ;
    mutex mtx;
    HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, L"Global\\MyEvent2");

    const TCHAR* target1 = _T("C:\\path\\process1.exe");
    const TCHAR* target2 = _T("C:\\path\\process2.exe");


    if (!CreateProcess(target1, 0, 0, FALSE, 0, 0, 0, 0, &si1, &pi1))
    
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    
    else
    
        if (!CreateProcess(target2, 0, 0, FALSE, 0, 0, 0, 0, &si2, &pi2))
        
            cerr << "CreateProcess failed (" << GetLastError() << ").\n";
        
        else
        
            WaitForSingleObject(pi2.hProcess, INFINITE);
            printf("process2: step5 ...\n");
            CloseHandle(pi2.hProcess);
            CloseHandle(pi2.hThread);
        

        WaitForSingleObject(pi1.hProcess, INFINITE);
        printf("process1: step6 ...\n");
        CloseHandle(pi1.hProcess);
        CloseHandle(pi1.hThread);
    
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
    cin.sync();
    cin.ignore();
    return 0;

结果:

【讨论】:

谢谢,我会试试的,虽然我设法在上面添加了 Mutex - update ;) 我用了你回复的第一个代码,稍微修改了一下,问题就解决了:)

以上是关于两个进程的 WinApi SpinLock 方法的主要内容,如果未能解决你的问题,请参考以下文章

32位 64位 获得进程peb的方法

为啥尝试绕行winapi时进程崩溃?

winApi使用c++创建进程

如何获取具有可见窗口的任何进程的名称 - WinAPI?

C#、C++、WinAPI - 从另一个进程获取窗口数

进程(WINAPI),遍历并查找树状的进程信息,实现控制系统进程