Windows进程
进程是一个容器,包含程序执行需要的代码、数据、资源等等信息。Windows是多任务操作系统,可以同时执行多个进程。
- Windows进程的特点:
- 每个进程都有自己的ID号
- 每个进程都有自己的地址空间,进程之间无法访问对方的地址空间。
- 每个进程都有自己的安全属性
- 每个进程当中至少包含一个线程
- 进程环境信息(进程上下文)
- 获取和释放环境信息
- 获取
LPVOID GetEnvironmentStrings(VOID);
- 释放
BOOL FreeEnvironmentStrings( LPTSTR lpszEnvironmentBlock // environment strings );
- 获取
- 获取和设置环境变量
GetEnvironmentVariable
SetEnvironmentVariable
- 获取和释放环境信息
- 进程的信息
1 进程ID
GetCurrentProcessId
2 进程句柄
GetCurrentProcess返回进程的伪句柄(-1),可以使用该句柄访问该进程的所有操作。 - 进程的使用
- 创建进程
WinExec - 早期16位
ShellExecute - Shell操作
CreateProcess - 目前最多使用
BOOL CreateProcess( LPCTSTR lpApplicationName,//应用程序名称 LPTSTR lpCommandLine, //命令行参数 LPSECURITY_ATTRIBUTES lpProcessAttributes, //进程安全属性 SD LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性 SD BOOL bInheritHandles, //进程的句柄继承 DWORD dwCreationFlags, //创建方式 LPVOID lpEnvironment, //环境信息 LPCTSTR lpCurrentDirectory,//当前目录 LPSTARTUPINFO lpStartupInfo, //起始信息 LPPROCESS_INFORMATION lpProcessInformation //返回进程和线程的句柄ID );
- 结束进程
VOID ExitProcess( UINT uExitCode // exit code for all threads ); BOOL TerminateProcess( HANDLE hProcess, // handle to the process UINT uExitCode // exit code for the process );
- 通过进程ID获取 进程句柄
HANDLE OpenProcess( DWORD dwDesiredAccess, //访问权限 BOOL bInheritHandle, //继承标识 DWORD dwProcessId //进程ID ); 返回进程句柄
- 关闭进程句柄
CloseHandle - 进程间的等候
等候 可等候的句柄 的信号
DWORD WaitForSingleObject(
HANDLE hHandle, //句柄
DWORD dwMilliseconds //等候时间 INFINITE
);
阻塞函数,等候句柄的信号,只在句柄有信号或超出等候时间,才会结束等候。
- 创建进程
Windows线程
Windows线程是可以执行的代码的实例。系统是以线程为单位调度程序。一个程序当中可以有多个线程,实现多任务的处理。
- Windows线程的特点:
- 线程都具有1个ID
- 线程具有自己的安全属性
- 每个线程都具有自己的内存栈
- 每个线程都具有自己的寄存器信息
- 进程多任务和线程多任务:
- 进程多任务是每个进程都使用私有地址空间,
- 线程多任务是进程内的多个线程使用同一个地址空间。
线程的调度:
将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。
线程轮询:线程A -> 线程B -> 线程A......- 线程的使用
- 定义线程处理函数
DWORD WINAPI ThreadProc( LPVOID lpParameter //创建线程时,传递给线程的参数 );
- 创建线程
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,//安全属性 SIZE_T dwStackSize, //线程栈的大小 LPTHREAD_START_ROUTINE lpStartAddress, //线程处理函数的函数地址 LPVOID lpParameter, //传递给线程处理函数的参数 DWORD dwCreationFlags,//线程的创建方式, LPDWORD lpThreadId //创建成功,返回线程的ID ); 创建成功,返回线程句柄
dwCreationFlags:
0 - 创建之后线程立刻执行
CREATE_SUSPENDED - 创建之后线程处于挂起状态。 - 结束线程
- 结束指定线程
BOOL TerminateThread( HANDLE hThread, // handle to thread DWORD dwExitCode // exit code );
- 结束函数所在的线程
VOID ExitThread( DWORD dwExitCode // exit code for this thread );
- 关闭线程句柄
CloseHandle - 线程的挂起和执行
- 挂起
DWORD SuspendThread( HANDLE hThread // handle to thread );
* 执行
DWORD ResumeThread( HANDLE hThread // handle to thread );
- 线程的信息
GetCurrentThreadId - 获取当前线程的ID
GetCurrentThread - 获取当前线程的句柄
打开指定ID的线程,获取其句柄HANDLE OpenThread( DWORD dwDesiredAccess, // access right BOOL bInheritHandle, // handle inheritance option DWORD dwThreadId // thread identifier );
多线程的问题
线程A -> 线程B -> 线程A 。。。。。
当线程A执行printf输出时,如果线程A的执行时间结束,系统会将线程A的相关信息(栈、寄存器)压栈保护,同时将线程B相关信息恢复,然后执行线程B,线程B继续输出字符。由于线程A正输出字符,线程B会继续输出,画面字符会产生混乱。
- 线程同步技术
- 原子锁
- 原子锁概念
相关问题
多个线程对同一个数据进行原子操作,会产生结果
丢失。比如执行++运算时,
当线程A执行g_nValue++时,如果线程切换时间
正好是在线程A将值保存到g_nValue之前,
线程B继续执行g_nValue++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_nValue上,线程B进行的加法操作被覆盖。 - 原子锁的使用
原子锁-对单条指令的操作。
API
InterlockedIncrement
InterlockedDecrement
InterlockedCompareExchange
InterlockedExchange
...
原子锁的实现:
直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问。
- 原子锁概念
- 临界区(段)
相关问题
printf输出混乱,多线程情况下同时使用一段代码。
临界区可以锁定一段代码,防止多个线程同时使用该段代码
使用- 初始化一个临界区
VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection //临界区变量 );
- 进入临界区
添加到被锁定的代码之前
VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section );
- 离开临界区
添加到被锁定的代码之后
VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section );
- 删除临近区
VOID DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection //临界区变量 );
- 原子锁和临界区
原子锁 - 单条指令。
临界区 - 单条或多行代码。
- 互斥 Mutex
相关的问题
多线程下代码或资源的共享使用。- 互斥的使用
- 创建互斥
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性 BOOL bInitialOwner,//初始的拥有者 LPCTSTR lpName //命名 ); 创建成功返回互斥句柄
* bInitialOwner - 初始的拥有者 * TRUE - 调用CreateMutex的线程拥有互斥 * FALSE - 创建的时没有线程拥有互斥
- 等候互斥
WaitFor....
互斥的等候遵循谁先等候谁先获取。 - 释放互斥
BOOL ReleaseMutex(
HANDLE hMutex // handle to mutex
); - 关闭互斥句柄
CloseHandle
互斥和临界区的区别
- 临界区 - 用户态,执行效率高,只能在同一个进程中使用。
- 互斥 - 内核态,执行效率低,可以通过命名的方式跨进程使用。
- 事件
相关问题
程序之间的通知的问题。
事件的使用- 创建事件
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性 BOOL bManualReset, //事件复位方式,TRUE手动,FALSE自动 BOOL bInitialState, //事件初始状态,TRUE有信号 LPCTSTR lpName //事件命名 ); 创建成功返回 事件句柄
- 等候事件
WaitForSingleObject/
WaitForMultipleObjects
- 触发事件
- 将事件设置成有信号状态
BOOL SetEvent( HANDLE hEvent // handle to event );
- 将事件设置成无信号状态
BOOL ResetEvent( HANDLE hEvent // handle to event );
- 关闭事件 CloseHandle
小心事件的死锁。
- 信号量
相关的问题
类似于事件,解决通知的相关问题。但是可以提供一个计数器,可以设置次数。
信号量的使用- 创建 信号量
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //安全属性 LONG lInitialCount, //初始化信号量数量 LONG lMaximumCount, //信号量的最大值 LPCTSTR lpName //命名 ); 创建成功返回信号量句柄
- 等候信号量
WaitFor...
每等候通过一次,信号量的信号减1,直到为0阻塞 - 释放信号量(重新给信号量设置计数值)
BOOL ReleaseSemaphore( HANDLE hSemaphore, //信号量句柄 LONG lReleaseCount, //释放数量 LPLONG lpPreviousCount //释放前原来信号量的数量,可以为NULL );
- 关闭句柄
CloseHandle
可等候定时器
- 等候函数
- WaitForSingleObject - 等候单个
- WaitForMultipleObjects - 等候多个
DWORD WaitForMultipleObjects( DWORD nCount, //句柄数量 CONST HANDLE *lpHandles, //句柄BUFF的地址 BOOL bWaitAll,//等候方式 DWORD dwMilliseconds // 等候时间 INFINITE );
- bWaitAll - 等候方式
TRUE - 表示所有句柄都有信号,才结束等候
FASLE- 表示句柄中只要有1个有信号,就结束等候。
- 原子锁