Re: proc fs and shared pids

Linus Torvalds (torvalds@cs.helsinki.fi)
Wed, 7 Aug 1996 08:10:35 +0300 (EET DST)


On Tue, 6 Aug 1996, Al Longyear wrote:
>
> Microsoft (yea, yea, I know some people's opinion of commerical
> companies, but that is not really the issue that I am trying to make)
> did not support threads in their POSIX implementation of Windows NT
> for several reasons.
>
> The reaons are well known, but perhaps the most well known is "What does it
> mean if a thread does a call to fork()?"

These kinds of issues are _exactly_ why clone() is such a good idea. With
clone() there is never any ambiguity when it comes to fork/exec/exit,
something that is not true of traditional threads. And the clone()
semantics are actually _sane_ for all these cases.

If you look at threads as just contexts of execution, WITHOUT the idiotic
threads/process separation that traditional threads packages use, the
semantics are always clear:

- fork() is exactly the same as "clone(0)", and as such it's just
another way to create a new thread. It's _completely_ orthogonal to
all the other thread operations, and if you think of fork() as
clone(0) (and that's actually how it's implemented inside the kernel
too, not just a "conceptual" idea) there is never any question of what
happens.
- execve() is just a way to change the VM space of a task. Whether that
task is a "thread" or a "process" in traidional parlance is totally
irrelevant. A execve() does not change the VM space of other tasks.
- the exit() system call closes down only the task that called it.
Nothing else makes sense (if you want to have other behaviour you use
"atexit()" to send a signal to the other threads you want to shut
down).
- quite naturally, a terminal signal kills only the task that got the
signal.

The above are all problems that exist with "traditional" threads, as you
point out. The fact that the problems exist in the first place, and that
traditional threads do not have clear-cut answers to them is just another
reason why traditional threads are obviously not the right thing. If the
semantics aren't clear, you CANNOT consider a interface a good one.

> Perhaps the cleanest approach is to simply deny fork() if you have more
> than one thread. That too, would work. If you are going to write a multi-
> threaded application then do you need fork for creating a copy of your
> application to run a second copy with your data? Perhaps a 'create_process'
> function could be implemented to start a process in a multi-thread app.

"execve()" is actually much worse than "fork()" when it comes to traditional
threads. For fork() you can at least come up with _some_ reasonable behaviour
(usually that only the fork'ing thread continues execution in the child, but
the implementation is usually very ugly). But execve() is _totally_
impossible to do sanely with traditional threads.

The clone() interface has _none_ of these problems. There simply aren't
any ambiguosities with how the path of execution works.

> In terms of threads, I am of the opinion that a thread is just the
> current cpu state, priority, etc. It does not contain maps to memory,
> file tables, etc. Those other items belong to the process. A thread is
> just a dispatchable context, or COE.

WHY? You just pointed out yourself only _some_ of the problems with that
approach. The whole approach is stupidity incarnate. The only reason that
people are using the stupid thread/process approach is historical (due to
the fact that you can implement that kind of limited threading with no
kernel support, by "hiding" the threads inside the processes the kernel
knows about). It's only a half-hearted approach at doing threading.

The "clone()" approach, on the other hand, makes threads first-class objects.
That's _real_ threading, as opposed to the thread/process limited threading.

I see that you use "COE": what I meant with COE was the exact opposite of
what you say. A COE is not just the CPU state and priority. A COE is
_everything_ that is needed to actually run, and thus it includes memory
mappings etc. It's the "struct task_struct" in Linux world. After all, you
can't dispatch a thread without all the other info..

Linus