The translation is temporarily closed for contributions due to maintenance, please come back later.

Translation

Threaded programs should be written with as little contention on locks as possible. Otherwise, instead of doing useful work the thread just waits on a lock. BecauseAs a result of this, the most well written threaded programs show little locks contention.
(itstool) path: sect3/para
English
Threaded programs should be written with as little contention on locks as possible. Otherwise, instead of doing useful work the thread just waits on a lock. As a result of this, the most well written threaded programs show little locks contention.
Context English Portuguese (Brazil) State
Threads in computer science are entities within a process that can be scheduled independently from each other. The threads in the process share process wide data (file descriptors, etc.) but also have their own stack for their own data. Sometimes there is a need for process-wide data specific to a given thread. Imagine a name of the thread in execution or something like that. The traditional <trademark class="registered">UNIX</trademark> threading API, <application>pthreads</application> provides a way to do it via <citerefentry><refentrytitle>pthread_key_create</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>pthread_setspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> and <citerefentry><refentrytitle>pthread_getspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> where a thread can create a key to the thread local data and using <citerefentry><refentrytitle>pthread_getspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> or <citerefentry><refentrytitle>pthread_getspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> to manipulate those data. You can easily see that this is not the most comfortable way this could be accomplished. So various producers of C/C++ compilers introduced a better way. They defined a new modifier keyword thread that specifies that a variable is thread specific. A new method of accessing such variables was developed as well (at least on i386). The <application>pthreads</application> method tends to be implemented in userspace as a trivial lookup table. The performance of such a solution is not very good. So the new method uses (on i386) segment registers to address a segment, where TLS area is stored so the actual accessing of a thread variable is just appending the segment register to the address thus addressing via it. The segment registers are usually <varname>%gs</varname> and <varname>%fs</varname> acting like segment selectors. Every thread has its own area where the thread local data are stored and the segment must be loaded on every context switch. This method is very fast and used almost exclusively in the whole i386 <trademark class="registered">UNIX</trademark> world. Both FreeBSD and <trademark class="registered">Linux</trademark> implement this approach and it yields very good results. The only drawback is the need to reload the segment on every context switch which can slowdown context switches. FreeBSD tries to avoid this overhead by using only 1 segment descriptor for this while <trademark class="registered">Linux</trademark> uses 3. Interesting thing is that almost nothing uses more than 1 descriptor (only <application>Wine</application> seems to use 2) so <trademark class="registered">Linux</trademark> pays this unnecessary price for context switches. Threads na ciência da computação são entidades com um processo que podem ser agendados independentemente de qualquer outro. As threads nos processos compartilham amplos dados de processos (file descriptors, etc.) mas também tem sua prŕopria pilha para seus próprios dados. Algumas vezes é preciso para um processamento amplo de dados dado uma thread. Imagine um nome de uma thread algo assim. A tradicional API de threading do <trademark class="registered">UNIX</trademark>, <application>pthreads</application> prove um caminho para isso em <citerefentry><refentrytitle>pthread_key_create</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>pthread_setspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> and <citerefentry><refentrytitle>pthread_getspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> onde a thread pode criar uma chave para os dados da thread local <citerefentry><refentrytitle>pthread_getspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> ou <citerefentry><refentrytitle>pthread_getspecific</refentrytitle><manvolnum>3</manvolnum></citerefentry> para manipular esses dados. Você pode ver que esse não é o caminho mais confortavel que poderia ser usado. Então varios produtores de compiladores C/C++ introduziram um caminho melhor. Eles definiram uma nova chave modificadora de thread que especifica que a variavel é especifica de uma thread. Um novo método de acessar as variaveis foi desenvolvio como (ao menos no i386). O método <application>pthreads</application> tende a ser implementado no espaço de usuário como uma tabela de lookup trivial. A performance como uma solução não é muito boa. Então o novo método (no i386) registradores de segmentos para endereçar um segmento, onde a área do TLS é armazenada, então o atual acesso da variável de uma thread é apenas adicionada ao registrador de segmentos para o endereçamento via it. Os registradores de segmentos são usualmente <varname>%gs</varname> e <varname>%fs</varname> agindo como seletores de segmento. Toda thread tem sua própria área onde os dados da thread local são armazenados e o segmento deve ser carregado em toda troca de contexto. Esse método é muito rapido e usado em todo mundo em volta do <trademark class="registered">UNIX</trademark> i386. Ambos FreeBSD e <trademark class="registered">Linux</trademark> Implementam sua abordagem e seus resultados tem sido muito bons. Unico ponto negativo é ter que recarregar o segmento em toda troca de contexto que pode deixar o processo lento. FreeBSD tenta evitar essa sobrecarga usando apenas 1 descritor de segmento enquanto <trademark class="registered">Linux</trademark> usa 3. Interessante que isso quase nunca usa mais que 1 descritor (apenas o <application>Wine</application> parece usar 2) então o <trademark class="registered">Linux</trademark> paga esse preço desnecessário na troca de contexto.
Segments on i386 Segmentos em i386
The i386 architecture implements the so called segments. A segment is a description of an area of memory. The base address (bottom) of the memory area, the end of it (ceiling), type, protection, etc. The memory described by a segment can be accessed using segment selector registers (<varname>%cs</varname>, <varname>%ds</varname>, <varname>%ss</varname>, <varname>%es</varname>, <varname>%fs</varname>, <varname>%gs</varname>). For example let us suppose we have a segment which base address is 0x1234 and length and this code: A arquitetura i386 implementa os então chamados segmentos.Um segmento é uma descrição de um espaço na memória. A base de endereço (baixa) na area da memória, o fim disso (teto), tipo, proteção, etc. A memória descrita por um segmento pode ser acessada usando um seletor de segmento (<varname>%cs</varname>, <varname>%ds</varname>, <varname>%ss</varname>, <varname>%es</varname>, <varname>%fs</varname>, <varname>%gs</varname>). Por exemplo, deixe nos supor que temos um segmento com base no endereço 0x1234 e comprimento e esse codigo:
mov %edx,%gs:0x10 mov %edx,%gs:0x10
This will load the content of the <varname>%edx</varname> register into memory location 0x1244. Some segment registers have a special use, for example <varname>%cs</varname> is used for code segment and <varname>%ss</varname> is used for stack segment but <varname>%fs</varname> and <varname>%gs</varname> are generally unused. Segments are either stored in a global GDT table or in a local LDT table. LDT is accessed via an entry in the GDT. The LDT can store more types of segments. LDT can be per process. Both tables define up to 8191 entries. Isso carregará o conteúdo do registro <varname>% edx</varname> na localização da memória 0x1244. Alguns registradores de segmento têm um uso especial, por exemplo <varname>% cs</varname> é usado para segmento de código e <varname>% ss</varname> é usado para o segmento de pilha, mas <varname>% fs</varname> e <varname>% gs</varname> geralmente não são usados. Os segmentos são armazenados em uma tabela GDT global ou em uma tabela LDT local. O LDT é acessado por meio de uma entrada no GDT. O LDT pode armazenar mais tipos de segmentos. LDT pode ser por processo. Ambas as tabelas definem até 8191 entradas.
Implementation on <trademark class="registered">Linux</trademark> i386 Implementação no <trademark class="registered">Linux</trademark> i386
There are two main ways of setting up TLS in <trademark class="registered">Linux</trademark>. It can be set when cloning a process using the <function>clone</function> syscall or it can call <function>set_thread_area</function>. When a process passes <literal>CLONE_SETTLS</literal> flag to <function>clone</function>, the kernel expects the memory pointed to by the <varname>%esi</varname> register a <trademark class="registered">Linux</trademark> user space representation of a segment, which gets translated to the machine representation of a segment and loaded into a GDT slot. The GDT slot can be specified with a number or -1 can be used meaning that the system itself should choose the first free slot. In practice, the vast majority of programs use only one TLS entry and does not care about the number of the entry. We exploit this in the emulation and in fact depend on it. Existem duas maneiras principais de configurar o TLS no <trademark class="registered">Linux</trademark>. Pode ser definido ao clonar um processo usando a syscall <function>clone</function> ou ele pode chamar <function>set_thread_area</function>. Quando um processo passa a flag <literal>CLONE_SETTLS</literal> para <function>clone</function>, o kernel espera que a memória apontada pelo registrador <varname>% esi</varname> uma representação <trademark class = "registered">Linux</trademark> do espaço do usuário de um segmento, que é traduzido para a representação da máquina de um segmento e carregado em um slot GDT. O slot GDT pode ser especificado com um número ou -1 pode ser usado, o que significa que o próprio sistema deve escolher o primeiro slot livre. Na prática, a grande maioria dos programas usa apenas uma entrada de TLS e não se importa com o número da entrada. Nós exploramos isso na emulação e dependemos disso.
Emulation of <trademark class="registered">Linux</trademark> TLS Emulação de TLS do <trademark class="registered">Linux</trademark>
i386 i386
Loading of TLS for the current thread happens by calling <function>set_thread_area</function> while loading TLS for a second process in <function>clone</function> is done in the separate block in <function>clone</function>. Those two functions are very similar. The only difference being the actual loading of the GDT segment, which happens on the next context switch for the newly created process while <function>set_thread_area</function> must load this directly. The code basically does this. It copies the <trademark class="registered">Linux</trademark> form segment descriptor from the userland. The code checks for the number of the descriptor but because this differs between FreeBSD and <trademark class="registered">Linux</trademark> we fake it a little. We only support indexes of 6, 3 and -1. The 6 is genuine <trademark class="registered">Linux</trademark> number, 3 is genuine FreeBSD one and -1 means autoselection. Then we set the descriptor number to constant 3 and copy out this to the userspace. We rely on the userspace process using the number from the descriptor but this works most of the time (have never seen a case where this did not work) as the userspace process typically passes in 1. Then we convert the descriptor from the <trademark class="registered">Linux</trademark> form to a machine dependant form (i.e. operating system independent form) and copy this to the FreeBSD defined segment descriptor. Finally we can load it. We assign the descriptor to threads PCB (process control block) and load the <varname>%gs</varname> segment using <function>load_gs</function>. This loading must be done in a critical section so that nothing can interrupt us. The <literal>CLONE_SETTLS</literal> case works exactly like this just the loading using <function>load_gs</function> is not performed. The segment used for this (segment number 3) is shared for this use between FreeBSD processes and <trademark class="registered">Linux</trademark> processes so the <trademark class="registered">Linux</trademark> emulation layer does not add any overhead over plain FreeBSD. O carregamento de TLS para o segmento atual acontece chamando <function>set_thread_area</function> enquanto o TLS é carregado para um segundo processo em <function>clone</function> é feito no bloco separado em <function>clone</function>. Essas duas funções são muito semelhantes. A única diferença é o carregamento real do segmento GDT, que acontece na próxima troca de contexto para o processo recém-criado, enquanto <function>set_thread_area</function> deve carregar isso diretamente. O código basicamente faz isso. Ele copia o descritor de segmento de formulário <trademark class="registered">Linux</trademark> da área de usuário. O código verifica o número do descritor, mas como isso difere entre o FreeBSD e o <trademark class="registered">Linux</trademark>, maquiamos um pouco. Nós suportamos apenas índices de 6, 3 e -1. O número 6 é genuíno do <trademark class="registered">Linux</trademark>, 3 é genuíno do FreeBSD one e -1 significa uma auto seleção. Em seguida, definimos o número do descritor como constante 3 e copiamos isso para o espaço do usuário. Contamos com o processo em espaço de usuário usando o número do descritor, mas isso funciona na maior parte do tempo (nunca vi um caso em que isso não funcionou), como o processo em espaço de usuário normalmente passa em 1. Então, convertemos o descritor da classe do <trademark class="registered">Linux</trademark> para um formulário dependente da máquina (isto é, independente do sistema operacional) e copie isto para o descritor de segmento definido pelo FreeBSD. Finalmente podemos carregá-lo. Atribuímos o descritor às threads PCB (bloco de controle de processo) e carregamos o segmento <varname>% gs</varname> usando <function>load_gs</function>. Este carregamento deve ser feito em uma seção crítica para que nada possa nos interromper. O caso <literal>CLONE_SETTLS</literal> funciona exatamente como este, apenas o carregamento usando <function>load_gs</function> não é executado. O segmento usado para isso (segmento número 3) é compartilhado para este uso entre os processos do FreeBSD e do <trademark class="registered">Linux</trademark> para que a camada de emulação <trademark class="registered">Linux</trademark> não adicione nenhuma sobrecarga sobre o FreeBSD.
amd64 amd64
The amd64 implementation is similar to the i386 one but there was initially no 32bit segment descriptor used for this purpose (hence not even native 32bit TLS users worked) so we had to add such a segment and implement its loading on every context switch (when a flag signaling use of 32bit is set). Apart from this the TLS loading is exactly the same just the segment numbers are different and the descriptor format and the loading differs slightly. A implementação do amd64 é semelhante à do i386, mas inicialmente não havia um descritor de segmento de 32 bits usado para esse propósito (por isso nem usuários nativos de TLB de 32 bits trabalhavam), então tivemos que adicionar esse segmento e implementar seu carregamento em cada troca de contexto (quando a flag sinalizando uso de 32 bits está definida). Além disso, o carregamento de TLS é exatamente o mesmo, apenas os números de segmento são diferentes e o formato do descritor e o carregamento diferem ligeiramente.
Futexes Futexes
Introduction to synchronization Introdução à sincronização
Threads need some kind of synchronization and <trademark class="registered">POSIX</trademark> provides some of them: mutexes for mutual exclusion, read-write locks for mutual exclusion with biased ratio of reads and writes and condition variables for signaling a status change. It is interesting to note that <trademark class="registered">POSIX</trademark> threading API lacks support for semaphores. Those synchronization routines implementations are heavily dependant on the type threading support we have. In pure 1:M (userspace) model the implementation can be solely done in userspace and thus be very fast (the condition variables will probably end up being implemented using signals, i.e. not fast) and simple. In 1:1 model, the situation is also quite clear - the threads must be synchronized using kernel facilities (which is very slow because a syscall must be performed). The mixed M:N scenario just combines the first and second approach or rely solely on kernel. Threads synchronization is a vital part of thread-enabled programming and its performance can affect resulting program a lot. Recent benchmarks on FreeBSD operating system showed that an improved sx_lock implementation yielded 40% speedup in <firstterm>ZFS</firstterm> (a heavy sx user), this is in-kernel stuff but it shows clearly how important the performance of synchronization primitives is. Threads precisam de algum tipo de sincronização e <trademark class="registered">POSIX</trademark> fornece alguns deles: mutexes para exclusão mútua, bloqueios de leitura/gravação para exclusão mútua com relação de polarização de leituras e gravações e variáveis de condição para sinalizar um mudança de status. É interessante observar que a API de thread <trademark class="registered">POSIX</trademark> não tem suporte para semáforos. Essas implementações de rotinas de sincronização são altamente dependentes do tipo de suporte a threading que temos. No modelo puro 1:M (espaço de usuário), a implementação pode ser feita apenas no espaço do usuário e, portanto, ser muito rápida (as variáveis de condição provavelmente serão implementadas usando sinais, ou seja, não rápido) e simples. No modelo 1:1, a situação também é bastante clara - as threading devem ser sincronizadas usando as facilidades do kernel (o que é muito lento porque uma syscall deve ser executada). O cenário M:N misto combina apenas a primeira e a segunda abordagem ou depende apenas do kernel. A sincronização de threads é uma parte vital da programação ativada por threads e seu desempenho pode afetar muito o programa resultante. Benchmarks recentes no sistema operacional FreeBSD mostraram que uma implementação sx_lock melhorada gerou 40% de aceleração no <firstterm>ZFS</firstterm> (um usuário sx pesado), isso é algo in-kernel, mas mostra claramente quão importante é o desempenho das primitivas de sincronização. .
Threaded programs should be written with as little contention on locks as possible. Otherwise, instead of doing useful work the thread just waits on a lock. As a result of this, the most well written threaded programs show little locks contention. Os programas em threading devem ser escritos com o mínimo de contenção possível em bloqueios. Caso contrário, em vez de fazer um trabalho útil, a threading apenas espera em um bloqueio. Devido a isso, os programas encadeados mais bem escritos mostram pouca contenção de bloqueios.
Futexes introduction Introdução a Futexes
<trademark class="registered">Linux</trademark> implements 1:1 threading, i.e. it has to use in-kernel synchronization primitives. As stated earlier, well written threaded programs have little lock contention. So a typical sequence could be performed as two atomic increase/decrease mutex reference counter, which is very fast, as presented by the following example: O <trademark class="registered">Linux</trademark> implementa a segmentação 1:1, ou seja, tem de utilizar primitivas de sincronização no kernel. Como afirmado anteriormente, programas encadeados bem escritos possuem pouca contenção de bloqueio. Assim, uma sequência típica poderia ser executada como dois contador de referência de mutex de aumento/redução atômico, que é muito rápido, conforme apresentado pelo exemplo a seguir:
pthread_mutex_lock(&amp;mutex);
....
pthread_mutex_unlock(&amp;mutex);
pthread_mutex_lock(&amp;mutex);
....
pthread_mutex_unlock(&amp;mutex);
1:1 threading forces us to perform two syscalls for those mutex calls, which is very slow. O threading 1:1 nos força a executar dois syscalls para as chamadas mutex, o que é muito lento.
The solution <trademark class="registered">Linux</trademark> 2.6 implements is called futexes. Futexes implement the check for contention in userspace and call kernel primitives only in a case of contention. Thus the typical case takes place without any kernel intervention. This yields reasonably fast and flexible synchronization primitives implementation. A solução que o <trademark class="registered">Linux</trademark> 2.6 implementa é chamada de futexes. Futexes implementam a verificação de contenção no espaço do usuário e chama primitivas do kernel apenas em um caso de contenção. Assim, o caso típico ocorre sem qualquer intervenção do kernel. Isso produz uma implementação de primitivas de sincronização razoavelmente rápida e flexível.
Futex API API do Futex
The futex syscall looks like this: A syscall do futex é assim:
int futex(void *uaddr, int op, int val, struct timespec *timeout, void *uaddr2, int val3); int futex(void *uaddr, int op, int val, struct timespec *timeout, void *uaddr2, int val3);
In this example <varname>uaddr</varname> is an address of the mutex in userspace, <varname>op</varname> is an operation we are about to perform and the other parameters have per-operation meaning. Neste exemplo <varname>uaddr</varname> é um endereço do mutex no espaço do usuário, <varname>op</varname> é uma operação que estamos prestes a executar e os outros parâmetros têm significado por operação.
Futexes implement the following operations: Futexes implementam as seguintes operações:
<literal>FUTEX_WAIT</literal> <literal>FUTEX_WAIT</literal>
<literal>FUTEX_WAKE</literal> <literal>FUTEX_WAKE</literal>
<literal>FUTEX_FD</literal> <literal>FUTEX_FD</literal>
<literal>FUTEX_REQUEUE</literal> <literal>FUTEX_REQUEUE</literal>
<literal>FUTEX_CMP_REQUEUE</literal> <literal>FUTEX_CMP_REQUEUE</literal>

Loading…

Threaded programs should be written with as little contention on locks as possible. Otherwise, instead of doing useful work the thread just waits on a lock. BecauseAs a result of this, the most well written threaded programs show little locks contention.
a month ago
Browse all component changes

Glossary

English Portuguese (Brazil)
No related strings found in the glossary.

Source information

Source string comment
(itstool) path: sect3/para
Source string location
article.translate.xml:2000
String age
a month ago
Source string age
a month ago
Translation file
articles/pt_BR/linux-emulation.po, string 289