Myslím, že tato část clone(2)
manuálová stránka může vyjasnit rozdíl. PID:
CLONE_THREAD (od Linuxu 2.4.0-test8)
Pokud je nastaveno CLONE_THREAD, potomek je umístěn do stejné skupiny vláken jako volající proces.
Skupiny vláken byly funkcí přidanou v Linuxu 2.4, aby podporovala představu vláken POSIX o sadě vláken, která sdílejí jeden PID. Interně je toto sdílené PID takzvaným identifikátorem skupiny vláken (TGID) pro skupinu vláken. Od Linuxu 2.4 vracejí volání getpid(2) TGID volajícího.
Fráze „vlákna jsou implementována jako procesy“ odkazuje na problém vláken, která měla v minulosti samostatné PID. Linux v podstatě původně neměl vlákna v procesu, jen samostatné procesy (se samostatnými PID), které mohly mít nějaké sdílené prostředky, jako je virtuální paměť nebo deskriptory souborů. CLONE_THREAD
a díky oddělení ID procesu a ID vlákna vypadá chování Linuxu více jako jiné systémy a více jako požadavky POSIX v tomto smyslu. Ačkoli technicky operační systém stále nemá samostatné implementace pro vlákna a procesy.
Další problematickou oblastí u staré implementace byla manipulace se signálem, která je podrobněji popsána v článku, na který @FooF odkazuje ve své odpovědi.
Jak je uvedeno v komentářích, Linux 2.4 byl také vydán v roce 2001, ve stejném roce jako kniha, takže není divu, že se zprávy nedostaly do tohoto tisku.
Máte pravdu, skutečně "něco se muselo změnit mezi rokem 2001 a nyní". Kniha, kterou právě čtete, popisuje svět podle první historické implementace vláken POSIX v Linuxu, nazvané LinuxThreads (některé viz také článek na Wikipedii).
LinuxThreads měl určité problémy s kompatibilitou se standardem POSIX - například vlákna nesdílela PID - a některé další vážné problémy. Aby se tyto nedostatky napravily, Red Hat postavil do čela další implementaci nazvanou NPTL (Native POSIX Thread Library), která přidala nezbytnou podporu knihoven jádra a uživatelského prostoru pro dosažení lepší shody s POSIX (přebírá dobré části z dalšího konkurenčního projektu reimplementace IBM s názvem NGPT (" Next Generation Posix Threads"), viz článek Wikipedie o NPTL). Do clone(2)
byly přidány další příznaky systémové volání (zejména CLONE_THREAD
že @ikkkachu
poukazuje ve své odpovědi) je pravděpodobně nejzřetelnější částí modifikací jádra. Část práce s uživatelským prostorem byla nakonec začleněna do knihovny GNU C.
Stále ještě dnes některé embedded Linux SDK používají starou implementaci LinuxThreads, protože používají verzi LibC s menšími nároky na paměť nazvanou uClibc (také nazývaná µClibc), a trvalo mnoho let, než byla implementace uživatelského prostoru NPTL z GNU LibC portována a předpokládá se jako výchozí implementace vláken POSIX, protože obecně řečeno tyto speciální platformy se nesnaží následovat nejnovější módy v rychlosti blesku. Použití implementace LinuxThreads v provozu lze pozorovat tak, že si všimnete, že PID pro různá vlákna na těchto platformách se skutečně liší na rozdíl od specifikací standardu POSIX – stejně jako popisuje kniha, kterou právě čtete. Vlastně, jakmile jste zavolali pthread_create()
, náhle jste zvýšili počet procesů z jedné na tři, protože k udržení nepořádku bylo zapotřebí dalšího procesu.
Manuálová stránka Linuxu pthreads(7) poskytuje komplexní a zajímavý přehled rozdílů mezi těmito dvěma. Dalším poučným, i když zastaralým popisem rozdílů je tento článek Ulricha Deppera a Ingo Molnara o návrhu NPTL.
Doporučuji nebrat tuto část knihy příliš vážně. Místo toho doporučuji Butenhofova programovací vlákna POSIX a manuálové stránky POSIX a Linux na toto téma. Mnoho výukových programů na toto téma je nepřesných.
Vlákna (Userspace) nejsou v Linuxu implementována jako procesy jako takové, protože nemají svůj vlastní soukromý adresní prostor, stále sdílejí adresní prostor nadřazeného procesu.
Tato vlákna jsou však implementována pro použití systému účtování procesů jádra, takže mají přiděleno své vlastní ID vlákna (TID), ale je jim přiděleno stejné PID a „ID skupiny vláken“ (TGID) jako nadřazenému procesu – to je na rozdíl od vidlice, kde se vytvoří nové TGID a PID a TID je stejné jako PID.
Zdá se tedy, že nedávná jádra měla samostatné TID, na které je možné se dotazovat, právě toto se u vláken liší. Vhodný úryvek kódu, který to zobrazí v každém z výše uvedených main() thread_function() je:
long tid = syscall(SYS_gettid);
printf("%ld\n", tid);
Takže celý kód s tímto by byl:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <syscall.h>
void* thread_function (void* arg)
{
long tid = syscall(SYS_gettid);
printf("child thread TID is %ld\n", tid);
fprintf (stderr, "child thread pid is %d\n", (int) getpid ());
/* Spin forever. */
while (1);
return NULL;
}
int main ()
{
pthread_t thread;
long tid = syscall(SYS_gettid);
printf("main TID is %ld\n", tid);
fprintf (stderr, "main thread pid is %d\n", (int) getpid ());
pthread_create (&thread, NULL, &thread_function, NULL);
/* Spin forever. */
while (1);
return 0;
}
Uvádíme příklad výstupu:
main TID is 17963
main thread pid is 17963
thread TID is 17964
child thread pid is 17963