进程相关函数汇总解释(超级详细)|操作系统

发布于 2021-07-10  47 次阅读


1、前言

这一篇博客是为了我其他关于多线程同步实验准备的,遇到不明白函数可以方便查阅,并附上函数相关链接。
虽然看着会很困,但是还是多看看

2、相关函数

在说明函数之前先把函数中参数类型先大致说明一下

参数类型解释

HANDLE类型:(可以说是最常见到了)
HANDLE
HANDLE(句柄)是Windows操作系统中的一个概念。在Windows程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时会为它们分配内存,并返回标示这些资源的标示号,即句柄。句柄指的是一个核心对象在某一个进程中的唯一索引,而不是指针。由于地址空间的限制,句柄所标识的内容对进程是不可见的,只能由操作系统通过进程句柄列表来进行维护。句柄列表:每个进程都要创建一个句柄列表,这些句柄指向各种系统资源,比如信号量,线程,和文件等,进程中的所有线程都可以访问这些资源。

LPCTSTR类型
LPCTSTR链接
L表示long指针 这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32位操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。 P表示这是一个指针 C表示是一个常量 T表示在Win32环境中, 有一个_T宏 STR表示这个变量是一个字符串 LPCTSTR就表示一个指向const对象的指针 (LPTSTR也差不多只是不是指向const对象的)

LPSECURITY_ATTRIBUTES类型
LPSECURITY_ATTRIBUTES链接
LPSECURITY_ATTRIBUTES,结构包含一个对象的安全描述符,并指定检索到指定这个结构的句柄是否是可继承的。

DWORD类型
DWORD链接
dword全称Double Word,每个word为2个字节的长度,是指计算机中数值的位数(4字节,32位)。

LPVOID类型
LPVOID
LPVOID是一个没有类型的指针,也就是说你可以将LPVOID类型的变量赋值给任意类型的指针,比如在参数传递时就可以把任意类型传递给一个LPVOID类型为参数的方法,然后在方法内再将这个“任意类型”从传递时的“LPVOID类型”转换回来。

LPSTARTUPINFO类型
LPSTARTUPINFO
用于指定新进程的主窗口特性的一个结构(也就是决定进程的窗体如何显示)

LPPROCESS_INFORMATION
LPPROCESS_INFORMATION
在创建进程时相关的数据结构之一,该结构返回有关新进程及其主线程的信息

size_t
size_t
size_t 类型定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版。它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。在64位系统中为long long unsigned int,非64位系统中为long unsigned int。

LPTHREAD_START_ROUTINE
LPTHREAD_START_ROUTINE
LPTHREAD_START_ROUTINE是一种函数指针,该函数指针指向一个函数,通知宿主某个线程已开始执行。

CRITICAL_SECTION
CRITICAL_SECTION
critical section是每个线程中访问临界资源的那段代码,不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。

例如
CRITICAL_SECTION CS_DATA;

函数部分

CreateProcess函数

CreateProcess函数 CreateProcess用来创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件。

BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATIONlpProcessInformation
);

2、函数参数解释

lpApplicationName: 指向一个NULL结尾的、用来指定可执行模块的字符串。可以简单理解为这里参数是一个可执行文件的路径,之后会调用这个路径上的可执行文件

lpCommandLine: 指向一个以NULL结尾的字符串,该字符串指定要执行的命令行。 这个参数可以为空,那么函数将使用lpApplicationName参数指定的字符串当做要运行的程序的命令行。 如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行。新运行的进程可以使用GetCommandLine函数获得整个命令行。C语言程序可以使用argc和argv参数,argc表示命令行参数个数,而argv为存储命令行参数的数组。 其实这个就是个命令行参数,参数可以为NULL,也可以为一个const char类型的值,允许你通过参数给程序传一些变量

lpProcessAttributes: 指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpProcessAttributes参数为NULL,则表示使用默认安全性,不可以被子进程继承 一般为NULL

lpThreadAttributes: 指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE 跟lpProcessAttributes一样,不过这个参数决定的是线程是否被继承.通常置为NULL

bInheritHandles: 指示新进程是否从调用进程处继承了句柄。 如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限。

dwCreationFlags: 指定附加的、用来控制优先类和进程的创建的标志。 有很多定义好的不同创建标志宏,不同的创建标志会给进程不同的创建方式和优先级区别。一般为NULL

lpEnvironment指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。 一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。每个字符串都是name=value的形式。 因为相等标志被当做分隔符,所以它不能被环境变量当做变量名。 与其使用应用程序提供的环境块,不如直接把这个参数设为空

lpCurrentDirectory: 指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录(也就是当前进程目录)。

lpStartupInfo: 指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。

lpProcessInformation: 指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。

CreateThread函数

CreateThread函数CreateThread链接 CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
)

函数参数解释

lpThreadAttributes: 指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE 跟lpProcessAttributes一样,不过这个参数决定的是线程是否被继承.通常置为NULL

dwStackSize:
设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。

lpStartAddress:
指向线程函数的指针,函数名称没有限制,

线程有两种声明方式
(1)DWORD WINAPI 函数名 (LPVOID lpParam); //标准格式

DWORD WINAPI 函数名 (LPVOID lpParam)
{
    return 0;
}
CreateThread(NULL, 0, 函数名, 0, 0, 0);
(2)void 函数名();
使用void 函数名()此种线程声明方式时,lpStartAddress需要加入LPTHREAD_START_ROUTINE转换,如

void 函数名()
{
    return;
}
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)函数名, 0, 0, 0);

lpParameter:
向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。

dwCreationFlags
指定附加的、用来控制优先类和进程的创建的标志。
有很多定义好的不同创建标志宏,不同的创建标志会给进程不同的创建方式和优先级区别。一般为NULL

lpThreadId:保存新线程的id。
返回值:函数成功,返回线程句柄;函数失败返回false。若不想返回线程ID,设置值为NULL。

CloseHandle函数

CloseHandle函数 :
关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。在CreateThread(这是创建线程函数,后面会有)成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
若在线程执行完之后,没有调用CloseHandle,在进程执行期间,将会造成内核对象的泄露,相当于句柄泄露,但不同于内存泄露,这势必会对系统的效率带来一定程度上的负面影响。但当进程结束退出后,系统会自动清理这些资源。

BOOL CloseHandle(
HANDLE hObject
);
参数
hObject :代表一个已打开对象句柄。
返回值
TRUE:执行成功;
FALSE:执行失败,可以调用GetLastError()获知失败原因

WaitForSingleObject函数

WaitForSingleObject函数 :
WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。

DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);

函数参数解释

hHandle: hHandle对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

dwMillisecondsdwMilliseconds为定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。 (我们常常会设为INFINTE,因为我们经常使用WaitForSingleObject函数为多线程里的P操作,起到一个上锁作用)

WaitForMultipleObjects函数

WaitForMultipleObjects函数
WaitForMultipleObjects是Windows中的一个功能非常强大的函数,几乎可以等待Windows中的所有的内核对象,简单说WaitForSingleObject函数可以等待单个线程 WaitForMultipleObjects可以等待一组线程程

DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);

函数参数解释

nCount
lpHandles指向的数组中的对象句柄数。对象句柄的最大数量为MAXIMUM_WAIT_OBJECTS。此参数不能为零。
lpHandles一组对象句柄。该数组可以包含不同类型对象的句柄。它可能不包含同一句柄的多个副本。
如果其中一个句柄在等待仍处于暂挂状态时关闭,则该函数的行为未定义。
句柄必须具有SYNCHRONIZE访问权限。
bWaitAll
如果此参数为TRUE,则在lpHandles数组中的所有对象的状态发出信号时,该函数返回。如果为FALSE,则当任何一个对象的状态设置为信号时,该函数返回。在后一种情况下,返回值表示其状态导致函数返回的对象。
dwMilliseconds
超时间隔,以毫秒为单位。如果指定了非零值,则该函数将一直等到指定的对象发出信号或经过间隔。如果dwMilliseconds为零,则如果未发出指示对象,则该函数不会进入等待状态;它总是立即返回。如果dwMilliseconds是INFINITE,则仅在发出指定对象信号时才返回该函数。

例如这里这句意思为等待MAX_THREAD个h_thread句柄对象(这里为线程)
全部发出信号,即全部线程都结束了,才停止挂起状态
WaitForMultipleObjects(MAX_THREAD, h_thread, TRUE, -1);

CreateMutex函数

CreateMutex函数
CreateMutex链接
CreateMutex是一个计算机函数,作用是找出当前系统是否已经存在指定进程的实例。如果没有则创建一个互斥体(我们用它作为互斥量进行上锁操作)

HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
BOOL bInitialOwner, // 初始化互斥对象的所有者
LPCTSTR lpName // 指向互斥对象名的指针
);

函数参数解释

lpMutexAttributes :指定一个SECURITY_ATTRIBUTES结构,或传递零值(将参数声明为ByVal As Long,并传递零值),表示使用不允许继承的默认描述符
bInitialOwner ,如创建进程希望立即拥有互斥体,则设为TRUE。一个互斥体同时只能由一个线程拥有
lpName,指定互斥体对象的名字。用vbNullString创建一个未命名的互斥体对象。如已经存在拥有这个名字的一个事件,则打开现有的已命名互斥体。这个名字可能不与现有的事件、信号机、可等待计时器或文件映射相符

OpenMutex函数

CreateMutex函数
OpenMutex函数
OpenMutex函数为现有的一个已命名互斥体对象创建一个新句柄。
一般在多线程同步里不太会用到

HANDLE OpenMutex(
DWORD dwDesiredAccess, // access
BOOL bInheritHandle, // inheritance option
LPCTSTR lpName // object name
);

函数参数解释

dwDesiredAccess:
MUTEX_ALL_ACCESS 请求对互斥体的完全访问
MUTEX_MODIFY_STATE 允许使用 ReleaseMutex 函数
SYNCHRONIZE 允许互斥体对象同步使用
bInheritHandle : 如希望子进程能够继承句柄,则为TRUE
lpName :要打开对象的名字

返回值:如执行成功,返回对象的句柄;零表示失败。若想获得更多错误信息,请调用GetLastError函数。

ReleaseMutex函数

ReleaseMutex函数ReleaseMutex函数 ReleaseMutex函数的功能是释放互斥对象的控制权 一个线程释放了互斥对象的控制权后,如果其他进程在等待互斥对象置位,则等待的线程可以得到该互斥对象,等待函数返回,互斥对象被新的线程所拥有。(我们用它来解除上锁,在线程控制中CreateMutex函数和ReleaseMutex函数是一对PV操作)

BOOL WINAPI ReleaseMutex(
HANDLE hMutex//hMutex:HANDLE,制定一个互斥体的句柄。
);

CreateSemaphore函数

CreateSemaphore函数CreateSemaphore函数 创建或打开命名或未命名的信号量对象。 Semaphore是另一个同步问题机制,不论是Event或Mutex,其他Process在执行WaitForSingleObject时,就看当时的对象是Signal或UnSignal而决定是否等待,而Semaphore也相同,但是它要变成Signal /UnSignal的状态,却有些不同,它是提供一个计数值,它允许在这个计数值之内,任何执行到WaitForSingleObject的Thread都不会停下来,而且每执行WaitForSingleObject一次,计数值就减一,当计数值变成0时,该Semaphore才会处於UnSignal的状态,而某个Thread ReleaseSemaphore时,便会将计数值增加,以便其他的Thread或本身可得Signal的讯号,而使WaitForSingleObject停止等待(简单来说,用互斥量Mutex等实现WaitForSingleObject等待是一个开关,而用信号量Semaphore是一个范围,只有在0还有最大值情况才会触发开关,也就是等待挂起或者进行,具体例子下面介绍)。

HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, 
    LONG lInitialCount, 
    LONG lMaximumCount, 
    LPCT STRlpName
);

函数参数解释

lpSemaphoreAttributes 指定一个SECURITY_ATTRIBUTES结构,或传递零值(将参数声明为ByVal As Long,并传递零值)——表示采用不允许继承的默认描述符。该参数定义了信号量的安全特性 lInitialCount 设置信号量的初始计数。可设置零到lMaximumCount之间的一个值(初始值) lMaximumCount 设置信号量的最大计数(最大值) lpName 指定信号量对象的名称。用vbNullString可创建一个未命名的信号量对象。如果已经存在拥有这个名字的一个信号量,就直接打开现成的信号量。这个名字可能不与一个现有的互斥体、事件、可等待计时器或文件映射的名称相符

具体例子

HANDLE Semaphore = CreateSemaphore(NULL, 0, 10, NULL);
WaitForSingleObject(Semaphore, INFINITE);
我们创建了一个句柄Semaphore来作为信号量,
其中他初始值为0,最大值为10,当我们WaitForSingleObject
一般mutex情况下只有第一个线程会进入挂起,
之后的得等解锁才可以继续,而信号量这里会有一个计数,
每次WaitForSingleObject时候,初始值加1,如果达到最大值了才会上锁
这样就给了我们一个0~10 的范围,具体应用呢,
比如我们在多线程里的消费者生产者问题中,
物品有很多件,这样的话如果用互斥量就不行了,
可以选择信号量设置范围,当物品满了等待不再填入,
或者物品空了不能再购买,这些用信号量机制都能轻易解决

ReleaseSemaphore函数

ReleaseSemaphore函数ReleaseSemaphore函数 与 WaitForSingleObject函数对应,WaitForSingleObject函数会对指定信号量减1,ReleaseSemaphore函数用于对指定的信号量增加指定的值。(与互斥量机制类型,可以实现PV操作)

BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);

函数参数解释

hSemaphore 所要操作的信号量对象的句柄,这个句柄是CreateSemaphore或者OpenSemaphore函数的返回值。这个句柄必须有SEMAPHORE_MODIFY_STATE 的权限。 lReleaseCount [输入参数]这个信号量对象在当前基础上所要增加的值,这个值必须大于0,如果信号量加上这个值会导致信号量的当前值大于信号量创建时指定的最大值,那么这个信号量的当前值不变,同时这个函数返回FALSE; lpPreviousCount [输出参数]指向返回信号量上次值的变量的指针,如果不需要信号量上次的值,那么这个参数可以设置为NULL;返回值:如果成功返回TRUE,如果失败返回FALSE,可以调用GetLastError函数得到详细出错信息;

ResumeThread函数

ResumeThread函数ResumeThread函数 使线程的挂起时间计数减一。创建一个挂起的线程或者手动挂起一个线程后调用。调用该函数后线程不一定会立刻执行,而是由操作系统继续调度,直到计数为0,系统为其分配资源时才开始执行。

DWORD WINAPI ResumeThread(_In_ HANDLE hThread);
hthread是要重新启动的线程的句柄。
此句柄必须具有线程“挂起”恢复访问权限。

注意事项
resumeThread函数检查主题线程的挂起计数。如果挂起计数为零,则线程当前未挂起。否则,主题线程的挂起计数将减少。如果结果值为零,则继续执行主题线程。
如果返回值为零,则指定的线程没有挂起。如果返回值为1,则指定的线程已挂起,但已重新启动。如果返回值大于1,则指定的线程仍将挂起。
请注意,在报告调试事件时,报告进程中的所有线程都将被冻结。调试程序需要使用suspendthread和resumethread函数来限制可以在进程内执行的线程集。通过挂起进程中除报告调试事件的线程外的所有线程,可以“单步”处理单个线程。如果挂起其他线程,则继续操作不会释放这些线程。

InitializeCriticalSection函数

InitializeCriticalSection函数InitializeCriticalSection函数 InitializeCriticalSection函数用来初始化一个临界资源对象。“临界区”CCriticalSection 是临界资源对象指针,该函数无返回值。单进程的各个线程可以使用临界资源对象来解决同步互斥问题,该对象不能保证哪个线程能够获得到临界资源对象,该系统能公平的对待每一个线程。

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection )

lpCriticalSection 临界资源对象指针。

实例

InitializeCriticalSection(&CS_DATA);
InitializeCriticalSection()的初始化后才能使用,
而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下

EnterCriticalSection函数和LeaveCriticalSection函数

EnterCriticalSection函数和LeaveCriticalSection函数EnterCriticalSection函数和LeaveCriticalSection函数 多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。

函数 EnterCriticalSection 和 LeaveCriticalSection 声明如下:
void WINAPI
EnterCriticalSection(
_inout LPCRITICAL_SECTION lpCriticalSection
);
void WINAPI LeaveCriticalSection( 
_Inout_LPCRITICAL_SECTION lpCriticalSection
);
是多线程中用来确保同一时刻只有一个线程操作被保护的数据的操作函数,相关的多线程数据操作函数还有:

InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区
//操作数据
MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter.. Leave...
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区

3、多线程同步,进程互斥相关的博客

下面会附上链接,都是自己写的可以运行的c++程序,大家可以一遍试一下一边看

1、哲学家进餐
2、兔子吃草同步算法
3、生产者消费者同步问题
4、理发师问题
5、写者优先的读者写者算法
6、读者优先的读者写者算法
7、进程互斥