前言
微软鼓励大家多用线程,而弱化子进程的使用,所以和unix不同,windows下进程的通信很少,内存映射文件是一种。
进程
-
进程环境信息(进程上下文)
每个进程启动时系统都会生成相关的环境表,获取里面的环境变量可使用下面函数
GetEnvironmentStrings FreeEnvironmentStrings 可以获得该环境表的首地址
SetEnvironmentVariable GetEnvironmentVariable 可以设置和得到环境变量的值 -
获取进程ID和进程句柄
GetCurrentProcessId 获取id
GetCurrentProcess 获得句柄,但是一个假句柄,值为-1,可正常使用
OpenProcess 通过id获得进程句柄,通常拿来获得别的进程的句柄,也可调用OpenProcess(GetCurrentProcessId)获得本进程真实句柄 -
创建和结束进程
BOOL CreateProcess(
LPCTSTR lpApplicationName,应用程序全路径名
LPTSTR lpCommandLine, // 命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 安全属性 设为NULL
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性NULL
BOOL bInheritHandles, // 继承标识 NULL
DWORD dwCreationFlags, // 创建方式 给0立即启动
LPVOID lpEnvironment, // 设置环境信息 可为NULL
LPCTSTR lpCurrentDirectory, // 设置工作目录 可为NULL
LPSTARTUPINFO lpStartupInfo, // 保存起始信息 需放入结构体地址
LPPROCESS_INFORMATION lpProcessInformation // 创建的进程相关信息,需放入结构体地址
);
ExitProcess 可以结束当前进程
TerminateProcess 可以结束别的进程
CloseHandle 将句柄置为 -1,进程不结束-
进程间的等候,即阻塞函数等待别的进程返回
DWORD WaitForSingleObject(
HANDLE hHandle, // 进程句柄
DWORD dwMilliseconds // 等待时间(INFINITE为无限长)
);
DWORD WaitForMultipleObjects(
DWORD nCount, // 句柄数量
CONST HANDLE *lpHandles, // 存放句柄的数组
BOOL fWaitAll, // 等候方式TRUE等待所有 FALSE任何一个
DWORD dwMilliseconds // 等待时间(INFINITE为无限长)
);
这两个等候的函数一个可以等待单个进程,一个可以等候多个,里头的HANDLE其实可以不光只是进程,也可以是线程,只要是带信号的句柄,这些句柄包括
Change notification
Console input 控制台输入
Event 事件
Job
Mutex 互斥
Process 进程
Semaphore 信号
Thread 线程
Waitable timer 可等候定时器
。
-
进程间的等候,即阻塞函数等待别的进程返回
线程
进程如果对应电脑的内存,线程对应着电脑的CPU,线程也就是执行的代码的实例。
系统是以线程为单位调度程序。
线程的调度: 将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。
– 创建
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性
DWORD dwStackSize, // 线程栈大小为0向1M靠拢
LPTHREAD_START_ROUTINE lpStartAddress, // 线程处理函数
LPVOID lpParameter, // 处理函数参数
DWORD dwCreationFlags, //创建方式 挂起还是执行
LPDWORD lpThreadId // 接收的线程ID
);
– 结束 TerminateThread / ExitThread
结束后依旧需要调用CloseHandle释放句柄资源
– 挂起/执行 SuspendThread ResumeThread
– 线程信息 GetCurrentThreadId / GetCurrentThread
线程同步
线程同步技术是解决线程之间的资源竞争和线程之间的协调工作。
其中,原子锁、临界区(段)、互斥 是给线程间加锁,
-
原子锁
直接对一个数据的内存操作,任一时间只有一个线程访问该内存,可对这个数据进行增,减,改变数值
InterlockedIncrement, InterlockedExchange, InterlockedDecrement -
临界区
可以锁定一段代码,防止多个线程同时使用该段代码
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // 临界区结构体的地址
); //初始化一个临界区
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
); //进入临界区,放在要锁定的代码前
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection //
); //离开临界区,放在锁定的代码结尾
VOID DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
); //释放临界区资源 -
互斥
用于多线程下代码或资源的共享使用
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
// 安全属性
BOOL bInitialOwner, // 初始拥有着,TRUE立刻获得互斥锁
LPCTSTR lpName // 互斥名
); //返回互斥句柄
一个进程调用WaitForSingleObject时,如果持锁,则无需等待,否则等待持锁线程调用ReleaseMutext时立刻获得互斥锁
CloseHandle 可以释放互斥锁句柄资源,OpenMutex可以根据互斥名获得互斥锁句柄,也可以使用在两个进程间的通信。注: 互斥和临界区的区别,
临界区 – 运行在用户态,执行效率高,只能在同一个进程中使用
互斥 – 运行在内核态,执行效率低,可以通过命名方式跨进程使用(配合内存映射文件) -
事件
事件用于程序之间的通知的问题,是最常用的线程同步技术,事件句柄也分为有信号和无信号两种状态,无信号时会被WaitFor..函数等待,而SetEvent和ResetEvent可以手动设置事件句柄的信号状态
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
// 安全属性
BOOL bManualReset, // 手动还是自动复位
BOOL bInitialState, // 初始状态(有无信号)
LPCTSTR lpName // 事件名字
);
另一进程调用WaitFor..函数等待时间句柄变为有信号状态,如果是自动复位,会在调用结束后信号立马变成无信号状态,如果是手动,需要在调用结束后使用ResetEvent将信号置为无信号状态
不使用以后使用CloseHandle关闭事件句柄 -
信号量
类似于事件,解决通知的相关问题,可以提供一个计数器,设置信号有效的次数
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
// 安全属性
LONG lInitialCount, // 初始计数值
LONG lMaximumCount, // 最大值
LPCTSTR lpName // 信号量名字
);
当一个线程调用WaitFor..函数会使信号量计数值减1,直到该值为0这个信号量会变为无信号状态。使用ReleaseSemaphore可以重置信号量的计数值。使用结束后依旧使用CloseHandle释放资源