Жіптік жергілікті сақтау (TLS) – бағдарламалауда жіптер үшін арналған жадты басқару әдісі. Жадты динамикалық түрде бөлу арқылы көлемді сақтауға мүмкіндік береді.
Ағылшыншамен салыстырыңыз: абзацты басыңыз — түпнұсқа терезеде ашылады. Абзац астындағы EN түймесі оны мәтін ішінде көрсетеді.
Мазмұны
Кіріспе
Компьютерлік жадыны басқару әдісі
Method for computer memory management
Компьютерлік бағдарламалауда, жиекке (thread) жергілікті сақтау (TLS) – жиекке жергілікті статикалық немесе жаһандық жадты пайдаланатын жадты басқару әдісі. Бұл ұғым жеке жиектері бар жүйеде жаһандық көрінетін деректерді сақтауға мүмкіндік береді. Көптеген жүйелер жиекке жергілікті жад блогының мөлшеріне шектеу қояды, көбінесе бұл шектеулер өте қатаң болады. Дегенмен, егер жүйе кем дегенде жад мекенжайы (көрсеткіш) мөлшеріндегі айнымалы жиекке жергілікті жадты қамтамасыз ете алса, онда бұл кез келген мөлшердегі жад блоктарын жиекке жергілікті түрде пайдалануға мүмкіндік береді, мұндай жад блогын динамикалық түрде бөліп, осы блоктың жад мекенжайын жиекке жергілікті айнымалыда сақтау арқылы. RISC машиналарында шақыру конвенциясы көбінесе осы мақсат үшін жиек көрсеткіш тіркелімін резервтейді.
In computer programming, thread local storage (TLS) is a memory management method that uses static or global memory local to a thread. The concept allows storage of data that appears to be global in a system with separate threads. Many systems impose restrictions on the size of the thread local memory block, in fact often rather tight limits. On the other hand, if a system can provide at least a memory address (pointer) sized variable thread local, then this allows the use of arbitrarily sized memory blocks in a thread local manner, by allocating such a memory block dynamically and storing the memory address of that block in the thread local variable. On RISC machines, the calling convention often reserves a thread pointer register for this use.
Қолданылуы
Қазіргі заманғы бағдарламалауда жалпы айнымалыларды қолдану көбінесе ұсынылмайды, бірақ UNIX сияқты кейбір ескі операциялық жүйелер бастапқыда бір процессорлы аппараттық құралдар үшін жасалған және маңызды мәндерді сақтау үшін жиі жалпы айнымалыларды пайдаланады. Мысалы, C кітапханасының көптеген функциялары қолданатын `errno`. Қазіргі заманғы машинада, егер бірнеше жіп `errno` айнымалысын өзгерте алса, бір жіптегі жүйелік функцияны шақыру, басқа жіптегі жүйелік функцияны шақыру арқылы бұрын орнатылған мәнді жойып жіберуі мүмкін, тіпті осы басқа жіптегі код қате жағдайын тексеруге үлкермес бұрын. Шешім – `errno` айнымалысы глобалды сияқты көрініп тұрса да, физикалық жағынан әр жіпке арналған жад жинағында, яғни жіптік жергілікті сақтау орнында сақталуы керек. Екінші жағдай – бірнеше жіптің ақпаратты жалпы айнымалыға жинақтауы. Жарыс жағдайын болдырмау үшін, осы жалпы айнымалыға кез келген қолжетімділік mutex арқылы қорғалуы тиіс. Оның орнына, әр жіп жеке жіптік жергілікті айнымалыға жинақтауы мүмкін, осылайша жарыс жағдайын тудыру мүмкіндігін жойып, құлыптау қажеттілігін де жояды. Содан кейін жіптер тек өздерінің жеке жіптік жергілікті айнымалыларынан бір ғана жалпы айнымалыға соңғы жинақтауды синхронизациялауы керек.
While the use of global variables is generally discouraged in modern programming, some older operating systems such as UNIX were originally designed for uniprocessor hardware and often use global variables to store important values. An example is the errno used by many functions of the C library. On a modern machine, where multiple threads may be modifying the errno variable, a call of a system function on one thread may overwrite the value previously set by a call of a system function on a different thread, possibly before following code on that different thread could check for the error condition. The solution is to have errno be a variable that looks as if it is global, but is physically stored in a per thread memory pool, the thread local storage. A second use case would be multiple threads accumulating information into a global variable. To avoid a race condition, every access to this global variable would have to be protected by a mutex. Instead, each thread might accumulate into a thread local variable, thereby eliminating any possibility of a race condition, thereby removing the need for locking. The threads then only have to synchronise a final accumulation from their own thread local variable into a single global variable.
Windows-ты іске асыру
Қолданбалы бағдарламалау интерфейсі (API) функциясы TlsAlloc қолданылмаған TLS слот индексін алу үшін пайдаланылуы мүмкін; бұл кезде TLS слот индексі «қолданылған» деп есептеледі. TlsGetValue және TlsSetValue функциялары TLS слот индексімен сәйкестендірілген, жіпке арналған жергілікті айнымалыға жад адресін оқу және жазу үшін қолданылады. TlsSetValue тек ағымдағы жіп үшін ғана айнымалыға әсер етеді. TLS слот индексін босату үшін TlsFree функциясын шақыруға болады. Әрбір жіп үшін Win32 жіп ақпарат блогы болады. Бұл блок ішіндегі жазбалардың бірі – сол жіпке арналған жіпке арналған жергілікті сақтау кестесі. TlsAlloc әр шақыру үшін адрес кеңістігіне сәйкес келетін, осы кестеге бірегей индекс қайтарады. Әрбір жіптің өз жеке жіпке арналған жергілікті сақтау кестесінің көшірмесі болады. Сондықтан, әрбір жіп TlsSetValue(индекс) функциясын тәуелсіз түрде пайдалана алады және TlsGetValue(индекс) арқылы көрсетілген мәнді ала алады, себебі бұл функциялар жіптің өзінің кестесінде жазуды орнату және іздеуді жүзеге асырады. TlsXxx функциялар тобынан басқа, Windows атқарылатын файлдары процестің әрбір жіпі үшін әртүрлі бетке бейімделген бөлімді анықтай алады. TlsXxx мәндерінен айырмашылығы, бұл беттер кез келген және жарамды адрестерді қамтуы мүмкін. Дегенмен, бұл адрестер әрбір атқарылатын жіп үшін әртүрлі болады, сондықтан оларды асинхронды функцияларға (басқа жіпте орындалуы мүмкін) немесе виртуалды адрестің бүкіл процесс ішінде бірегей деп есептейтін кодқа жібермеу керек. TLS бөлімдері жадты беттеу арқылы басқарылады және олардың мөлшері бет мөлшеріне дейін квантталады (x86 машиналарын үшін 4кБ). Мұндай бөлімдерді бағдарламаның негізгі атқарылатын файлында ғана анықтауға болады, себебі олар LoadLibrary арқылы жүктелгенде дұрыс инициализацияланбайды.
The application programming interface (API) function TlsAlloc can be used to obtain an unused TLS slot index; the TLS slot index will then be considered 'used'. The TlsGetValue and TlsSetValue functions are then used to read and write a memory address to a thread local variable identified by the TLS slot index. TlsSetValue only affects the variable for the current thread. The TlsFree function can be called to release the TLS slot index. There is a Win32 Thread Information Block for each thread. One of the entries in this block is the thread local storage table for that thread. TlsAlloc returns an index to this table, unique per address space, for each call. Each thread has its own copy of the thread local storage table. Hence, each thread can independently use TlsSetValue(index) and obtain the specified value via TlsGetValue(index), because these set and look up an entry in the thread's own table. Apart from TlsXxx function family, Windows executables can define a section which is mapped to a different page for each thread of the executing process. Unlike TlsXxx values, these pages can contain arbitrary and valid addresses. These addresses, however, are different for each executing thread and therefore should not be passed to asynchronous functions (which may execute in a different thread) or otherwise passed to code which assume that a virtual address is unique within the whole process. TLS sections are managed using memory paging and its size is quantized to a page size (4kB on x86 machines). Such sections may only be defined inside a main executable of a program DLLs should not contain such sections, because they are not correctly initialized when loading with LoadLibrary.
P3 тармақтарды іске асыру
Pthreads API-де, жіпке жергілікті жад, "жіпке тән деректер" терминімен белгіленеді. `pthread key create` және `pthread key delete` функциялары сәйкесінше, жіпке тән деректерге кілт жасау және жою үшін қолданылады. Кілттің түрі нақты түрде жасырын қалдырылған және `pthread key t` деп аталады. Бұл кілтты барлық жіптер көре алады. Әрбір жіпте кілтты `pthread setspecific` арқылы жіпке тән деректермен байланыстыруға болады. Деректерді кейіннен `pthread getspecific` көмегімен алуға болады. Сонымен қатар, `pthread key create` опциялық түрде деструктор функциясын қабылдауы мүмкін, егер жіпке тән деректер NULL болмаса, жіп аяқталғанда автоматты түрде шақырылады. Деструктор параметр ретінде кілтқа байланысты мәнді қабылдайды, сондықтан ол тазалау әрекеттерін (қосылыстарды жабу, жадты босату және т.б.) орындай алады. Деструктор көрсетілген кезде де, бағдарлама процестік деңгейде жіпке тән деректерді босату үшін `pthread key delete` шақыруы керек (деструктор тек жіпке жергілікті деректерді ғана босатады).
In the Pthreads API, memory local to a thread is designated with the term Thread specific data. The functions pthread key create and pthread key delete are used respectively to create and delete a key for thread specific data. The type of the key is explicitly left opaque and is referred to as pthread key t. This key can be seen by all threads. In each thread, the key can be associated with thread specific data via pthread setspecific. The data can later be retrieved using pthread getspecific. In addition pthread key create can optionally accept a destructor function that will automatically be called at thread exit, if the thread specific data is not NULL. The destructor receives the value associated with the key as parameter so it can perform cleanup actions (close connections, free memory, etc.). Even when a destructor is specified, the program must still call pthread key delete to free the thread specific data at process level (the destructor only frees the data local to the thread).
Тілге тән іске асыру
Бағдарламашылардың тиісті API функцияларын шақыруына сүйенуден басқа, бағдарламалау тілін жіптік жергілікті сақтауды (TLS) қолдау үшін кеңейту де мүмкін.
Apart from relying on programmers to call the appropriate API functions, it is also possible to extend the programming language to support thread local storage (TLS).