Como criar um semáforo no bash

Conteúdo

Bash Shell

Você está desenvolvendo um aplicativo multithread? Tarde ou cedo, você provavelmente precisará usar um semáforo. Neste artigo, você aprenderá o que é um semáforo, como criar / pratique um em bash e mais.

Que e um Luzes de trânsito?

Um semáforo é uma construção de programação usada em programas de computador que empregam vários threads de processamento. (threads de processamento de computador executando código-fonte do mesmo programa ou conjunto de programas) para obter o uso exclusivo de um recurso comum a qualquer momento. . Dito de uma maneira muito mais simples, pense nisso como “um ao mesmo tempo, por favor”.

Um semáforo foi definido pela primeira vez no final dos anos 1990. 1960 pelo falecido cientista da computação Edsger Dijkstra de Rotterdam, na Holanda. Você provavelmente já usou semáforos com frequência em sua vida, sem perceber especificamente que estava fazendo isso!!

Fique na Holanda por um tempo, um país cheio de pequenos cursos de água e muitas pontes móveis (chamado Ponte levadiça em inglês americano), você pode ver muitos exemplos do mundo real de um semáforo; considere a alça de um operador de ponte levadiça: Para cima ou para baixo. Essa alça é a variável do semáforo que protege a hidrovia ou rodovia contra acidentes. A) Sim, a hidrovia e as estradas podem ser vistas como outras variáveis ​​protegidas pelo semáforo.

Se a maçaneta estiver levantada e a ponte aberta, o uso exclusivo da água da interseção / estrada é dada ao navio ou navios que passam pelo canal de água. Quando a manivela está abaixada, a ponte é fechada e o uso exclusivo da água da interseção é dado / estrada para carros passando pela ponte.

A variável semáforo pode controlar o acesso a outro conjunto de variáveis. Como um exemplo, a condição da alça evita que $cars_per_minute e $car_toll_booth_total_income variáveis ​​para que não sejam atualizadas, etc.

Podemos levar o exemplo um pouco mais adiante e explicar que quando o manípulo funciona para cima ou para baixo, os capitães dos barcos na água e as pessoas que dirigem carros e caminhões na estrada podem ver uma luz correspondente: todos os operadores podem ler uma variável comum para um estado específico.

Mesmo que o cenário descrito aqui não seja apenas um semáforo, mas ao mesmo tempo é um mutex simples. Um mutex é outra construção de programação comum que é muito semelhante a um semáforo, com a condição adicional de que um mutex só pode ser desbloqueado pela mesma tarefa ou thread que o bloqueou. Significado mutex “Mutualmente exclusivo”.

Para este caso, isso se aplica ao nosso exemplo, uma vez que o operador de bridge é o único com controle sobre nosso semáforo e mutex up / baixa. Pelo contrário, se a guarita no final da ponte tivesse um interruptor de cancelamento de ponte, ainda teríamos uma configuração de semáforo, mas não um mutex.

Ambas as construções são usadas regularmente em programação de computador ao usar vários threads para garantir que apenas um único procedimento ou tarefa acesse um determinado recurso a qualquer momento. Alguns semáforos podem ser de comutação ultrarrápida, como um exemplo, quando usado em software de negociação multi-threaded do mercado financeiro, e alguns podem ser muito mais lentos, eles só mudam de estado a cada poucos minutos, como quando usado em uma ponte levadiça automatizada ou em um cruzamento de ferrovia.

Agora que entendemos melhor os semáforos, vamos implementar um no bash.

Implementar um semáforo no Bash: ¿fácil o no?

Implementar um semáforo no Bash é tão fácil que pode até ser feito diretamente da linha de comando, Ou assim parece …

Comencemos de forma simples.

BRIDGE=up
if [ "${PONTE}" = "abaixo" ]; então eco "Carros podem passar!"; mais eco "Navios podem passar!"; fi
BRIDGE=down
if [ "${PONTE}" = "abaixo" ]; então eco "Carros podem passar!"; mais eco "Navios podem passar!"; ser

Exemplo de linha de comando de atualização e saída do status da ponte

Neste código, a variável BRIDGE tem nosso status de ponte. Quando colocamos up, Os navios podem passar e quando o colocamos down, carros podem passar. Ao mesmo tempo, podemos ler o valor de nossa variável em qualquer ponto para ver se a ponte está realmente para cima ou para baixo.. O compartilhamento / comum, nesta circunstância, é a nossa ponte.

Apesar dele, este exemplo é de encadeamento único e, É por isso que, nunca encontramos comoajude os sindicatos situação. Outra maneira de pensar sobre isso é que nossa variável nunca pode ser up e down exatamente ao mesmo tempo que o código é executado sequencialmente, em outras palavras, passo a passo.

Outra coisa a se prestar atenção é que não controlamos realmente o acesso a outra variável (como um semáforo faria regularmente), então nosso BRIDGE variável não é verdadeiramente uma variável semáforo verdadeira, mesmo quando se aproxima.

Em resumo, assim que apresentarmos vários tópicos que podem afetar o BRIDGE variável encontramos problemas. Como um exemplo, e sim, logo depois BRIDGE=up comando, outro problema de discussão BRIDGE=down que mais tarde resultaria na mensagem Cars may pass! Saída, mesmo que o primeiro segmento esperasse que a ponte fosse up, e na verdade a ponte ainda está se movendo. Perigoso!

Você pode ver como as coisas podem ficar rapidamente turvas e confusas, para não mencionar complexo, ao trabalhar com tópicos suficientes.

A situação em que vários encadeamentos tentam atualizar a mesma variável ao mesmo tempo ou pelo menos perto o suficiente para outro encadeamento bagunçar a situação (que no caso de pontes levadiças pode demorar muito) se denomina condição de corrida: dois tópicos competindo para atualizar ou relatar alguma variável ou status, com o resultado que um ou mais tópicos podem dar errado.

Podemos melhorar muito este código fluindo o código em procedimentos e usando uma variável semáforo real que restringirá o acesso ao nosso BRIDGE variável dependendo da situação.

Criação de um semáforo bash

Implementar um multithreaded completo, torná-lo seguro (uma definição de computação para descrever o software que é seguro para thread ou desenvolvido de tal forma que os threads não podem afetar negativa ou incorretamente uns aos outros quando não deveriam) Não é uma tarefa fácil. Mesmo um programa bem escrito que usa semáforos não é absolutamente seguro para threads..

Quanto mais tópicos houver, e quanto maior a frequência e complexidade das interações entre threads, as condições de corrida são mais prováveis.

Para nosso pequeno exemplo, veremos a definição de um semáforo Bash quando um dos operadores de ponte levadiça abaixa a alça da ponte, indicando que você deseja abaixar a ponte. Los lectores ávidos pueden haber notado la referencia a operadors ao invés de operador: ahora hay varios operadores que pueden bajar el puente. Em outras palavras, hay varios subprocesos o tareas que se ejecutan al mismo tiempo.

#!/bin/bash

BRIDGE_SEMAPHORE=0

lower_bridge(){  # Um operador colocou uma das alças da operação da ponte para baixo (como um novo estado).
  # Suponha que foi previamente acordado entre operadores que assim que um dos operadores 
  # move uma alça operação ponte que seu comando tem que ser executado, mais cedo ou mais tarde
  # daqui, we commence a loop which will wait for the bridge to become available for movement
  while true; do
    if [ "${BRIDGE_SEMAPHORE}" -Eq 1 ]; then
      echo "Ponte semafórica bloqueada, ponte em movimento ou outra questão. Espera 2 minutos antes de re-verificar."
      dormir 120
      continuar  # Continue loop
    elif [ "${BRIDGE_SEMAPHORE}" -Eq 0 ]; then   
      echo "Comando de ponte inferior aceito, travando semáforo e baixando a ponte."
      BRIDGE_SEMAPHORE=1
      execute_lower_bridge
      wait_for_bridge_to_come_down
      BRIDGE='down'
      echo "Ponte baixada, garantindo, pelo menos, 5 minutos passam antes do próximo movimento permitido ponte."
      dormir 300
      eco "5 Minutos se passaram, desbloqueio semafórico (liberando o controle da ponte)"
      BRIDGE_SEMAPHORE=0
      break  # Exit loop
    fi
  done
}

Una implementación de semáforo en Bash

Aquí tenemos un lower_bridge función que hará una serie de cosas. Em primeiro lugar, suponha que outro operador mudou recentemente a ponte no último minuto. Como tal, há outro thread que executa código em uma função semelhante a esta chamada raise_bridge.

Na realidade, essa função terminou de elevar a ponte, mas instituiu uma espera obrigatória de 5 minutos que todos os operadores concordaram com antecedência e que foram codificados no código-fonte: evitar que a ponte suba / desce o tempo todo. Ao mesmo tempo, você pode ver esta espera obrigatória de 5 minutos implementados nesta função como sleep 300.

Então, quando isso raise_bridge a função está operando, terá definido a variável semáforo BRIDGE_SEMAPHORE uma 1, da mesma forma que fazemos no código aqui (logo depois echo "Lower bridge command accepted, locking semaphore and lowering bridge" comando), e – através do primeiro if verificação condicional neste código: o loop infinito presente nesta função continuará (ref continue No código) fazer loop, com pausas de 2 minutos, como ele BRIDGE_SEMAPHORE variável é 1.

Assim que isso raise_bridge O espetáculo termina de levantar a ponte e encerra a suspensão de cinco minutos, irá estabelecer o BRIDGE_SEMAPHORE uma 0, permitindo nosso lower_bridge co função começa a executar funções execute_lower_bridge e subsequente wait_for_bridge_to_come_down ao mesmo tempo que bloqueamos nosso semáforo novamente para 1 para evitar que outras funções assumam a ponte.

Apesar dele, existem lacunas neste código e possíveis condições de corrida que podem ter consequências de longo alcance para os operadores de ponte. Você pode ver algum?

a "Lower bridge command accepted, locking semaphore and lowering bridge" não é seguro para discussão.

Se outro tópico, como um exemplo raise_bridge está sendo executado ao mesmo tempo e tentando acessar o BRIDGE_SEMAPHORE variável, poderia ser (quando BRIDGE_SEMAPHORE=0 e ambos os threads em execução alcançam seus respectivos echoÉ exatamente ao mesmo tempo que os operadores de ponte veem “O comando da ponte inferior é aceito, o semáforo está bloqueado e a ponte é baixada” e “Comando de elevador de ponte aceito, o semáforo está bloqueado e a ponte é levantada”. Diretamente um após o outro na tela! Com medo, não?

Ainda mais assustador é o fato de que ambos os tópicos podem continuar BRIDGE_SEMAPHORE=1, E ambos os threads podem continuar a funcionar! (Não há nada que os impeça de fazer isso) A razão é que ainda não há muita proteção para esses cenários. Mesmo que este código implemente um semáforo, não é de forma alguma thread-safe. Como foi dito, a codificação multi-thread é complexa e requer muita experiência.

Mesmo que o tempo necessário nesta circunstância seja mínimo (1-2 linhas de código levam apenas alguns milissegundos para serem executadas), e dado o provavelmente baixo número de operadores de ponte, a chance de isso acontecer é muito pequena. Apesar dele, o fato de ser viável é o que o torna perigoso. Criar código thread-safe no Bash não é uma tarefa fácil.

Isso poderia ser melhorado ainda mais, como um exemplo, introdução de um pré-bloqueio e / ou introduzir algum tipo de atraso com uma nova verificação subsequente (mesmo que isso provavelmente exija uma variável adicional) ou fazer uma verificação regular antes da execução real da ponte. , etc. Outra alternativa é fazer uma fila de prioridade ou uma variável de contador que verifica quantos threads bloquearam o controle da ponte, etc.

Outra abordagem comumente usada, como um exemplo, ao executar vários scripts bash que podem interagir, é usar mkdir o flock como operações de bloqueio de base. Existem vários exemplos de como implementá-los disponíveis online, como um exemplo, Quais comandos Unix podem ser usados ​​como semáforo / bloqueio?.

Final

Neste artigo, nós damos uma olhada no que é um semáforo. Ao mesmo tempo, tocamos brevemente no tema de um mutex. Em resumo, analisamos a implementação de um semáforo em Bash usando o exemplo prático de vários operadores de ponte operando uma ponte móvel / Ponte levadiça. Ao mesmo tempo, exploramos o quão complexa é a implementação de uma solução confiável baseada em semáforos.

Se você gostou de ler este artigo, dê uma olhada em nossas reivindicações, erros e travamentos: qual é a diferença? Artigo.

Assine a nossa newsletter

Nós não enviaremos SPAM para você. Nós odiamos isso tanto quanto você.