Windows系统编程 - 进程间通信

文章目录

  • 前言
    • 概述
    • 发送消息WM_COPYDATA
    • DLL共享段
    • 文件映射
      • 文件映射步骤
      • 相关API讲解
      • 文件映射 进程间的通信(有文件版本)
      • 文件映射 进程间的通信(匿名版本)
    • 管道
      • 相关API讲解
      • 父子之间的匿名进程通信
        • GetStdHandle
        • STARTUPINFO指定句柄
        • 测试案例
      • 模拟CMD
  • 总结

前言

  • 各位师傅大家好,我是qmx_07,今天给大家讲解进程间通信的相关知识
    在这里插入图片描述

概述

  • 进程间通信(Inter-Process Communication,IPC)是指不同进程之间进行数据交换、信息共享、同步互斥等操作的机制

发送消息WM_COPYDATA

  • 介绍:WM_COPYDATA是一个Windows消息,用于在进程之间传递数据,使用结构体COPYDATASTRUCT
typedef struct tagCOPYDATASTRUCT {ULONG_PTR dwData;//自定义数据,可以用于标识传递的数据类型或其他用途DWORD     cbData;//数据大小PVOID     lpData;//传递的数据
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
  • 通过SendMessage发送消息

案例:创建两个项目工程,发送方 和 接收方
发送方:
在这里插入图片描述

void CSendDlg::OnBnClickedSend()
{//获取编辑框内容CString strText;GetDlgItemText(IDC_EDIT, strText);//获取窗口句柄HWND hWnd = ::FindWindow(NULL, "recvcopy");//编辑要发送的数据COPYDATASTRUCT cds;cds.dwData = 0x12345678;cds.cbData = strText.GetLength();cds.lpData = strText.GetBuffer();//发送数据::SendMessage(hWnd, WM_COPYDATA, (WPARAM)GetSafeHwnd(),(LPARAM)&cds);// TODO: 在此添加控件通知处理程序代码
}

接收方:
在这里插入图片描述

  • 通过类向导,创建WM_COPYDATA消息
BOOL CreceiveDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{// TODO: 在此添加消息处理程序代码和/或调用默认值CString strFmt;strFmt.Format("%08X %s",pCopyDataStruct->dwData,pCopyDataStruct->lpData);AfxMessageBox(strFmt);return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}
  • 接收数据,弹窗输出
    画面演示:
    在这里插入图片描述
    使用WM_COPYDATA消息进行进程间通信,我修改A进程的发送数据,B进程接收到数据会受影响吗?

如果A进程通过WM_COPYDATA消息向B进程发送数据,那么B进程接收到的数据不会受到A进程后续的修改影响。因为WM_COPYDATA消息是将A进程的数据拷贝一份并发送给B进程的,之后B进程操作的是自己的一份拷贝,和A进程的数据无关。所以,A进程修改数据不会影响B进程接收到的数据

在这里插入图片描述

其中,发送进程通过发送WM_COPYDATA消息向接收进程发送数据。在接收进程接收到数据后,发生了两次内存拷贝,第一次是将数据拷贝至共享内存中,第二次是从共享内存中读取数据。最后,接收进程向发送进程发送响应消息。WM_COPYDATA可以携带少量数据;效率比较低 ,是因为WM_COPYDATA先将数据拷贝到高2G系统内存中,再从高2G内存中拷贝到目标进程中,发生两次拷贝

缺点

  1. 数据的传输是单向的。
  2. 效率低:从A进程拷贝到B进程 数据拷贝了 2 次。先将数据拷贝到高 2 G内存中,然后再从 高 2 G拷贝到目标进程中(发生两次拷贝,所以效率比较低)
  3. 必须是带有窗口之间才能通信(且必须有标题);
  4. 消息大小受限:WM_COPYDATA 消息传递的数据大小受到系统限制,默认为 4MB。如果需要传递更大的数据,就需要将数据分成多个块来传递

原理:通过系统的高2G内存来达到传输,因为高 2 G的内存是所有引用共享的。

适用场景:数据小,发送频繁等不建议使用,大小不限制

DLL共享段

  • 介绍:当一个进程加载一个 DLL 时,该进程会将该 DLL 的实例映射到该进程的虚拟地址空间中,虽然每个进程都有自己的虚拟地址空间,但是它们共享同一个物理内存,这样就可以实现共享代码和数据的目的
  • 好处:节省了内存空间,因为多个进程可以共享同一份物理内存,这可以减少系统资源的消耗
  1. 定义共享段,并定义导出变量,注意导出需要初始化,未初始化不给实际内存
//为共享数据开辟空间
#pragma data_seg("cr41")
_declspec(dllexport) int g_nVal = 0;
#pragma data_seg()
  1. 链接选项将此共享段声明为可共享
//为开辟的空间 设置共享
#pragma comment(linker,"/SECTION:cr41,RWS")
  • 将共享段导出,设置可读、可写、可执行权限
    在这里插入图片描述
    使用方:
#pragma comment(lib,"Dll.lib")
_declspec(dllimport) extern int g_nVal;
void CUseDllDlg::OnBnClickedShow()
{SetDlgItemInt(EDT_SHOW, g_nVal);// TODO: 在此添加控件通知处理程序代码
}void CUseDllDlg::OnBnClickedWrite()
{g_nVal = GetDlgItemInt(EDT_WRITE);// TODO: 在此添加控件通知处理程序代码
}

在这里插入图片描述

  • 注意:如果全局变量不初始化的话,进程间不共享
  • 已经初始化为0的全局变量通常被编译器放在数据段(.data段)中,因此它们在DLL加载到内存时已经被初始化为0。因为它们已经被初始化,所以它们的值可以被不同的进程共享,而且不同进程中的值都是相同的
  • 未初始化的全局变量通常被编译器放在BSS段(.bss段)中,这些变量在程序加载时会被清零。因为它们在程序加载时才被初始化,所以它们的值在不同的进程中是不同的,不能被不同进程共享

文件映射

  • 介绍:文件映射是一种进程间通信的机制,可以通过将一个文件映射到多个进程的虚拟地址空间来实现数据的共享。在文件映射中,可以分为有文件和无文件的区别
  • 有文件:指将一个实际的文件映射到多个进程的虚拟地址空间中,在这种情况下,文件映射的数据是持久的,即文件的内容在进程结束后仍然存在
  • 无文件:指在进程间通信中使用文件映射的机制,但并不依赖于实际的文件,进程可以通过创建一个匿名的文件映射对象,将其映射到多个进程的虚拟地址空间中。在这种情况下,映射的数据源并不是一个实际的文件,而是系统内存中的一块共享内存区域,无文件的文件映射的数据是临时的,即在进程结束后会被释放
  • 适用场景:无文件的文件映射通常用于临时共享数据、进程间通信等场景,不需要将数据持久保存在文件中

文件映射步骤

在这里插入图片描述

  1. 打开文件:首先,需要打开需要操作的文件,可以使用标准的文件操作函数,如CreateFile等来打开文件。
  2. 创建文件映射对象:使用CreateFileMapping函数创建一个文件映射对象,该函数会返回一个句柄,该句柄可以用于后续的文件映射操作。
  3. 映射文件到内存:使用MapViewOfFile函数将文件映射到内存中,该函数也会返回一个指针,该指针指向文件在内存中的起始位置。
  4. 执行读写操作:通过操作内存中的数据来进行读写操作,内存中的数据会自动同步到文件中。
  5. 取消文件映射:使用UnmapViewOfFile函数取消文件映射,释放内存空间。
  6. 关闭文件句柄:使用CloseHandle函数关闭文件句柄和文件映射对象句柄。

相关API讲解

映射文件对象:

HANDLE CreateFileMappingW([in]           HANDLE                hFile,//文件句柄[in, optional] LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//安全描述符,填写NULL 为默认安全[in]           DWORD                 flProtect,//文件映射对象的页保护[in]           DWORD                 dwMaximumSizeHigh,//高地址位[in]           DWORD                 dwMaximumSizeLow,//低地址位[in, optional] LPCWSTR               lpName//文件映射对象的名称,为NULL创建匿名的映射对象
);

页保护对象值:
在这里插入图片描述
将文件对象映射到内存:

LPVOID MapViewOfFile([in] HANDLE hFileMappingObject,//文件映射对象的句柄[in] DWORD  dwDesiredAccess,//文件映射对象的访问类型,比如读、写、执行[in] DWORD  dwFileOffsetHigh,//高地址位[in] DWORD  dwFileOffsetLow,//低地址位[in] SIZE_T dwNumberOfBytesToMap//要映射的文件字节数
);

在这里插入图片描述

文件映射 进程间的通信(有文件版本)

A方:

#include <iostream>
#include <Windows.h>using namespace::std;int main()
{//打开文件HANDLE hFile = CreateFile(R"(G:\c_study\Dll\Debug\Dll.dll)",GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);//判断文件是否正常打开if (hFile == INVALID_HANDLE_VALUE){cout << "打开文件失败!" << endl;return 0;}//映射对象HANDLE hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0, 0,//操作整个文件"cr41map"//映射对象名称);//判断映射对象是否成功if (hFileMap == NULL){cout << "文件映射对象失败" << endl;CloseHandle(hFile);return 0;}//映射LPVOID pView = MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0, 0,0x1000);//映射是否成功if (pView == NULL){cout << "映射失败" << endl;}//清理资源UnmapViewOfFile(pView);CloseHandle(hFileMap);CloseHandle(hFile);
}

B方:

#include <iostream>
#include <Windows.h>using namespace::std;int main()
{//映射对象HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,"cr41map");//判断映射对象是否成功if (hFileMap == NULL){cout << "文件映射对象失败!" << endl;return 0;}//映射LPVOID pView = MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0, 0,0x1000);//映射是否成功if (pView == NULL){cout << "映射失败" << endl;}//清理资源UnmapViewOfFile(pView);CloseHandle(hFileMap);
}

演示:
在这里插入图片描述

文件映射 进程间的通信(匿名版本)

A方:

#include <iostream>
#include <Windows.h>using namespace::std;int main()
{//映射对象HANDLE hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0, 0x1000,"cr41map");//判断映射对象是否成功if (hFileMap == NULL){cout << "文件映射对象失败" << endl;return 0;}//映射LPVOID pView = MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0, 0,0x1000);//映射是否成功if (pView == NULL){cout << "映射失败" << endl;}//清理资源UnmapViewOfFile(pView);CloseHandle(hFileMap);
}

B方:

#include <iostream>
#include <Windows.h>using namespace::std;int main()
{//映射对象HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,"cr41map");//判断映射对象是否成功if (hFileMap == NULL){cout << "文件映射对象失败!" << endl;return 0;}//映射LPVOID pView = MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0, 0,0x1000);//映射是否成功if (pView == NULL){cout << "映射失败" << endl;}//清理资源UnmapViewOfFile(pView);CloseHandle(hFileMap);}

演示:
在这里插入图片描述

管道

  • 概念:可跨进程的队列,实现进程之间的数据传输
  • 种类:命名管道,匿名管道

命名管道:是一种有名字的管道,它可以被多个进程同时使用,命名管道适用于本地进程之间的通信和远程进程之间的通信

匿名管道:命名管道适用于本地进程之间的通信和远程进程之间的通信,主要用于父子进程之间的数据传输。(单向传输)
在这里插入图片描述
在这里插入图片描述

  • 父进程读取的管道以及子进程读取的管道,相应的子进程也可以对父进程读取的管道进行传输数据

相关API讲解

创建匿名管道:

BOOL CreatePipe([out]          PHANDLE               hReadPipe,//管道读取句柄[out]          PHANDLE               hWritePipe,//管道写入句柄[in, optional] LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全属性,NULL为默认的安全属性[in]           DWORD                 nSize//填写0,为默认的缓冲区长度
);

查看管道是否有数据可读:

BOOL PeekNamedPipe([in]            HANDLE  hNamedPipe,//管道句柄[out, optional] LPVOID  lpBuffer,//缓冲区指针,用于读取数据[in]            DWORD   nBufferSize,//缓冲区大小[out, optional] LPDWORD lpBytesRead,//用于存储已读取的缓冲区大小[out, optional] LPDWORD lpTotalBytesAvail,//存储可用的字节数大小[out, optional] LPDWORD lpBytesLeftThisMessage//用于存储当前消息中剩余的字节数。该参数只对消息式管道有效,对字节流式管道没有意义
);
  • 若要从管道读取,请在调用 ReadFile 函数时使用管道的读取句柄
  • 若要写入管道,请在调用 WriteFile 函数时使用管道的写入句柄
  • 如果管道缓冲区已满,并且有更多的字节需要写入,则 WriteFile 不会返回,直到另一个进程从管道中读取内容,从而提供更多的缓冲区空间。 管道服务器在调用 CreatePipe时指定管道的缓冲区大小。

父子之间的匿名进程通信

步骤:

  1. 获取标准输入输出的句柄
  2. 继承句柄
  3. 创建管道的时候;
  4. 创建子窗口的时候;
  5. 创建进程时需要STARTUPINFO指定句柄
GetStdHandle

**介绍:**获取标准输入、标准输出或标准错误设备的句柄

HANDLE WINAPI GetStdHandle(_In_ DWORD nStdHandle
);

在这里插入图片描述

STARTUPINFO指定句柄

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

  • 通过设置STARTUPINFO的输入输出,来获取句柄
测试案例
  • 父进程:
    在这里插入图片描述
oid CParentDlg::OnBnClickedCreate()
{SECURITY_ATTRIBUTES sa = { };sa.nLength = sizeof(sa);sa.bInheritHandle = TRUE;BOOL bRet = CreatePipe(&m_hChildRead, &m_hParentWrite, &sa, 0);if (!bRet){AfxMessageBox("创建管道失败!");}bRet = CreatePipe(&m_hParentRead, &m_ChildWrite, &sa, 0);if (!bRet){AfxMessageBox("创建管道失败!");}STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));si.cb = sizeof(si);si.dwFlags = STARTF_USESTDHANDLES;si.hStdInput = m_hChildRead;//子进程读数据的句柄si.hStdOutput = m_ChildWrite;//子进程写数据的句柄ZeroMemory(&pi, sizeof(pi));if (!CreateProcess(NULL,R"(G:\c_study\test_pipe\Chlid\Debug\Chlid.exe)",&sa,NULL,TRUE,0,NULL,NULL,&si,&pi)){AfxMessageBox("CreateProcess Failed");}CloseHandle(pi.hProcess);CloseHandle(pi.hThread);// TODO: 在此添加控件通知处理程序代码
}
  • 创建模块:设置安全属性,创建两条管道,创建child进程 允许继承
oid CParentDlg::OnBnClickedWrite()
{// TODO: 在此添加控件通知处理程序代码CString strBuf;GetDlgItemText(EDT_WRITE, strBuf);DWORD dwBytesWrited = 0;BOOL bRet = WriteFile(m_hParentWrite,strBuf.GetBuffer(),strBuf.GetLength() + 1,&dwBytesWrited,NULL);if (!bRet){AfxMessageBox("写入失败!");}}
  • 写入模块: 获取EDT内容,通过writeFile写入管道
void CParentDlg::OnBnClickedRead()
{DWORD dwAvailNum = 0;//管道剩余大小BOOL bRet = PeekNamedPipe(m_hParentRead, NULL, 0, NULL, &dwAvailNum, NULL);if (!bRet){return;}if (dwAvailNum > 0){CString strBuf;DWORD dwBytesReaded = 0;BOOL bRet = ReadFile(m_hParentRead,strBuf.GetBufferSetLength(dwAvailNum),dwAvailNum,&dwBytesReaded,NULL);strBuf.ReleaseBuffer(dwBytesReaded);if (!bRet){AfxMessageBox("读取失败!");}else{SetDlgItemText(EDT_READ, strBuf);}}else{AfxMessageBox("没有数据可读!");}// TODO: 在此添加控件通知处理程序代码
}
  • 读取模块: 判断 管道内是否有数据,通过读取管道 写入到 文本框中

子进程:

void CChlidDlg::OnBnClickedRead()
{HANDLE hChildRead = GetStdHandle(STD_INPUT_HANDLE);DWORD dwAvailNum = 0;//管道剩余大小BOOL bRet = PeekNamedPipe(hChildRead, NULL, 0, NULL, &dwAvailNum, NULL);if (!bRet){return;}if (dwAvailNum > 0){CString strBuf;DWORD dwBytesReaded = 0;BOOL bRet = ReadFile(hChildRead,strBuf.GetBufferSetLength(dwAvailNum),dwAvailNum,&dwBytesReaded,NULL);strBuf.ReleaseBuffer(dwBytesReaded);if (!bRet){AfxMessageBox("读取失败!");}else{SetDlgItemText(EDT_READ, strBuf);}}else{AfxMessageBox("没有数据可读!");}// TODO: 在此添加控件通知处理程序代码
}

void CChlidDlg::OnBnClickedWrite()
{HANDLE m_ChildWrite = GetStdHandle(STD_OUTPUT_HANDLE);CString strBuf;GetDlgItemText(EDT_WRITE, strBuf);DWORD dwBytesWrited = 0;BOOL bRet = WriteFile(m_ChildWrite,strBuf.GetBuffer(),strBuf.GetLength() + 1,&dwBytesWrited,NULL);if (!bRet){AfxMessageBox("写入失败!");}// TODO: 在此添加控件通知处理程序代码
}
  • 与父进程的读取管道,写入管道 基本一致,需要改改句柄

画面演示:
在这里插入图片描述

模拟CMD

在这里插入图片描述

  • 设置多行文本框、设置水平和纵向下拉框
void CParentDlg::OnBnClickedCreate()
{SECURITY_ATTRIBUTES sa = { };sa.nLength = sizeof(sa);sa.bInheritHandle = TRUE;BOOL bRet = CreatePipe(&m_hChildRead, &m_hParentWrite, &sa, 0);if (!bRet){AfxMessageBox("创建管道失败!");}bRet = CreatePipe(&m_hParentRead, &m_ChildWrite, &sa, 0);if (!bRet){AfxMessageBox("创建管道失败!");}STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));si.cb = sizeof(si);si.dwFlags = STARTF_USESTDHANDLES;si.hStdInput = m_hChildRead;//子进程读数据的句柄si.hStdOutput = m_ChildWrite;//子进程写数据的句柄ZeroMemory(&pi, sizeof(pi));if (!CreateProcess(NULL,"cmd.exe",&sa,NULL,TRUE,CREATE_NO_WINDOW,//无窗口打开NULL,NULL,&si,&pi)){AfxMessageBox("CreateProcess Failed");}CloseHandle(pi.hProcess);CloseHandle(pi.hThread);// TODO: 在此添加控件通知处理程序代码
}
  • 设置打开cmd程序,设置 无窗口打开程序
void CParentDlg::OnBnClickedWrite()
{// TODO: 在此添加控件通知处理程序代码CString strBuf;GetDlgItemText(EDT_WRITE, strBuf);strBuf += "\r\n";//换行DWORD dwBytesWrited = 0;BOOL bRet = WriteFile(m_hParentWrite,strBuf.GetBuffer(),strBuf.GetLength(),//将+1取消,不要读取\0&dwBytesWrited,NULL);if (!bRet){AfxMessageBox("写入失败!");}}
  • 写入文本换行,不要读取\0
void CParentDlg::OnBnClickedRead()
{DWORD dwAvailNum = 0;//管道剩余大小BOOL bRet = PeekNamedPipe(m_hParentRead, NULL, 0, NULL, &dwAvailNum, NULL);if (!bRet){return;}if (dwAvailNum > 0){CString strBuf;DWORD dwBytesReaded = 0;BOOL bRet = ReadFile(m_hParentRead,strBuf.GetBufferSetLength(dwAvailNum),dwAvailNum,&dwBytesReaded,NULL);strBuf.ReleaseBuffer(dwBytesReaded);if (!bRet){AfxMessageBox("读取失败!");}else{SetDlgItemText(EDT_READ, strBuf);}}else{AfxMessageBox("没有数据可读!");}// TODO: 在此添加控件通知处理程序代码
}
  • 和之前的代码逻辑一致

画面演示:
在这里插入图片描述

总结

  • 介绍了常用的进程通信方式:WM_COPYDATA、DLL共享段、文件映射、管道的相关知识

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19560.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于yolov8、yolov5的植物类别识别系统(含UI界面、训练好的模型、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

1+X应急响应(网络)系统信息收集分析:

系统信息收集分析&#xff1a; 系统启动项和计划任务分析&#xff1a; 系统进程&#xff0c;服务分析&#xff1a; 内存取证&#xff1a; 系统崩溃转储&#xff1a;

智慧环保平台_大数据平台_综合管理平台_信息化云平台

系统原理   智慧环保是新一代信息技术变革的产物&#xff0c;是信息资源日益成为重要生产要素和信息化向更高阶段发展的表现&#xff0c;是经济社会发展的新引擎。   现今&#xff0c;环保信息化建设进入高速发展阶段。在此轮由物联网掀起的信息浪潮下&#xff0c;环境信息…

如何通过电脑监控软件远程监控一台电脑的所有屏幕画面记录

7-1 本教程介绍一个简单的工具&#xff0c;可以安装在电脑中&#xff0c;按设置的时间间隔&#xff0c;自动对屏幕截图保存&#xff0c;并且可以在有网络的其它电脑上远程提取截图文件。 该软件用于自动记录电脑的屏幕画面内容和变化&#xff0c;如果你有这方面的使用场景&am…

深度解读混合专家模型(MoE):算法、演变与原理

假设一个专家团队共同解决复杂问题。每位专家都拥有独特的技能&#xff0c;团队通过高效分配任务实现了前所未有的成功。这就是混合专家&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;模型架构背后的基本思想&#xff0c;这种方法允许机器学习系统&#xff0c;特别…

电商微服务项目第一天(品牌管理)

1.BaseTrademarkController&#xff08;品牌管理CRUD&#xff09; /*** 添加品牌* param baseTrademark* return*/PostMapping("baseTrademark/save")public Result<BaseTrademark> save(RequestBody BaseTrademark baseTrademark){baseTrademarkService.save(…

初探Ranking系统的离在线满意度评估

【引子】在上周发布了《大模型应用系列&#xff1a;从Ranking到Reranking》之后&#xff0c; 有AI 产品经理问我&#xff0c;如何评估Ranking 系统的性能呢&#xff1f; 再进一步&#xff0c;如何评估RAG系统的性能呢&#xff1f; 老码农整理了一下在搜索引擎方面的感受&#x…

初识C++ (五)

没事干就学习 auto关键字 auto是C程序设计语言的关键字。自C11以来&#xff0c;auto关键字用于两种情况&#xff1a;声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。C98标准中auto关键字用于自动变量的声明&#xff0c;但由于使用极少且多余…

shell脚本判断nginx安装和运行

shell脚本判断nginx安装和运行 脚本内容&#xff1a; 传入服务名称&#xff1a; read -p "请输入要判断的程序名称:" service_name 查看服务进程&#xff1a; countps -aux | grep -cw $service_name 判断nginx是否安装&#xff08;系统中是否有nginx命令&#xff…

电脑msvcr100.dll丢失的解决方法,详细介绍多个解决方法

由于系统中关键文件msvcr100.dll的缺失&#xff0c;用户可能会遭遇一系列始料未及的困扰与问题。msvcr100.dll是Microsoft Visual C运行库中的一个核心动态链接库文件&#xff0c;对于许多应用程序的正常运行至关重要。当这个特定的dll文件丢失时&#xff0c;可能会导致部分软件…

Windows安装vcpkg教程(VS2022)

内容摘要&#xff1a; 本文详细介绍如何在Windows系统上使用 Git 克隆 vcpkg 仓库来安装vcpkg工具&#xff0c;并链接Visual Studio 2022。 目录 一、关于vcpkg 二、开发环境 三、安装Git 四、使用 Git 克隆 vcpkg 仓库 一、关于vcpkg vcpkg 是一个开源的 C 包管理工具&am…

TypeScript泛型基础知识

1.1 泛型 泛型是可以在保证类型安全的前提下&#xff0c;让函数等与多种类型一起工作&#xff0c;从而实现复用&#xff0c;常用于&#xff1a;函数、接口、class中。 需求&#xff1a;创建一个id函数&#xff0c;传入什么数据就返回该数据本身&#xff08;也就是说&#xff0c…

多线程的安全问题

什么是线程安全问题&#xff1f; 多个线程&#xff0c;访问同一资源&#xff0c;出现了问题&#xff0c;就是线程安全问题&#xff08;数据不准确&#xff0c;或者直接报错&#xff09; 1&#xff09;错误演示&#xff1a; public class Demo04 {static int tickedNum 100;/…

推荐一套相片复原工具:Focus Magic

Focus Magic是一套相片复原工具&#xff0c;能够帮助你修补及强化那些模糊不清楚的影像。其他那些锐利化工具只能够修补那些只有一点模糊的相片&#xff0c;但有了Focus Magic你就可以把那些根本完全没对准焦距的相片重新对准焦距。程序还可以以插件的形式作为其他图形处理工具…

C++:类的继承

在C中&#xff0c;类的继承是一种面向对象编程&#xff08;OOP&#xff09;的重要特性&#xff0c;它允许一个类&#xff08;子类或派生类&#xff09;继承另一个类&#xff08;基类或父类&#xff09;的属性和方法。通过继承&#xff0c;可以重用现有的代码&#xff0c;减少重…

详细解读Gson 、Jackson 、FastJson 三大json序列化工具

一 gson Google提供的用来java对象和JSON数据之间进行映射的JAVA类库 优缺点 优点&#xff1a;快速、高效&#xff0c; 依赖少只有一个jar包&#xff0c;面向对象&#xff0c;数据传输解析方便 缺点&#xff1a;速度较慢 mvn依赖 <dependency><groupId>com.go…

版块控制---git

引入 设想&#xff0c;当我们写论文时&#xff0c;对第一版不够满意时&#xff0c;想做出修改但是又怕修改时回毁掉整个论文版本&#xff0c;所以我们通常会进行备份&#xff0c;以防止数据被修改后的崩毁&#xff0c;版块控制就是这个合理创建管理备份的过程&#xff0c;而且这…

BLE 蓝牙客户端和服务器连接

蓝牙通信在设计小型智能设备时非常普遍&#xff0c;之前一直没有使用过&#xff0c;最近使用ardunio ESP32 做了一些实验&#xff0c;做了一个收听播客的智能旋钮&#xff08;Smart Knob&#xff09;&#xff0c;它带有一个旋转编码器和两个按键。 本文介绍BLE 服务器Server和W…

图(Graph)的概念和遍历

目录 定义 相关概念 无向图&#xff08;Undirected graphs) 有向图&#xff08;Directed graphs&#xff09; 完全图 稀疏图 稠密图 权&#xff08;Weight&#xff09; 网&#xff08;Network&#xff09; 子图&#xff08;Subgraph&#xff09; 图的顶点与边间关系 …

python成长技能之正则表达式

文章目录 一、认识正则表达式二、使用正则表达式匹配单一字符三、正则表达式之重复出现数量匹配四、使用正则表达式匹配字符集五、正则表达式之边界匹配六、正则表达式之组七、正则表达式之贪婪与非贪婪 一、认识正则表达式 什么是正则表达式 正则表达式&#xff08;英语&…