首先,我们应该知道,windows调度的单位是线程而不是进程!
进程的生死周期:
当然,你也可以编写一个程序,使用CreateProcess函数创建一个新的进程去调用其他的程序。
BOOL CreateProcess(
LPCTSTR lpApplicationName, // pointer to name of executable module
LPTSTR lpCommandLine, // pointer to command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // process security attributes
LPSECURITY_ATTRIBUTES lpThreadAttributes, // thread security attributes
BOOL bInheritHandles, // handle inheritance flag
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // pointer to new environment block
LPCTSTR lpCurrentDirectory, // pointer to current directory name
LPSTARTUPINFO lpStartupInfo, // pointer to STARTUPINFO
LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION
);
以下是《深入浅出MFC中的参数解析》,详细的可查MSDN
按理来说,shell创建了一个新进程,那么他就是shell的子进程了,然而实际上,shell在创建了一个新进程之后,它就切断了该进程与自己的父子关系,如下使用CloseHandle就可以实现上面的操作:
线程的生死周期:
我们使用CreateThread函数创建一个线程在调用进程的地址空间。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程的安全属性
DWORD dwStackSize, // 初始化线程的栈大小
LPTHREAD_START_ROUTINE lpStartAddress, // 线程需要处理的函数(content)
LPVOID lpParameter, // 线程处理函数的参数
DWORD dwCreationFlags, // 线程创建的附加标识
LPDWORD lpThreadId // 返回线程的id
);
下面是MSDN里面的参数解析
Parameters
lpThreadAttributes
指向一个SECURITY_ATTRIBUTES 结构的 that确定返回的句柄是否可以由子进程继承。如果被子进程是null,手柄不能被继承。
Windows NT: 该结构的lpSecurityDescriptor成员指定为新的线程安全描述符。如果被子进程是空的,线程获取默认安全描述符.
dwStackSize
指定堆栈的初始提交大小,以字节为单位。系统将该值旋转到最近的页面。如果该值为零,或者小于默认提交大小,则默认使用与调用线程相同的大小。有关更多信息,请参见线程堆栈大小.
lpStartAddress
应用程序定义的函数指针类型LPTHREAD_START_ROUTINE被线程执行的线程的起始地址。在线程函数的更多信息。
lpParameter
指定传递给线程的单个32位参数值。
dwCreationFlags
指定控制线程创建的附加标志。如果create_suspended标志指定, 线程处于挂起状态了,也不会跑到resumethread函数被调用。如果此值为零,则线程在创建后立即运行。此时,没有其他值被支持。
lpThreadId
指向接收线程标识符的32位变量的指针。.
Windows NT:如果此参数为空,则不返回线程标识符。
Windows 95和Windows 98:此参数可能不是null。
返回值:
如果函数创建成功,则返回该线程句柄,如果失败,返回NULL。错误信息调取函数GetLastError。
与此同时,我们不能忘了C runtime函数库里面也有一个创建线程的方法:_beginthreadex
uintptr_t _beginthread( // NATIVE CODE
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthread( // MANAGED CODE
void( __clrcall *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthreadex( // NATIVE CODE
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
uintptr_t _beginthreadex( // MANAGED CODE
void *security,
unsigned stack_size,
unsigned ( __clrcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
参数
start_address
启动开始执行新线程的例程的地址。 对于 _beginthread,调用约定是 __cdecl (针对本机代码)或 __clrcall (针对托管代码);对于_beginthreadex,它是 __stdcall (针对本机代码)或 __clrcall (针对托管代码)。
stack_size
新线程的堆栈大小或 0。
arglist
要传递到新线程的参数列表或 NULL。
Security
指向 SECURITY_ATTRIBUTES 结构的指针,此结构确定返回的句柄是否由子进程继承。 如果 Security 为 NULL,则不能继承句柄。 对于 Windows 95 应用程序,必须为 NULL。
initflag
控制新线程的初始状态的标志。 将 initflag 设置为 0 以立即运行,或设置为 CREATE_SUSPENDED 以在挂起状态下创建线程;使用 ResumeThread来执行此线程。 将 initflag 设置为 STACK_SIZE_PARAM_IS_A_RESERVATION 标志以将 stack_size 用作堆栈的初始保留大小(以字节计);如果未指定此标志, stack_size 将指定提交大小。
thrdaddr
指向接收线程标识符的 32 位变量。 如果此变量为 NULL,则不可用。
返回值
如果成功,则这些函数中的每一个都会返回一个句柄到新创建的线程;但是,如果新创建的线程退出过快,则 _beginthread 可能不会返回有效句柄。 (请参见“备注”节中的讨论。)发生错误时,_beginthread 返回 -1L,并在线程过多的情况下将 errno 设置为 EAGAIN;如果参数无效或堆栈大小错误,则设置为 EINVAL;如果资源(如内存)不足,则设置为 EACCES。 发生错误时, _beginthreadex 返回 0 并设置 errno 和 _doserrno 。
如果 startaddress 为 NULL,则会调用无效的参数处理程序,如 Parameter Validation所述。 如果允许执行继续,则这些功能将 errno 设置为EINVAL 并返回 -1。