Page 1 of 1

threads address space implementation confusion

Posted: Mon May 03, 2010 3:39 pm
by garob
Okay so each thread in the linked list of tasks looks like this:

Code: Select all

typedef struct task
{
   int id;                      // Process ID.
   int parent_id;           // Process ID of parent.
   u32int esp, ebp;       // Stack and base pointers.
   u32int eip;               // Instruction pointer.
   page_directory_t *page_directory; // Page directory.
   struct task *next;     // The next task in a linked list.
} task_t;
If I create a process and then fork it the pointers to page tables in the page directory should all be the same except for the pointers to the tables that contain the stack's page tables(my max stack size will be a multiple of 4Mb).

My question is that is it usual to implement each threads address space as described above with each thread getting an individual address space each containing one stack and sharing common page tables(such as for code, data bss, heap) and when a call is made to expand the common address space such as with sbrk() you need to go through the page directory of that threads family and set the right pointer to point to a newly created page table if one had to be made, or is it usual for all threads to point to the same page table and the address space to contain all the process's stacks?

Re: threads address space implementation confusion

Posted: Mon May 03, 2010 3:51 pm
by Combuster
It is generally more efficient to have a process' threads share the same page directory (CR3 value), because changing that causes all TLB caches to be wiped - even though the address spaces are identical. You also save a few bytes and instructions in not having to store multiple page directories, and (lazily) update each one of them for every thing you map.

Differences in address space per thread are usually made by using the FS or GS segments for thread-local storage.

Re: threads address space implementation confusion

Posted: Mon May 03, 2010 4:10 pm
by garob
Thanks for the answer

What about for kernel address space? When expanding or contracting the kernel heap or loading a driver I would need to add it to all address spaces, right?

Re: threads address space implementation confusion

Posted: Mon May 03, 2010 4:14 pm
by Combuster
Yes and no - you only need to perform one "add" operation to have the effect apply to all address spaces. Can you figure out how? (Tip: try not to waste memory on duplicate tables)

Re: threads address space implementation confusion

Posted: Tue May 04, 2010 7:01 am
by JamesM
garob wrote:Thanks for the answer

What about for kernel address space? When expanding or contracting the kernel heap or loading a driver I would need to add it to all address spaces, right?
Remember that the page tables are two-tier. So yes, if you added a new PDE (page directory entry, first tier), you would have to manually update the page directories for all address spaces. But if you were to just add a PTE (page table entry, second tier) to an already existing PDE in kernel space, that update would be reflected automatically across all others (as they share the same PDE).

I find the best way to deal with this is to take the 4MB hit and preallocate all the PDEs for kernel space (1GB of address space in my case for 32-bit), then you have no cleanup code because new kernel PDEs never get added after an address space is copied.

Re: threads address space implementation confusion

Posted: Tue May 04, 2010 10:05 am
by Owen
One reason for having the stacks of all threads in the same address space: pointers to stack allocated objects often get passed around between threads.

Re: threads address space implementation confusion

Posted: Tue May 04, 2010 11:36 am
by Combuster
I sure hope not :shock:. Do you have any idea how many stack smashes and race conditions that causes?

Re: threads address space implementation confusion

Posted: Tue May 04, 2010 12:42 pm
by Owen
Combuster wrote:I sure hope not :shock:. Do you have any idea how many stack smashes and race conditions that causes?
Common Qt C++ example:

Code: Select all

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    MyWindow mw;
    mw.show();
    return app.exec();
}

Anything dangerous there?

Re: threads address space implementation confusion

Posted: Tue May 04, 2010 1:01 pm
by Combuster
Obviously, making the thread that "exclusively own" it block is of course the only hack that works. I also couldn't find any language requirement that forces all threads' stacks to exist in each thread's address space. (or they are the same, in the case of fork() )

Re: threads address space implementation confusion

Posted: Tue May 04, 2010 7:34 pm
by gerryg400
My question is that is it usual to implement each threads address space as described above with each thread getting an individual address space each containing one stack and sharing common page tables(such as for code, data bss, heap) and when a call is made to expand the common address space such as with sbrk() you need to go through the page directory of that threads family and set the right pointer to point to a newly created page table if one had to be made, or is it usual for all threads to point to the same page table and the address space to contain all the process's stacks?
I'd say it's more usual for the threads in a process to share a single process address space, so that the stack of each thread is available to all. A problem would occur if a function started some threads to process data that exisited on its (the parent's) stack. Perhaps (discalimer: I'm definitely not a c++ programmer) if the thread creation were inside a c++ class constructor, and the class was instantiated as a local variable there could be problems accessing static variables within the class ?

In the 'usual' model, if two threads don't want to share memory, then they can/should be in different processes.

- gerryg400

Re: threads address space implementation confusion

Posted: Wed May 05, 2010 3:39 am
by garob
I find the best way to deal with this is to take the 4MB hit and preallocate all the PDEs for kernel space (1GB of address space in my case for 32-bit), then you have no cleanup code because new kernel PDEs never get added after an address space is copied.
Don't you mean 1MB, not 4MB (4096 bytes per table, 256 tables needed for the kernel address space)?
Or do you mean 4MB to create all the tables at once for a user space program?

Re: threads address space implementation confusion

Posted: Thu May 06, 2010 4:13 am
by JamesM
garob wrote:
I find the best way to deal with this is to take the 4MB hit and preallocate all the PDEs for kernel space (1GB of address space in my case for 32-bit), then you have no cleanup code because new kernel PDEs never get added after an address space is copied.
Don't you mean 1MB, not 4MB (4096 bytes per table, 256 tables needed for the kernel address space)?
Or do you mean 4MB to create all the tables at once for a user space program?
That's the one. Knew I'd got my numbers wrong but was too lazy to look it up!