threads address space implementation confusion

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
garob
Posts: 16
Joined: Wed Dec 30, 2009 3:26 am
Location: Melbourne, Australia

threads address space implementation confusion

Post 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?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: threads address space implementation confusion

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
garob
Posts: 16
Joined: Wed Dec 30, 2009 3:26 am
Location: Melbourne, Australia

Re: threads address space implementation confusion

Post 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?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: threads address space implementation confusion

Post 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)
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Re: threads address space implementation confusion

Post 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.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: threads address space implementation confusion

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: threads address space implementation confusion

Post by Combuster »

I sure hope not :shock:. Do you have any idea how many stack smashes and race conditions that causes?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: threads address space implementation confusion

Post 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?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: threads address space implementation confusion

Post 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() )
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: threads address space implementation confusion

Post 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
If a trainstation is where trains stop, what is a workstation ?
garob
Posts: 16
Joined: Wed Dec 30, 2009 3:26 am
Location: Melbourne, Australia

Re: threads address space implementation confusion

Post 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?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Re: threads address space implementation confusion

Post 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!
Post Reply