{"id":102503,"date":"2019-05-20T07:00:00","date_gmt":"2019-05-20T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102503"},"modified":"2019-05-20T06:53:34","modified_gmt":"2019-05-20T13:53:34","slug":"20190520-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190520-00\/?p=102503","title":{"rendered":"If each thread&#8217;s TEB is referenced by the fs selector, does that mean that the 80386 is limited to 1024 threads?"},"content":{"rendered":"<p>Commenter Waleri Todorov recalled that the global descriptor table (GDT), which is one of the places that selectors are defined, is limited to 1024 selectors. <!-- A HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20190204-00\/?p=100855\" --> Does that mean that there is a hard limit of 1024 threads?<\/p>\n<p>The question was in the context of how Windows NT for the 80386 managed the thread environment block (TEB), namely, by using the <code>fs<\/code> register to point to the per-thread data. The point is that there are at most 1024 possible distinct values for the <code>fs<\/code> register to have, so does this implicitly limit the number of threads to 1024?<\/p>\n<p>No, it doesn&#8217;t, because nobody said that the distinct values had to be different simultaneously.<\/p>\n<p>Let&#8217;s start with a single-processor system. That single processor is executing only one thread at a time, so there needs to be only one valid value for <code>fs<\/code> at a time. When the processor changes threads, the definition of that selector is updated to refer to the TEB for the incoming thread. Using selectors to access another thread&#8217;s TEB is not part of the ABI; all that is required is that you can use <code>fs<\/code> to access <i>your own<\/i> TEB.<\/p>\n<p>You can see this in the debugger. Break into a multithreaded program and look at the value of the <code>fs<\/code> register. On my system it&#8217;s <code>0x0053<\/code>. Switch to another thread and look at the value of the <code>fs<\/code> register. It&#8217;s the same value: <code>0x0053<\/code>. Every thread has the same selector in <code>fs<\/code>. What happens is that each time the processor changes threads, the GDT entry for <code>0x0053<\/code> is updated to refer to the TEB of the thread that is being scheduled.\u00b9<\/p>\n<p>This trick works even on multiprocessor systems. Each processor has its own GDTR internal register, so instead of sharing a single GDT for all processors, you can give each processor its own GDT.<\/p>\n<p>So I guess this puts the theoretical maximum number of processors supported by an x86-based system at around twenty-four million, because that would exhaust all of kernel mode address space just for GDTs.\u00b2<\/p>\n<p>No, wait, that&#8217;s still not the limit, because each processor also gets its own page table. After all, that&#8217;s how two processors can be executing threads from different processes (and therefore in different address spaces). So the theoretical limit is basically <i>until you run out of memory<\/i>.<\/p>\n<p>But I suspect you&#8217;ll run into other problems long before you add that twenty-four-millionth processor.<\/p>\n<p>\u00b9 Bit 2 is clear for GDT selectors and set for LDT selectors, so you can infer that <code>0x0053<\/code> is a GDT selector.<\/p>\n<p>\u00b2 I calculated this by dividing 2\u00b3\u00b9 by <code>0x60<\/code>, which is my presumed minimum size for a GDT. A selector whose numeric value is <code>0x0053<\/code> implies that the GDT is at least <code>0x0058<\/code> bytes in size, because that&#8217;s how big you need to be to get to a selector value of <code>0x0053<\/code> in the first place.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>They don&#8217;t all have to be different simultaneously.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-102503","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>They don&#8217;t all have to be different simultaneously.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102503","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=102503"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102503\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=102503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}