17

I want to implement the multiple threading in C without using any of the POSIX library. Any help would be appreciated.

Not : Don't use fork() or vfork().

Rahul
  • 4,699
  • 5
  • 26
  • 38
  • 1
    You have to use some library which does that. Threading is an OS building block. You cannot create them on your own from with in program. Still You might use things like glibc which gives api for performing certain tasks as a separate thread – fkl Nov 08 '12 at 05:55
  • You have to use some library which creates threads for you. Alternatives for pthreads can be found here http://www.gnu.org/software/pth/related.html – CCoder Nov 08 '12 at 05:59
  • 5
    Is there some _real_ reason you don't want to use POSIX threads? Unless you have a valid reason, you should use them and stop wasting time (yours and ours). – paxdiablo Nov 08 '12 at 06:04
  • 1
    @paxdiablo i was asked this question in a final round of a technical event and this was the question having the highest marks. I would have won that one if I knew the answer. and I use POSIX threads in REAL World. – Rahul Nov 08 '12 at 06:12
  • @RahulKumar check my updated answer, for the sake of completeness , I've also mentioned user-level threads. – iabdalkader Nov 08 '12 at 07:07
  • This is why removing tags like `interview questions` is just terrible. – UmNyobe Nov 08 '12 at 10:26
  • Related question: https://stackoverflow.com/q/10392800/2553424 – Arhadthedev Apr 03 '18 at 17:07
  • why not using OpenMP and/or OpenCL? – Foad S. Farimani May 22 '18 at 20:22

4 Answers4

20

A thread in Linux is essentially a process that shares memory and resources with its parent. The Linux Kernel does not distinguish between a process and a thread, in other words, there's no concept of a light weight process in Linux like in some other operating systems. Threads in Linux are implemented as standard processes, so it's possible to create a thread using just clone() which is normally called by fork() in the following way:

clone(SIGCHLD, 0);

This clones the signal handlers only, however, with the appropriate flags you can create a thread:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

This is identical to the previous call, except that the address space, filesystem resources, file descriptors and signal handlers are shared by the two processes.

A different approach is to use user-level threads (also called fibres) those are threads of execution implemented at the user-level, which means the OS is unaware of those threads and the scheduling or context switching has to be done at the user-level. Most user-level schedulers are implemented as cooperative schedulers, but it is also possible to implement a preemptive scheduler with a simple round robin scheduling.

Check the clone(2) man page for details and if you want more information I recommend the Linux Kernel Development 3rd edition By Robert Love, (not affiliated with the author in any way) there's a look inside link there you could read some of it online. As for the user-level threads, there's a minimal package written by me, called libutask, that implements both a cooperative and a preemptive scheduler, you can check the source code if you like.

Note1: I have not mentioned UNIX, as far as I know, this is Linux-specific implementation.

Note2: Creating your own threads with clone is not a real world solution, read the comments for the some problems you may have to deal with, it's only an answer to the question is it possible to create threads without using pthreads, in this case the answer is yes.

iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • 1
    You'll probably want to prepare a stack for the cloned process. Oh, and don't use any library functions in the child thread, since your TLS setup will be all screwed up. – bdonlan Nov 08 '12 at 06:13
  • "Creating your own threads" with `clone` **will not work** unless you refrain from using any (direct or indirect) calls to the standard library. This is because even things as basic as reading/writing `errno` access thread-local storage relative to a thread pointer/thread descriptor structure, which will not be initialized or pointing at anything valid if you call `clone` yourself. – R.. GitHub STOP HELPING ICE Nov 08 '12 at 07:40
8

See:

for UNIX like systems.

Also see:

for BSDs and modern UNIXes.

This page gives many examples of barebone implementations using these primitives and more.

You can use atomic instructions to implement the locking primitives (mutex, semaphores).

I also suggest looking at actual implementations of userland thread libraries to get some hints. See this page which gives a list of implementations for Linux.

Finally, you might want to get some information on coroutines and perhaps trampolines, although the later isn't as closely related.

didierc
  • 14,572
  • 3
  • 32
  • 52
  • Implementing threads à la main using `longjmp()` is a very bad piece of advice. –  Nov 08 '12 at 09:54
  • @H2CO3 I'm not saying it's easy :) – didierc Nov 08 '12 at 10:15
  • @H2CO3 but it's doable, see the link I provided. – didierc Nov 08 '12 at 10:25
  • I was not talking about if it's easy or hard (if you are skilled enough, everything is easy), I'm saying it's bad practice and one should not do it. –  Nov 08 '12 at 10:56
  • @H2CO3 Could you give an explanation as to why it is bad practice? It's on topic and I would like to know! – didierc Nov 08 '12 at 12:03
  • `setjmp()` and `longjmp()` need not preserve stack state - this means they cannot be used, in a portable way, for jumping *back to the caller function* in case of the second context switch. Although they often have this behavior, it's undocumented and not portable. –  Nov 08 '12 at 14:38
  • Yes, this issue is addressed in the page I linked, and they propose to use `sigaltstack` and `raise` to alleviate the problem. Apparently, GNU Pth uses this technique. Since you mentioned the issue, I am adding the necessary system calls in my answer. Thank you! – didierc Nov 08 '12 at 15:46
4

You can also check out the new <threads.h> header from the C standard library. (C11)

It's got what you need like int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); as well as mutex functions and condition variables.

Wiz
  • 2,145
  • 18
  • 15
2

One can most certainly make at least a co-operative microkernel with plain c on top of pretty much any operating system. Fundamentally it only requires cloning of the stack frame (and adjusting a few pointers accordingly -- especially the return address from a function to the other threads current return address). And a few utility functions, such as "context switch" the stack to heap and back.

If a timer interrupt with a callback is allowed, one can do a pre-emptive microkernel.

At least Dr Dobbs and IOCCC have presented options along these lines.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
  • Yeah my point was you have to use some OS specific instructions such as testandlock etc. for e.g. for implementing locks. Algorithmic locks such as Peitersen's sound good for concept but are never as efficient as in practice. I am not a kernel guy but i just know details of synchronization and its implementations. – fkl Nov 08 '12 at 06:14
  • see my own answer for more details :) – didierc Nov 08 '12 at 10:14