如何在不使用POSIX库<pthread.h>的情况下在C语言中创建线程

17

我希望在C语言中实现多线程,但不使用任何POSIX库。

注:请勿使用fork()或vfork()。


1
你必须使用一些库来完成这个任务。线程是操作系统的构建块,你不能在程序内部自己创建它们。但你可以使用像glibc这样的东西,它提供了用作单独线程执行某些任务的API。 - fkl
你需要使用一些库来为你创建线程。pthread的替代方案可以在这里找到:http://www.gnu.org/software/pth/related.html - CCoder
5
你是否有不想使用POSIX线程的真正原因?除非你有有效的理由,否则你应该使用它们并停止浪费时间(你自己和我们的时间)。 - paxdiablo
1
@paxdiablo我在技术比赛的最后一轮被问到了这个问题,这是分数最高的问题。如果我知道答案,我会赢得那个问题的。我在现实世界中使用POSIX线程。 - Rahul
这就是为什么删除像“面试问题”这样的标签是非常糟糕的原因。 - UmNyobe
显示剩余3条评论
4个回答

20

在Linux中,线程本质上是与其父进程共享内存和资源的进程。Linux内核不区分进程和线程,换句话说,Linux中没有像其他操作系统中那样轻量级进程的概念。Linux中的线程被实现为标准进程,因此可以仅使用clone()创建线程,通常可通过以下方式调用fork()

clone(SIGCHLD, 0);

这将仅克隆信号处理程序,但是使用适当的标志,您可以创建一个线程:
clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

这与先前的调用相同,唯一不同之处在于两个进程共享地址空间、文件系统资源、文件描述符和信号处理程序。

另一种方法是使用用户级线程(也称为纤程),它们是在用户级别实现的执行线程,这意味着操作系统不知道这些线程,调度或上下文切换必须在用户级别进行。大多数用户级调度程序都是协作式调度程序,但也可以使用简单的轮询调度实现抢占式调度程序。

查看clone(2)手册以获取详细信息,如果您需要更多信息,我建议阅读罗伯特·洛夫(Robert Love)的Linux Kernel Development第三版(与作者无关),其中有一个内部查看链接,您可以在线阅读部分内容。至于用户级线程,我编写了一个名为libutask的最小包,实现了协作和抢占式调度程序,如果您愿意,可以检查源代码。

注1:我没有提到UNIX,据我所知,这是Linux特定的实现。

注2:使用clone创建自己的线程不是真正的解决方案,请阅读评论以了解可能需要处理的一些问题,它只是回答了一个问题,即是否可以在不使用pthread的情况下创建线程,在这种情况下答案是肯定的。


1
你可能需要为克隆进程准备一个堆栈。哦,而且不要在子线程中使用任何库函数,因为你的TLS设置会被搞乱。 - bdonlan
创建自己的线程使用clone将不起作用,除非您避免使用任何(直接或间接)调用标准库。这是因为即使像读/写errno这样基本的事情也会访问相对于线程指针/线程描述符结构的线程本地存储,如果您自己调用clone,则这些内容将不会被初始化或指向任何有效内容。 - R.. GitHub STOP HELPING ICE

8

参见:

适用于类UNIX系统。

还可以参见:

适用于BSD和现代UNIX。

这个页面提供了使用这些基元和更多内容的许多基本实现示例。

您可以使用原子指令来实现锁定原语(互斥锁,信号量)。

我还建议查看用户空间线程库的实际实现,以获取一些提示。请参见此页面,其中列出了Linux的实现列表。

最后,您可能需要获取有关协程和也许trampolines的一些信息,尽管后者与之不太相关。


使用longjmp()手动实现线程是一个非常糟糕的建议。 - user529758
@H2CO3 我并不是说这很容易 :) - didierc
@H2CO3,但它是可行的,可以看一下我提供的链接。 - didierc
我并不是在谈论它是否容易或困难(如果你足够熟练,一切都很容易),我是说这是一种不好的做法,而且不应该这样做。 - user529758
是的,我提供的链接页面中解决了这个问题,并且他们建议使用 sigaltstackraise 来缓解这个问题。显然,GNU Pth 使用了这种技术。既然你提到了这个问题,我会在我的答案中添加必要的系统调用。谢谢! - didierc
显示剩余2条评论

4
你还可以查看C标准库中的新<threads.h>头文件。(C11)
它包括你需要的内容,如int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);,以及互斥函数和条件变量。

2

在几乎任何操作系统上,使用纯 c 语言可以构建至少一个协作式微内核。基本上只需要克隆栈帧(并相应地调整一些指针,特别是从函数返回到其他线程当前返回地址的返回地址)。还需要一些实用程序函数,例如“上下文切换”堆栈到堆和反向操作。

如果允许定时器中断带有回调,则可以执行抢占式微内核。

至少 Dr Dobbs 和 IOCCC 提出了这些方案。


我的意思是,你必须使用一些特定于操作系统的指令,例如testandlock等来实现锁。像Peitersen这样的算法锁在理论上很不错,但在实践中从未像它所声称的那么高效。虽然我不是内核专家,但我对同步和其实现的细节有所了解。 - fkl
请查看我的答案以了解更多详情 :) - didierc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接