1 \documentclass[a4paper,10pt]{abnt} %
2 \usepackage[utf8]{inputenc}
3 \usepackage[T1]{fontenc}
4 \usepackage[brazil]{babel}
10 %\author{Kauê Silveira \\171671 \and Silveira Kauê \\ 671171}
12 \autor{Kaue Soares da Silveira \\ 171671} %\and
13 \instituicao{Universidade Federal do Rio Grande do Sul}
14 \orientador[Professor:]{Alexandre Carissimi}
15 \titulo{Trabalho Opcional 1: Estudo da Linguagem Golang}
16 \comentario{Disciplina INF01151 - Sistemas Operacionais II N}
19 \definecolor{gray}{rgb}{0.5, 0.5, 0.5}
20 \definecolor{darkBlue}{rgb}{0.15,0.18,0.40}
21 \definecolor{darkGreen}{rgb}{0.15,0.40,0.18}
22 \lstset{extendedchars=true,
24 basicstyle=\small\color{blue},
25 keywordstyle=\color{red}\bfseries,
26 keywordstyle= [2]\color{magenta}\bfseries,
27 identifierstyle=\color{blue},
28 commentstyle=\color{gray}\textit,
29 stringstyle=\color{darkGreen},
30 showstringspaces=false,
35 numbers=left, % Números na margem da esquerda
36 numberstyle=\tiny\color{darkGreen}, % Números pequenos
37 stepnumber=1, % De 1 em 1
38 numbersep=5pt % Separados por 5pt
40 \lstdefinelanguage{Go}{%
41 morekeywords=[1]{func, int, chan, package, import, int64, var, const, type, struct, byte},% blocks
42 morekeywords=[2]{make, go, select, for, case, if, then, else, len, cap, break},%
43 morekeywords=[3]{idvar,var,->,as},% variables
44 morekeywords=[4]{lexical,sum,join},% aggregation/sorting
45 morekeywords=[5]{},% regexps (s.moredelim below)
49 morecomment=[s]{\\\#}{\#\\\\)},%
50 morecomment=[s]{/*}{*/},%
52 literate= {->}{$\rightarrow$}{2},%
56 \lstdefinelanguage{Shell}{%
57 morekeywords=[1]{sudo, apt-get, install, cd, export, hg, mkdir, mv, gedit, easy_install, clone, release, g, l, make},% blocks
58 morekeywords=[2]{declare,some,position,pos,optional,opt,where,order-by,group-by,and,or,not},%
59 morekeywords=[2]{variable,ns-default,ns-prefix,not,if,then,else,case,without,desc,descendant,in,identity},%
60 morekeywords=[3]{idvar,var,->,as},% variables
61 morekeywords=[4]{lexical,sum,count,join},% aggregation/sorting
62 morekeywords=[5]{},% regexps (s.moredelim below)
65 morecomment=[s]{\\\#}{\#\\\\)},%
67 literate= {->}{$\rightarrow$}{2},%
84 \emph{"Não comunique-se compartilhando memória, compartilhe
85 memória comunicando-se."}
88 O simples ato de comunicação garante a sincronização.
90 Concorrência em muitos ambientes se torna difícil pelas sutilezas envolvidas
91 no acesso correto a variáveis compartilhadas. Go\cite{GO} encoraja uma abordagem
92 diferente, na qual variáveis compartilhadas são passadas através de canais e
93 nunca são de fato ativamente compartilhadas por threads de execução separadas.
94 Apenas uma gorotina tem acesso à variável num dado momento de tempo. Condições
95 de corrida relativas aos dados não podem ocorrer, por construção. Modelo de concorrência inspirado no CSP \cite{CSP}.
96 \chapter{Primeiros Passos}
98 Passos que eu segui no Ubuntu 9.10:
100 \begin{lstlisting}[language=Shell, texcl=true, numbers=none, escapechar={\%}]
104 export GOROOT=$HOME/go
107 # reiniciar o shell para que as alterações tenham efeito
108 $ sudo apt-get install bison gcc libc6-dev ed gawk make
109 $ sudo apt-get install python-setuptools python-dev build-essential gcc
110 $ sudo easy_install mercurial
111 $ hg clone -r release https://go.googlecode.com/%hg%/ $GOROOT
120 \begin{lstlisting}[language=Shell, texcl=true, numbers=none, escapechar={\%}]
121 # criar o arquivo fonte num editor de sua preferência
122 $ gedit prog.go & # -> prog.go
124 $ %\color{red}{8}%g prog.go # -> prog.8
126 $ %\color{red}{8}%l -o prog prog.8 # -> prog
131 \chapter{Mecanismos de Programação Concorrente}
133 Go tem seu próprio modelo de processo / thread / corotina,
134 chamado gorotina (\emph{goroutines}). Gorotinas são divididas de acordo com o necessário em threads (de usuário) e processos do sistema. Quando uma goroutine executa uma chamada de sistema bloqueane, nenhuma outra gorotina é bloqueada. Para configurar o número máximo de processos criados podemos modificar a variável de ambiente \$GOMAXPROCS ou utilizar a função runtime.GOMAXPROCS (n int) durante a execução. Seu valor default é 1, ou seja, todas as gorotinas são threads de usuário pertencentes ao mesmo processo.
137 Representa um canal de comunicação / sincronização que pode conectar duas
138 computações concorrentes. Na realidade são referências para um objeto que
139 coordena a comunicação.
141 <- é o operador binário de comunicação (envio) <- também é o operador, desta vez unário, de recebimento. Ambos são atômicos.
143 Operações de leitura (respesctivamente, escrita) em canais que não têm buffer ou que estão com o buffer vazio (respectivamente, cheio) bloqueiam, e o bloqueio se mantém até que haja aconteça uma operação de escrita (respectivamente, leitura). A linguagem também permite leitura (respectivamente, escrita) não bloqueante, a qual retorna imediatamente uma flag dizendo se a operação foi realizada com sucesso ou não.
145 Na criação são sempre bidirecionais, mas quando são recebidos como parâmetro por uma função, além da declaração normal (chan T) podem ser declarados para apenas receber (<-chan T) e apenas enviar (chan<- T), com o objetivo garantir que serão utilizados corretamente no corpo da função.
147 As funções len e cap, quando aplicadas a canal, retornam, repspectivamente, a quantidade de mensagens esperando e o tamanho do buffer do canal (caso este seja assíncrono), ou zero caso contrário. São úteis para determinar se o buffer de um canal assíncrono está cheio, o que permite evitar torná-lo síncrono.
151 \begin{lstlisting}[language=Go, texcl=true, escapechar={\%}]
153 var canal_sincrono chan int // envia e recebe inteiros
154 var canal_assincrono chan int // poderia ser qualquer outro tipo
156 canal_sincrono = make (chan int)
157 canal_assincrono = make (chan int, 10) // buffer de tamanho 10
158 // função que utiliza um canal apenas para leitura
159 func so_leio (canal_leitura <-chan int) { ... }
160 // função que utiliza um canal apenas para escrita
161 func so_escrevo (canal_escrita chan<- int) { ... }
162 // função que utiliza um canal bidirecional
163 func leio_e_escrevo (canal chan int) { ... }
165 v := <-canal_sincrono // sempre leitura sincrona
166 v := <-canal_assincrono // leitura assincrona quando houver espaço no buffer, síncrona caso contrário
167 v, ok := <-canal // sempre leitura assíncrona, ok é setado para true ou false de acordo com o sucesso
169 canal_sincrono <- v // sempre escrita síncrona
170 canal_assincrono <- v // escrita assíncrona quando houver espaço no buffer, síncrona caso contrário
171 ok := canal <- v // sempre escrita assíncrona, ok é setado para true ou false de acordo com o sucesso
173 // 0 <= len(canal) <= cap(canal), para qualquer canal
174 // cap(make(chan int)) = 0
175 // cap(make(chan int, n)) = n
179 É o operador que inicia a execução concorrente de uma gorotina, sempre no mesmo espaço de endereçamento. Prefixe uma chamada de função ou de método com a palavra-chave go para executar a chamada numa nova gorotina. Quando a chamada termina, a gorotina também termina (silenciosamente). O efeito é similar a notação \& do shell Unix para rodar um comando em background.
183 \begin{lstlisting}[language=Go, texcl=true, escapechar={\%}]
185 func faz_algo () { ... }
189 // inicia a execução concorrente da função faz\_algo ...
191 // ... e continua executando ...
197 É um estrutura de controle análoga a um switch, mas que age sempre sobre comunicações. Escolhe qual das comunicações listadas em seus casos pode prosseguir. Se todas estão bloqueadas, ele espera até que alguma desbloqueie. Se várias podem prosseguir, ele escolhe uma aleatoriamente.
201 \begin{lstlisting}[language=Go, texcl=true, escapechar={\%}]
202 // esta função envia zeros e uns aleatoriamente a cada iteração
203 func gerador_aleatorio_de_bits (canal chan<- int)
204 for { // laço infinito
205 select { // escolha não-determinística
206 case c <- 0: // nada no corpo do case, o break é implícito (diferente de c)
213 \section{Compilando e executando os exemplos}
215 Todos os exemplos, exceto o hello.go, implementam a solução do problema produtor-consumidor com buffer limitado e taxa de produção e consumo aleatórias, sendo que o pc\_channel\_multi.go implementa a versão com vários produtores e vários consumidores. Os outros implementam a versão singular apenas para manter a simplicidade, mas também são facilmente generalizáveis.
217 \begin{lstlisting}[language=Shell, texcl=true, numbers=none, escapechar={\%}]
220 $ ./programa_desejado
222 \section{Hello, World!}
224 \item[Arquivo:] hello.go
226 \lstinputlisting[language=Go, texcl=true, escapechar={\#}]{../hello.go}
228 \section{Funções Auxiliares}
229 \lstinputlisting[firstline=11, lastline=17, language=Go, texcl=true, numbers=none, escapechar={\$}]{../pc_channel.go}
230 \lstinputlisting[firstline=18, lastline=22, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_channel.go}
231 \lstinputlisting[firstline=23, lastline=27, language=Go, texcl=true, numbers=none, escapechar={\$}]{../pc_channel.go}
232 \lstinputlisting[firstline=28, lastline=32, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_channel.go}
233 \lstinputlisting[firstline=33, lastline=40, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_channel.go}
234 \lstinputlisting[firstline=42, lastline=45, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_channel.go}
235 \lstinputlisting[firstline=47, lastline=51, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_monitor_cond.go}
236 \lstinputlisting[firstline=32, lastline=46, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_monitor_cond.go}
237 %\lstinputlisting[firstline=, lastline=, language=Go, texcl=true, escapechar={\#}]{../pc_channel.go}
240 \lstinputlisting[firstline=32, lastline=36, language=Go, texcl=true, numbers=none, escapechar={\#}]{../pc_mutex.go}
242 \section{Destruição (de variáveis)}
243 Como a linguagem tem coletor de lixo, não é necessário (nem possível) se preocupar com a destruição das variáveis.
248 \begin{lstlisting}[language=Go, numbers=none]
250 var mutex *Mutex = new(Mutex)
253 \begin{lstlisting}[language=Go, texcl, numbers=none]
258 \item[Arquivo:] pc\_mutex.go
259 \item[Implementação (parte principal)]:
260 \lstinputlisting[firstline=37, lastline=91, language=Go, texcl=true, escapechar={\#}]{../pc_mutex.go}
266 \begin{lstlisting}[language=Go, texcl, numbers=none]
267 var semaforo chan int = make(chan int, MAX_VAL) // o semáforo sempre começa em 0
269 \item[Inicialização]:
270 \begin{lstlisting}[language=Go, texcl, numbers=none]
271 // atribui o valor inicial n para o semáforo
272 // com n = 1 simulamos um mutex
273 for i := 0; i < n; i++ {
278 \begin{lstlisting}[language=Go, texcl, numbers=none]
283 \item[Arquivo:] pc\_sem.go
284 \item[Implementação (parte principal)]:
285 \lstinputlisting[firstline=36, lastline=110, language=Go, texcl=true, escapechar={\#}]{../pc_sem.go}
291 \begin{lstlisting}[language=Go, texcl, numbers=none]
292 type Monitor struct {
297 \item[Inicialização]:
298 \begin{lstlisting}[language=Go, texcl, numbers=none]
299 func newMonitor () *Monitor {
302 // inicializar outros campos ...
305 var monitor *Monitor = newMonitor()
308 \begin{lstlisting}[language=Go, texcl, numbers=none]
309 func (m *Monitor) metodo () {
311 // fazer o que tem que fazer ...
316 \item[Arquivo:] pc\_monitor.go
317 \item[Implementação (parte principal)]:
318 \lstinputlisting[firstline=37, lastline=110, language=Go, texcl=true, escapechar={\#}]{../pc_monitor.go}
319 \item[Discussão]: semáforos são utilizados para simular as variáveis de condição. As diferenças entre ambos são:
321 \item[Memória:] semáforos têm memória, no sentido de que sinalização nunca são perdidas, pois permanecem armazenadas no semáforo. Variáveis de condição não tem memória, caso não haja ninguém esperando quando ocorre um sinal, o sinal se perde.
322 \item[Atomicidade:] variáveis de condição destravam o mutex e entram em estado de espera de forma atômica. Isso é necessário pois, se houvesse uma troca de contexto entre estas duas ações, um outra thread poderia entrar no monitor e gerar um sinal que seria perdido, podendo causar um deadlock. Os semáforos, ao contrário, não realizam estas duas operações de forma atômica, mas isso não é um problema, já que os sinais não se perdem.
326 \section{Variável de Condição}
329 \begin{lstlisting}[language=Go, texcl, numbers=none]
331 var cond chan int = make(chan int)
334 \begin{lstlisting}[language=Go, texcl, numbers=none]
335 wait_cond(cond, mutex)
338 \item[Arquivo:] pc\_monitor\_cond.go
339 \item[Implementação (parte principal)]:
340 \lstinputlisting[firstline=57, lastline=130, language=Go, texcl=true, escapechar={\#}]{../pc_monitor_cond.go}
341 \item[Discussão]: a semântica das variáveis de condição diz que o destrancamento do mutex tem que acontecer de forma atômica com o início da espera pela variável (ver discussão na seção sobre Monitor). A melhor tentativa de simular esta atomicidade foi criar uma outra gorotina (waiter) e deixá-la esperando pelo variável de condição antes de destrancar o mutex, e então esperar pela resposta desta gorotina. Porém, mesmo esta implementação está incorreta, pois não há como garantir que a gorotina criada já esteja esperando pela variável de condição antes de destrancarmos o mutex. Portanto, não há como implementar variáveis de condição corretamente na linguagem, podemos apenas simular seu comportamente com semáforos, mas são coisas diferentes. Para se ter uma idéia da chance de ocorrência do deadlock, no meu computador pessoal o programa acima entrou em deadlock em média após 40000 iterações.
345 Os canais de go são exatamente o que precisamos para comunicação entre as gorotinas. Veja como a solução é bem mais elegante:
347 \item[Arquivo]: pc\_channel.go
348 \item[Implementação (parte principal)]:
349 \lstinputlisting[firstline=52, lastline=130, language=Go, texcl=true, escapechar={\#}]{../pc_channel.go}
352 \section{Múltiplos Produtores e Consumidores}
354 \item[Arquivo]: pc\_channel\_multi.go
356 \begin{lstlisting}[language=Shell, texcl=true, numbers=none, breaklines, escapechar={\%}]
357 $ ./pc_channel_multi.go -s BUFFER_SIZE -t MAX_SLEEP_TIME -m MAX_PRODUCTIONS -p N_PRODUCERS -c N_CONSUMERS
359 \item[Implementação (parte principal)]:
360 \lstinputlisting[firstline=40, lastline=130, language=Go, texcl=true, escapechar={\#}]{../pc_channel_multi.go}
364 Go é uma linguagem que tenta aliar um certo nível de abstração (coletor de lixo, canais inspirados num modelo formal amplamente aceito) com eficiência de compilação e execução. A linguagem não apresenta nativamente algumas funcionalidades comuns de concorrência, mas sua implementação é relativamente fácil, pois a linguagem possui o tipo canal que é tão poderoso quanto as funcionalidades a que estamos acostumados.
366 Com a disseminação de computadores multiprocessados, e as dificuldades que temos atualmente para programar corretamente sistemas concorrentes, certamente linguagens como essa terão grande crescimento e importância, podendo começar a substituir gradualmente linguagens mais antigas, pois a ineficiência de execução de suas abstrações individualmente vai ser compensada pela eficiência de sua execução como um todo, por aproveitarem melhor os recursos de hardware disponíveis.
368 \begin{thebibliography}{10}
369 \bibitem{GO} \emph{The Go Programming Language}, http://golang.org/. Acessado em: 04/04/2010.
370 \bibitem{CSP} Hoare, C. A. R., \emph{Communicating Sequential Processes}, Communications of the ACM 21 (8): 666–677.
371 \end{thebibliography}