Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

From the book Advanced programming in the Unix environment I read the following line regarding threads in Unix like systems

All the threads within a process share the same address space, file descriptors, stacks, and process-related attributes.Because they can access the same memory,the threads need to synchronize access to shared data among themselves to avoid inconsistencies.

What does the author mean by stacks here ? I do Java programming and know each thread gets its own stack . So I am confused by shared stacks concept here .

share|improve this question
2  
Have a look at wikipedia articels on Stack and Thread to get a general idea of how things work. –  peterph Dec 28 '12 at 13:10
add comment

2 Answers

up vote 5 down vote accepted

In the context of a Unix or linux process, the phrase "the stack" can mean two things.

First, "the stack" can mean the last-in, first-out records of the calling sequence of the flow of control. When a process executes, main() gets called first. main() might call printf(). Code generated by the compiler writes the address of the format string, and any other arguments to printf() to some memory locations. Then the code writes the address to which flow-of-control should return after printf() finishes. Then the code calls a jump or branch to the start of printf(). Each thread has one of these function activation record stacks. Note that a lot of CPUs have hardware instructions for setting up and maintaining the stack, but that other CPUs (IBM 360, or whatever it's called) actually used linked lists that could potentially be scattered about the address space.

Second, "the stack" can mean the memory locations to which the CPU writes arguments to functions, and the address that a called-function should return to. "The stack" refers to a contiguous piece of the process' address space.

Memory in a Unix or Linux or *BSD process is a very long line, starting at about 0x400000, and ending at about 0x7fffffffffff (on x86_64 CPUs). The stack address space starts at the largest numerical address. Every time a function gets called, the stack of function activatio records "grows down": the process code puts function arguments and a return address on the stack of activatio records, and decrements the stack pointer, a special CPU register used to keep track of where in the address space of the stack, the process current variables' values reside.

Each thread gets a piece of "the stack" (stack address space) for its own use as a function activation record stack. Somewhere between 0x7fffffffffff and a lower address, each thread has an area of memory reserved for use in function calls. Usually this is only enforced by convention, not hardware, so if your thread calls function after nested function, the bottom of that thread's stack can overwrite the top of another thread's stack.

So each thread has a piece of "the stack" memory area, and that's where the "shared stack" terminology comes from. It's a consequence of a process address space being a single linear chunk of memory, and the two uses of the term "the stack". I'm pretty sure that some older JVMs (really ancient) in reality only had a single thread. Any threading inside the Java code was really done by a single real thread. Newer JVMs, JVMs who invoke real threads to do Java threads, will have the same "shared stack" I describe above. Linux and Plan 9 have a process-starting system call (clone() for Linux, rfork() in Plan 9) that can set up processes that share parts of the address space, and maybe different stack address spaces, but that style of threading never really caught on.

share|improve this answer
    
The stack doesn't necessarily need to grow down, that is architecture dependent. Memory addresses really start at 0x0, the question is what virtual addresses are mappable - the lowest pages often are not to prevent NULL pointer dereference hitting a valid page. –  peterph Dec 28 '12 at 13:05
    
It is not true that native thread stacks are taken from some "main stack" of the process -- that implies the process arranges all this, which it does not. The kernel does, and it provides a separate stack for each thread, including the first one (which is not subsequently "divided up"). –  TAFKA 'goldilocks' Dec 28 '12 at 14:38
    
@peterph - You make good points, but can you name an architecture other than HP's PA-RISC that has a stack that grows up? Section 2.3.2 of "Thirty Years Later: Lessons learned from the Multics Security Evaluation" (acsac.org/2002/papers/classic-multics.pdf) implies that Multics hardware had such a stack. –  Bruce Ediger Dec 28 '12 at 14:41
    
@BruceEdiger check SO for some interesting reading. In additionn to that I believe for example on DEC Alphas stack grew - not sure if I can say "has grown" :) - up as well. –  peterph Dec 28 '12 at 15:36
    
@goldilocks by native you mean those that are implemented as separated kernel processes/tasks sharing some virtual memory mapping (all but thread local stuff)? –  peterph Dec 28 '12 at 15:37
show 4 more comments

What does the author mean by stacks here ? I do Java programming and know each thread gets its own stack . So I am confused by shared stacks concept here.

The use of the plural there is a bit odd and it does seem misleading, perhaps the point is that the multiple stacks of a multi-threaded program share the same address space.

As Bruce Ediger describes, "stack" refers to a single region of contiguous address space in which data is placed LIFO-wise. However, each native thread in a process has such a contiguous region -- there is not one divided up between each of them. Creating a thread does require the allocation of an additional stack of the same size (this is a fixed number, eg. the default on linux is 8 MB), which is why excessively multi-threaded applications can consume excessive amounts of memory.

Java uses native threads to implement java threads, but it manages a "stack" for each of the java threads itself, independent of the kernel. This means setting some global memory aside to use for this purpose; see the third part of the top answer here:

http://stackoverflow.com/questions/5483047/why-is-creating-a-thread-said-to-be-expensive

Of course, that refers to a specific implementation (openjdk), but presumably they all have to do this (allocate heap natively for internal use as a "thread stack").

Since the individual stack used by each native thread is managed by the kernel, I disagree with Bruce's implication that this is part of some overall stack belonging to the process as a whole -- again, a single (aka non-) threaded process only has one stack, but the main thread in a multi-threaded process does not share stack space with the other threads.

A "shared stack" would be one in which all data is stored beginning with a single address -- this is the sense in which nested function calls share the same stack. However, using a tweaked version of an example program mentioned below by Bruce, from the linux version of the pthread_create man page man page:

./a.out one two three
In main() stack starts near: 0x7fff17b80f98
Thread 1: top of stack near 0x7f11ac6d3e78; argv_string=one
Thread 2: top of stack near 0x7f11abed2e78; argv_string=two
Thread 3: top of stack near 0x7f11ab6d1e78; argv_string=three

This program takes the address of a local variable in the threaded function; the tweak I added was to do the same thing in main() before the threads are created. The run is on a recent 64-bit linux system; notice the starting addresses for the threads are EXACTLY 8 MB apart, and they are very far indeed from the top of the main thread stack. Contrast this with nested function calls like this:

#include <stdio.h>

void eg (int n) {
    char *p;
    printf("#%d first variable at %p\n", n, &p);
    if (n < 3) eg(n+1);
}

int main(int argc, const char *argv[]) {
    char *p;
    printf("main() first variable at %p\n", &p);
    eg(1);
    return 0;
}

Example run:

main() first variable at 0x7fffef0aaf68
#1 first variable at 0x7fffef0aaf38
#2 first variable at 0x7fffef0aaf08
#3 first variable at 0x7fffef0aaed8

These are just 48 bytes apart -- in other words, they are being placed one after the other contiguously (with other little bits of actual data) with no unused space between them. If we use that in the multi-threaded program, without the nested recursion but calling eg() once in each thread:

Thread 1: top of stack near 0x7f4bd5061e78; argv_string=one
Thread 2: top of stack near 0x7f4bd4860e78; argv_string=two
Thread 3: top of stack near 0x7f4bd405fe78; argv_string=three
#3 first variable at 0x7f4bd405fe48
#1 first variable at 0x7f4bd5061e48
#2 first variable at 0x7f4bd4860e48

The variable for each is near the top of the three separate stacks.

All of this is in the high address region referred to as "stack space", but in a multi-threaded program, that is divided into multiple stacks; it is not treated as one big LIFO structure.

share|improve this answer
    
The pthread_create(3) man page has an example program that illustrates exactly what I was writing about. I see that I used "the stack" to represent both function activation records in a stack structure, and the (on x86) upper address space used to contain the values of the stack structure. –  Bruce Ediger Dec 28 '12 at 16:08
    
Not really; although in the example run the virtual addresses are equidistant, they are not within the stack space of either each other or the main thread. Otherwise, you would use all of the main stack with only a few threads. The virtual address space of a process is more than just one giant stack -- the stack is a contiguous region of the address space, but "virtual address space" and "stack" are not synonymous. There's also nothing in the specification that promises those address will be sequential or equidistant. –  TAFKA 'goldilocks' Dec 28 '12 at 16:24
1  
@BruceEdiger : I do see your point about all the stacks being in the same high address "stack space" but this does not mean all the data from all threads is placed in one big LIFO structure -- they each have an independent stack. I've added some stuff using the pthread_create example to clarify this. –  TAFKA 'goldilocks' Dec 28 '12 at 17:55
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.