Windows核心编程--线程

-线程组成
1.线程内核对象。
2.线程栈,用于维护线程执行时所需的所有函数参数和局部变量。

同一进程内线程共享属于进程的资源。

-线程函数
每个线程都须有一个入口点函数,这是线程执行的起点。
主线程的入口点函数:_tmain或_tWinMain。
辅助线程的入口点函数形式:

DWORD WINAPI ThreadFunc(PVOID pvParam)
{

    DWORD dwResult = 0;
    ...
    return dwResult;
}

线程终止运行时,用于线程栈的内存也会被释放,线程内核对象的使用计数也会递减。如果使用计数变为0,线程内核对象会被销毁。
注意:
1.默认下,主线程的入口点函数须命名为main, wmain, WinMain 或 wWinMain。(用/ENTRY:链接器选项可指定一个入口点函数)普通线程函数命名有使用者决定。
2.线程函数必须返回一个值,它会成为该线程的退出代码。
3.函数参数和局部变量,在线程栈上创建。静态变量和全局变量在进程地址空间。

-创建线程

HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
// 默认下,采用链接器指示的 空间和调拨的量
// /STACK:[reserve] [,commit]
// >0,预定+挑拨所有指定空间。实际预定取两者中较大的
DWORD cbStackSize,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
// 0 立即开始
// CREATE_SUSPENDED 暂停
DWORD dwCreateFlags,
PDWORD pdwThreadID
);

写 C/C++代码时,创建线程要使用编译器提供的创建线程函数,后者在内部会调用CreateThread。Microsoft C++编译器中用 _beginthreadex。

-终止线程
1.线程函数返回。
可保证:
线程函数中创建的C++对象,通过其析构函数被销毁。
操作系统正确释放线程栈的内存。
操作系统把线程的退出代码设为线程函数的返回值。
系统递减线程内核对象的使用计数。

2.线程用ExitThread杀死自己。

VOID ExitThread(DWORD dwExitCode);

可保证:
清理操作系统资源。
但:
线程函数中创建的C/C++对象,不会被销毁。

应使用编译器提供的版本,Microsoft C++编译器为_endthreadex。

3.其它线程用TerminateThread。

// 异步调用:立即返回,返回时,可能线程还未终止。
BOOL TerminateThread(
HANDLE hThread,
DWORD dwExitCode
);

被终止运行的线程不会收到通知。

可保证:
操作系统把线程的退出代码设为线程函数的返回值。
系统递减线程内核对象的使用计数。
但不会:
线程函数中创建的C++对象,通过其析构函数被销毁。
操作系统正确释放线程栈的内存。

4.包含线程的进程终止。

-线程终止运行时
1.线程拥有的所有用户对象句柄会被释放。
大多数对象由进程拥有。
但窗口和挂钩属于线程拥有。
线程终止时,系统自动销毁线程创建或安装的任何窗口,并卸载由线程创建或安装的任何挂钩。
2.线程的退出代码从STILL_ACTIVE变为传递的退出代码。
3.线程内核对象变为触发状态。
4.如是进程中最后一个活动线程,则,系统认为进程也终止了。
5.线程内核对象使用计数减1。

// 获得线程退出代码
BOOL GetExitCodeThread(
HANDLE hThread,
PDWORD pdwExitCode
);

-线程内幕
1.一旦创建了线程内核对象,系统就分配内存,供线程的堆栈使用。
2.系统将传给CreateThread的pvParam和pfnStartAddr分别写入线程堆栈。
3.每个线程有其自己的一组CPU寄存器,称为线程的上下文。上下文反映了当线程上一次执行时,线程的CPU寄存器的状态。线程的CPU寄存器全部保存在一个CONTEXT结构。CONTEXT结构本身保存在线程内核对象中。
4.
指令指针寄存器
栈指针寄存器
是线程上下文中最重要的两个寄存器。
线程内核对象被初始化时,堆栈指针寄存器被设为pfnStartAddr在线程堆栈的地址。指令指针寄存器被设为RtlUserThreadStart函数的地址。

VOID RtlUserThreadStart(
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam
)
{
    __try
    {
        ExitThread((pfnStartAddr)(pvParam));
    }
    __except(UnhandledExceptionFilter(GetExceptionInformation()))
    {
        ExitProcess(GetExceptionCode());
    }
}

-C/C++运行库注意

库名称描述
LibCMt.lib库的静态链接发行版
LibCMtd.lib库的静态链接调试版
MSVCRt.lib导入库,动态链接MSVCR80.dll
MSVCRt.lib导入库,动态链接MSVCR80D.dll
MSVCMRt.lib导入库,托管/本机代码混合
MSVCURt.lib导入库,编译成纯MSIL代码

1.创建新线程时,用编译器的。
Microsoft C/C++为:_beginthreadex。

unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned (*start_address)(void *),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
)
{
_ptiddata ptd;
uintptr_t thdl;
if((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL)
{
    goto error_return;
}

initptd(ptd);
ptd->_initaddr = (void*)pfnStartAddr;
ptd->_initarg = pvParam;
ptd->_thandle = (uintptr_t)(-1);

thd1 = (uintptr_t)CreateThread(
(LPSECURITY_ATTRIBUTES)psa,
cbStackSize,
_threadstartex,
(PVOID)ptd,
dwCreateFlags,
pdwThreadID);
if(thd1 == 0)
{
    goto error_return;
}
return thd1;

error_return:
    _free_crt(ptd);
    return ((uintptr_t)0L);
}

static unsigned long WINAPI _threadstartex(void *ptd)
{
TlsSetValue(__tlsindex, ptd);
((_ptiddata)ptd)->_tid = GetCurrentThreadId();
_callthreadstartex();
return 0L;
}

static void _callthreadstartex(void)
{
_ptiddata ptd;
ptd = _getptd();
__try
{
    _endthreadex(((unsigned (WINAPI*) (void*))(((_ptiddata)ptd)->_initaddr))(((_ptiddata)ptd)->_initarg));
}
__except(_XcptFilter(GetExceptionCode(), GetExceptionInformation()))
{
_exit(GetExceptionCode());
}
}

void __cdecl _endthreadex(unsigned retcode)
{
_ptiddata ptd;
ptd = _getptd_noexit();
if(ptd)
{
_freeptd(ptd);
}
ExitThread(retcode);
}

1.1.每个线程有自己的 _tiddata内存块,从堆上分配。
1.2.C/C++运行库为每个新线程准备一个独立的数据块,来解决C/C++运行库对多线程的支持。
1.3.执行顺序
_beginthreadex
CreateThread
RtlUserThreadStart
_threadstartex
_callthreadstartex

2.C/C++运行库还围绕特定函数放置了同步对象。如malloc。
3.当模块链接到C/C++运行库的DLL版本时,库在终止时收到DLL_THREAD_DETACH时,会释放已分配的_tiddata数据块。

-了解自己的身份

// 返回 主调进程的进程内核对象的 伪句柄
HANDLE GetCurrentProcess();
// 返回 主调进程的线程内核对象的 伪句柄
HANDLE GetCurrentThread();

DWORD GetCurrentProcessId();
DWORD GetCurrentThreadId();

// 将伪句柄转换为真实的句柄
// DuplicateHandle会递增所复制句柄的使用计数
DuplicateHandle(
GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&hThread,
x1,
x2,
x3
);

DuplicateHandle(
GetCurrentProcess(),
GetCurrentProcess(),
GetCurrentProcess(),
&hProcess,
x1,
x2,
x3
);

伪句柄:
它们不会在主调进程的句柄表中新建句柄,不会影响进程内核对象或线程内核对象的使用计数。对伪句柄执行CloseHandle,会返回FALSE。可以充当句柄的功能。

把伪句柄以值传递的方式传递给另一个进程或另一个线程,则,在传递后的进程或线程中使用时,它指代的就是所在进程或线程的伪句柄,而不再是原来所在的进程或线程的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Windows核心编程中,线程是进程的最小执行单位,负责执行进程地址空间中的代码。可以将进程理解为一个容器,而线程是在这个容器中执行代码的实体。每个进程都至少有一个线程,被称为主线程,可以通过代码创建其他线程,也称为子线程线程的好处包括易于调度、提高并发性、开销小、利于发挥多处理器系统的功能。通过创建多个线程,每个线程在一个处理器上运行,实现应用程序的并发性,提高系统的整体性能。 进程与线程的区别在于一个线程只能属于一个进程,但一个进程可以包含多个线程。资源分配给进程,而处理器分配给线程,即真正运行在处理机上的是线程线程在执行中需要协作同步。 在Windows核心编程中,可以使用CreateThread函数创建线程。该函数的参数包括线程的安全属性、线程栈的大小、线程函数、传递给线程函数的参数、线程创建的标记以及用于接收线程ID的指针。调用CreateThread函数成功后,会返回线程的句柄,可以用于操作该线程。 在线程的调度中,可以通过调用SuspendThread函数将线程挂起。而通过调用ResumeThread函数可以恢复被挂起的线程。 对于线程的终止,可以使用TerminateThread函数,但在设计时应尽量避免使用该函数。TerminateThread函数可以告诉系统要终止线程,但函数返回时并不能保证线程已经被终止。 另外,为了控制线程的执行速度,也可以使用Sleep函数来使线程进入睡眠状态一段时间。 总结来说,在Windows核心编程中,线程是执行进程代码的最小单位,可以通过CreateThread函数创建线程,通过SuspendThread函数挂起线程,通过ResumeThread函数恢复线程,通过Sleep函数控制线程的执行速度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值