Re: kernel threading and 1.3.29.....

Linus Torvalds (Linus.Torvalds@cs.Helsinki.FI)
Wed, 27 Sep 1995 09:39:10 +0200


Randy Chapman: "kernel threading and 1.3.29....." (Sep 26, 11:06):
>
> Is anyone working on a threading library for the new thread-capable stuff
> in 1.3.29? If not, I guess I'll bo doing it ;)
>
> Also, I have a question about how clone() works in re to the stack. It
> would appear, from my initial testing of it, that the stack is marked as
> copy-on-write for each clone()'ed process.

No, there is no C-O-W with clone() if you use the CLONE_VM flag: the
stack is handled by giving clone() a completely new stackpointer for the
new process (so that you have two separate stacks in the same process
space).

> This would seem to me to be a problem should you, say, pass a pointer to
> something on the stack to a thread. Say, for example, that main()
> defines a C++ object on the stack and sets a global pointer to it.
> Unfortunately, each thread will have a separate copy of the object, which
> is kinda icky when thats not what most people expect. It isnt what Win32
> (ick!) threading, nor Java's green threads nor pthreads does. I dont
> have my Solaris machine, so I cant check that yet.

Don't worry, linux threading can use stack pointers between threads
(although you obviously have to be very careful about rendezvous between
the threads so that the stack of the other thread doesn't get
corrupted).

I'm including a (very stupid) program to show how the threading stuff
works.. Essentially, it only shows roughly how to create a new thread
(and start it off), and then it does a silly demo where the child
modifies a variable in the mother and closes a file descriptor in the
mother (you can play with not using CLONE_VM and CLONE_FILES to see how
the child can be made to not share either VM or file descriptors with
the mother).

Linus

----------
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#include <linux/unistd.h>

#define STACKSIZE 16384

#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers shared */

/*
* This is a "kind-of" thr_create() as in pthreads, but not really.
* It needs some fleshing out to work like pthreads thr_create().
*/
int start_thread(void (*fn)(void *), void *data)
{
long retval;
void **newstack;

/*
* allocate new stack for subthread
*/
newstack = (void **) malloc(STACKSIZE);
if (!newstack)
return -1;

/*
* Set up the stack for child function, put the (void *)
* argument on the stack.
*/
newstack = (void **) (STACKSIZE + (char *) newstack);
*--newstack = data;

/*
* Do clone() system call. We need to do the low-level stuff
* entirely in assembly as we're returning with a different
* stack in the child process and we couldn't otherwise guarantee
* that the program doesn't use the old stack incorrectly.
*
* Parameters to clone() system call:
* %eax - __NR_clone, clone system call number
* %ebx - clone_flags, bitmap of cloned data
* %ecx - new stack pointer for cloned child
*
* In this example %ebx is CLONE_VM | CLONE_FS | CLONE_FILES |
* CLONE_SIGHAND which shares as much as possible between parent
* and child. (We or in the signal to be sent on child termination
* into clone_flags: SIGCHLD makes the cloned process work like
* a "normal" unix child process)
*
* The clone() system call returns (in %eax) the pid of the newly
* cloned process to the parent, and 0 to the cloned process. If
* an error occurs, the return value will be the negative errno.
*
* In the child process, we will do a "jsr" to the requested function
* and then do a "exit()" system call which will terminate the child.
*/
__asm__ __volatile__(
"int $0x80\n\t" /* Linux/i386 system call */
"testl %0,%0\n\t" /* check return value */
"jne 1f\n\t" /* jump if parent */
"call *%3\n\t" /* start subthread function */
"movl %2,%0\n\t"
"int $0x80\n" /* exit system call: exit subthread */
"1:\t"
:"=a" (retval)
:"0" (__NR_clone),"i" (__NR_exit),
"r" (fn),
"b" (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD),
"c" (newstack));

if (retval < 0) {
errno = -retval;
retval = -1;
}
return retval;
}

int show_same_vm;

void cloned_process_starts_here(void * data)
{
printf("child:\t got argument %d as fd\n", (int) data);
show_same_vm = 5;
printf("child:\t vm = %d\n", show_same_vm);
close((int) data);
}

int main()
{
int fd, pid;

fd = open("/dev/null", O_RDONLY);
if (fd < 0) {
perror("/dev/null");
exit(1);
}
printf("mother:\t fd = %d\n", fd);

show_same_vm = 10;
printf("mother:\t vm = %d\n", show_same_vm);

pid = start_thread(cloned_process_starts_here, (void *) fd);
if (pid < 0) {
perror("start_thread");
exit(1);
}

sleep(1);
printf("mother:\t vm = %d\n", show_same_vm);
if (write(fd, "c", 1) < 0)
printf("mother:\t child closed our file descriptor\n");
}
----------