windows进程和线程

Windows进程

进程是一个容器,包含程序执行需要的代码、数据、资源等等信息。Windows是多任务操作系统,可以同时执行多个进程。

  • Windows进程的特点:

    • 每个进程都有自己的ID号
    • 每个进程都有自己的地址空间,进程之间无法访问对方的地址空间。
    • 每个进程都有自己的安全属性
    • 每个进程当中至少包含一个线程
  • 进程环境信息(进程上下文)

    • 获取和释放环境信息
      • 获取
        LPVOID GetEnvironmentStrings(VOID);
      • 释放
    BOOL FreeEnvironmentStrings(
    	  LPTSTR lpszEnvironmentBlock  
           // environment strings
    );
    
    • 获取和设置环境变量
      GetEnvironmentVariable
      SetEnvironmentVariable
  • 进程的信息
    1 进程ID
    GetCurrentProcessId
    2 进程句柄
    GetCurrentProcess返回进程的伪句柄(-1),可以使用该句柄访问该进程的所有操作。

  • 进程的使用

    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
    );
    
    1. 结束进程
    VOID ExitProcess(
    	UINT uExitCode   // exit code for all threads
    );	
    BOOL TerminateProcess(
    	 HANDLE hProcess, // handle to the process
    	 UINT uExitCode   // exit code for the process
    );
    
    1. 通过进程ID获取 进程句柄
    HANDLE OpenProcess(
    	DWORD dwDesiredAccess,  //访问权限
    	BOOL bInheritHandle,    //继承标识
    	DWORD dwProcessId       //进程ID
    ); 返回进程句柄
    
    1. 关闭进程句柄
      CloseHandle
    2. 进程间的等候
      等候 可等候的句柄 的信号
      DWORD WaitForSingleObject(
      HANDLE hHandle, //句柄
      DWORD dwMilliseconds //等候时间 INFINITE
      );
      阻塞函数,等候句柄的信号,只在句柄有信号或超出等候时间,才会结束等候。

Windows线程

Windows线程是可以执行的代码的实例。系统是以线程为单位调度程序。一个程序当中可以有多个线程,实现多任务的处理。

  • Windows线程的特点:

    • 线程都具有1个ID
    • 线程具有自己的安全属性
    • 每个线程都具有自己的内存栈
    • 每个线程都具有自己的寄存器信息
  • 进程多任务和线程多任务:

    • 进程多任务是每个进程都使用私有地址空间,
    • 线程多任务是进程内的多个线程使用同一个地址空间。
  • 线程的调度:
    将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。
    线程轮询:线程A -> 线程B -> 线程A......

  • 线程的使用

  1. 定义线程处理函数

    DWORD WINAPI ThreadProc(
      	LPVOID lpParameter //创建线程时,传递给线程的参数
    );
    
  2. 创建线程

    HANDLE CreateThread(
    	LPSECURITY_ATTRIBUTES lpThreadAttributes,//安全属性
    	SIZE_T dwStackSize,                       //线程栈的大小
    	LPTHREAD_START_ROUTINE lpStartAddress,    //线程处理函数的函数地址
    	LPVOID lpParameter,                       //传递给线程处理函数的参数
    	DWORD dwCreationFlags,//线程的创建方式,
    	LPDWORD lpThreadId                       //创建成功,返回线程的ID
    ); 创建成功,返回线程句柄
    
     dwCreationFlags:
    

    0 - 创建之后线程立刻执行
    CREATE_SUSPENDED - 创建之后线程处于挂起状态。

  3. 结束线程

    • 结束指定线程
    BOOL TerminateThread(
    	HANDLE hThread,    // handle to thread
    	DWORD dwExitCode   // exit code
    );
    
    • 结束函数所在的线程
    VOID ExitThread(
    	DWORD dwExitCode   // exit code for this thread
    );
    
  4. 关闭线程句柄
    CloseHandle

  5. 线程的挂起和执行

    • 挂起
    DWORD SuspendThread(
    	 HANDLE hThread   // handle to thread
    );
    
     * 执行
    
    DWORD ResumeThread(
    	HANDLE hThread   // handle to thread
    );
    
  6. 线程的信息
    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输出混乱,多线程情况下同时使用一段代码。
      临界区可以锁定一段代码,防止多个线程同时使用该段代码
      使用

      1. 初始化一个临界区
    VOID InitializeCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection  
      	//临界区变量
    );
    
      2. 进入临界区
    

    添加到被锁定的代码之前

    VOID EnterCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection  // critical section
    );
    
      3. 离开临界区
    

    添加到被锁定的代码之后

    VOID LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection   // critical section
    );
    
      4. 删除临近区
    
    VOID DeleteCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   
      	//临界区变量
    );
    
      * 原子锁和临界区
    

    原子锁 - 单条指令。
    临界区 - 单条或多行代码。

    • 互斥 Mutex
      相关的问题
      多线程下代码或资源的共享使用。
      • 互斥的使用
      1. 创建互斥
    HANDLE CreateMutex(
    	 LPSECURITY_ATTRIBUTES lpMutexAttributes,
    	   //安全属性
    	BOOL bInitialOwner,//初始的拥有者
    	 LPCTSTR lpName    //命名
    ); 创建成功返回互斥句柄
    
      	* bInitialOwner - 初始的拥有者
      		* TRUE  - 调用CreateMutex的线程拥有互斥
      		* FALSE - 创建的时没有线程拥有互斥
      2. 等候互斥
      WaitFor....
      互斥的等候遵循谁先等候谁先获取。
      3. 释放互斥
      BOOL ReleaseMutex(
      	HANDLE hMutex   // handle to mutex
      );
      4. 关闭互斥句柄
      CloseHandle
    
      * 互斥和临界区的区别
    
      	* 临界区 - 用户态,执行效率高,只能在同一个进程中使用。
      	* 互斥 - 内核态,执行效率低,可以通过命名的方式跨进程使用。
    
    • 事件
      相关问题
      程序之间的通知的问题。
      事件的使用
      1. 创建事件
    HANDLE CreateEvent(
    	LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性
    	BOOL bManualReset,                       
    		  	//事件复位方式,TRUE手动,FALSE自动
    	BOOL bInitialState,       //事件初始状态,TRUE有信号
    	LPCTSTR lpName  //事件命名
    ); 创建成功返回 事件句柄
    
      2. 等候事件
      `WaitForSingleObject/`
      `WaitForMultipleObjects`
      3. 触发事件
      	* 将事件设置成有信号状态
    
    BOOL SetEvent(
    	HANDLE hEvent   // handle to event
    );
    
      	* 将事件设置成无信号状态
    
    BOOL ResetEvent(
    	HANDLE hEvent   // handle to event
    );
    
      4. 关闭事件    CloseHandle
    
      小心事件的死锁。
    
    • 信号量
      相关的问题
      类似于事件,解决通知的相关问题。但是可以提供一个计数器,可以设置次数。
      信号量的使用
      1. 创建 信号量
    HANDLE CreateSemaphore(
      LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, 
      	//安全属性
      LONG lInitialCount,        //初始化信号量数量   
      LONG lMaximumCount, //信号量的最大值
      LPCTSTR lpName           //命名
    ); 创建成功返回信号量句柄
    
      2. 等候信号量
    

    WaitFor...
    每等候通过一次,信号量的信号减1,直到为0阻塞
    3. 释放信号量(重新给信号量设置计数值)

    BOOL ReleaseSemaphore(
      HANDLE hSemaphore, //信号量句柄
      LONG lReleaseCount, //释放数量
      LPLONG lpPreviousCount   
      	//释放前原来信号量的数量,可以为NULL
    );
    
      4. 关闭句柄
    

    CloseHandle

    可等候定时器

    • 等候函数
      • WaitForSingleObject - 等候单个
      • WaitForMultipleObjects - 等候多个
    DWORD WaitForMultipleObjects(
    	  DWORD nCount, //句柄数量
    	  CONST HANDLE *lpHandles,  //句柄BUFF的地址
    	  BOOL bWaitAll,//等候方式
    	  DWORD dwMilliseconds      // 等候时间 INFINITE
    );
    
      * bWaitAll - 等候方式
      	TRUE - 表示所有句柄都有信号,才结束等候
      	FASLE- 表示句柄中只要有1个有信号,就结束等候。
    
posted @ 2016-11-07 21:43  呉语伦比  阅读(154)  评论(0编辑  收藏  举报