Conhecimentos Essenciais do Sistema Linux para Entrevistas e Desenvolvimento

1. Diferenças entre Bibliotecas Estáticas e Dinâmicas e Seus Momentos de Vinculação

2. Por que as Bibliotecas Dinâmicas Possuem Maior Flexibilidade em Comparação com as Estáticas?

3. Como Garantir Segurança em Threads Durante uma Entrevista?

A questão aborda a implementação de sincronização entre threads sem interferência mútua. A segurança de threads é a base da sincronização em ambientes multithread, garantindo a correção de recursos compartilhados durante acessos concorrentes.

4. Quais São os Protocolos do Modelo OSI em Suas Sete Camadas?

5. Estado TIME_WAIT:

O estado TIME_WAIT é crucial no processo de conexão TCP, referindo-se ao estado da parte que encerra ativamente a conexão após enviar um segmento FIN e receber uma confirmação ACK da outra parte. Este estado persiste por 2 vezes o Tempo Máximo de Vida de Segmento (2MSL) antes de liberar completamente os recursos da conexão.
Por que dura 2 vezes o tempo máximo de vida do segmento?
1. Garantir que a parte passiva receba a confirmação ACK final. Como o ACK enviado pela parte ativa pode se perder, se a parte passiva não receber, continuará retransmitindo o FIN. O tempo de espera de 2MSL cobre completamente o ciclo "ACK perdido → retransmissão FIN pela parte passiva → retransmissão ACK pela parte ativa".
2. Prevenir que segmentos antigos residuais interfiram em novas conexões. Podem existir segmentos atrasados na rede (pertenções a conexões já encerradas). Se a parte ativa liberar imediatamente a porta e estabelecer nova conexão, segmentos antigos podem entrar no estado da nova conexão, causando erros nos dados.
O tempo de 2MSL garante que segmentos antigos expirem durante a transmissão, não interferindo em novas conexões.

6. Diferenças entre HTTP e HTTPS

  1. Conteúdo da linha de requisição quando o cliente envia dados (prova escrita)
    Cabeçalhos da requisição, corpo da requisição

7. O que São Conexões Longas e Curtas no Processo de Acesso a uma Página Web? (Prova Escrita)

Fundamentos do Linux

I. Conhecimentos Básicos do Linux

1. Introdução ao Terminal

2. Estrutura de Diretórios

  • A estrutura de diretórios do sistema Linux é uma árvore invertida:

3. Descrição de Diretórios Comuns

  • /bin Armazena comandos comuns (programas executáveis binários)
  • /etc Armazena arquivos de configuração do sistema
  • /home Diretório pessoal de todos os usuários comuns
  • /root Diretório pessoal do usuário administrador
  • /usr Armazena aplicações do sistema e documentação
  • /proc Diretório de sistema de arquivos virtual, armazenando mapeamentos de memória por processo
  • /dev Armazena arquivos de dispositivo
  • /mnt Ponto de montagem temporário
  • /lib Armazena arquivos de biblioteca
  • /boot Arquivos relacionados ao kernel do sistema e inicialização
  • /tmp Armazena vários arquivos temporários, acessível por todos os usuários
  • /var Armazena arquivos que frequentemente mudam durante a execução do sistema, como logs

4. Caminhos Absolutos e Relativos

  • Caminho Absoluto: É o caminho desde a raiz do sistema ("/") até a localização desejada
    Exemplo: /home/estudante/main.c
  • Caminho Relativo: É o caminho relativo desde o diretório atual até a localização desejada
    Exemplo: "." representa a posição atual ".." representa o diretório pai da posição atual
    "./main.c" representa o arquivo main.c na posição atual

5. Conceito de Diretório Pessoal

Todos os usuários comuns possuem um diretório criado em "/home" com o nome de usuário, que é o diretório pessoal desse usuário.
Por exemplo: O diretório pessoal do usuário estudante é: /home/estudante
Navegar para o diretório pessoal usando caminho absoluto:
cd /home/estudante
Cada usuário só pode criar e modificar arquivos em seu próprio diretório pessoal, não podendo acessar ou modificar arquivos nos diretórios pessoais de outros usuários.

II. Comandos Básicos do Linux

Comando: Após execução, permite que o sistema operacional execute a ação correspondente

comando (+opções+argumentos)

1.cd +nome_do_diretório: Navega para este diretório

cd .. Retorna ao diretório pai (usa caminho relativo)
cd Navega para o diretório pessoal

2.ls: Exibe arquivos no diretório atual

(ls diretório1 diretório2: Exibe respectivamente diretório1 e diretório2)

ls -l: Imprime informações detalhadas de todos os arquivos e diretórios no diretório atual (permissões)
(ls -l 0408: Visualiza informações detalhadas sem entrar no diretório)
ls -a: Imprime todos os arquivos e diretórios no diretório atual, incluindo ocultos

3.mkdir +nome_do_diretório: Cria um diretório

mkdir -p +diretório1/diretório2: Pode criar diretório2 dentro de diretório1
mkdir -m+permissão+nome_do_diretório: Define permissões correspondentes ao criar o diretório

mkdir -p a/b/c -m 111
Cria 'a' no diretório atual, depois 'b' dentro de 'a', depois 'c' dentro de 'b' com permissão 111

4.touch +nome_do_arquivo: Cria um arquivo comum

5.chmod: Modifica permissões de arquivo

  • Modificação textual: chmod u+rwx main.c
  • Modificação numérica: chmod 751 main.c

6.pwd: Exibe o caminho absoluto da posição atual

7.man +comando: Manual de ajuda para visualizar informações de ajuda sobre comandos, chamadas de sistema, funções de biblioteca, etc.

ex: exit(3) {indica uma função de biblioteca}

8.rm +nome_do_arquivo: Remove um arquivo

9.rmdir +nome_do_diretório: Remove um diretório vazio

Diretórios não vazios requerem rm -r +nome_do_diretório
rm *: Remove todos os diretórios e arquivos no diretório atual

10.cp +nome_do_arquivo1 +nome_do_arquivo2:

Copia arquivo (copia o conteúdo do arquivo1 para o arquivo2)
Se o nome_do_arquivo2 não existir, criará o arquivo2

Copiar diretório: cp -r +nome_do_diretório1 +nome_do_diretório2

(Se o diretório2 não existir: Cria o diretório2 e copia o conteúdo do diretório1 para ele
     Se o diretório2 existir: Copia diretório1 diretamente para diretório2)

11.mv: Mover/Renomear

  • mv +nome_do_arquivo1 +nome_do_arquivo2: (Cria arquivo2) Copia o conteúdo do arquivo1 para o arquivo2 e remove o arquivo1 (também pode ser entendido como renomear)
  • mv +nome_do_arquivo +nome_do_diretório: Move o arquivo para o diretório
  • mv +nome_do_diretório1 +nome_do_diretório2: Se o diretório2 não existir, renomeia diretório1 para diretório2; se existir, move diretório1 para dentro de diretório2

12.find

Busca arquivos especificados na árvore de diretórios, também pode especificar posição inicial de busca

  • find +nome_do_arquivo: Busca o arquivo correspondente no diretório atual
  • No diretório 0408, find 0408 está incorreto
  • Pode ser find /home/estudante/0408 (caminho absoluto)
    "Busca todas as informações no 0408 (incluindo diretórios e arquivos)"
  • find + -name +nome_do_arquivo: Busca em todos os subdiretórios do diretório atual por arquivos com nomes que contenham nome_do_arquivo

Teclas de seta para cima/baixo podem recuperar comandos históricos
Ctrl+c encerra imediatamente o processo atual (resolve situações onde o processo fica em execução travado)

13.grep

Filtra linhas em um arquivo que contêm uma string especificada (pode ser entendido como filtragem)
grep + "string_para_buscar" +nome_do_arquivo: Busca e imprime linhas contendo a string
grep -c "string" +nome_do_arquivo (conta)
grep -v "string" +nome_do_arquivo (inverte)
grep -i "string" +nome_do_arquivo (ignora maiúsculas/minúsculas)

14.Pipeline |

Passa a saída do comando anterior como entrada para o comando seguinte
ls 0408 | grep main.c: Buscará se há main.c no 0408
Mas: ls 0408 | find main.c não encontra (main.c e 0408 estão no mesmo nível, find não consegue localizar)

15.wc

Conta número de palavras (-w), caracteres (-c), linhas (-l) em um arquivo

16.su (sudo su)

su username Alterna usuário, se nenhum nome de usuário for especificado, alterna para o usuário administrador por padrão

17.nível de execução

runlevel Visualiza nível de execução do sistema
◼ 0 Desligar
◼ 1 Modo usuário único
◼ 2 Múltiplos usuários sem serviço de rede
◼ 3 Múltiplos usuários completos, interface textual
◼ 4 Não definido ou personalizado
◼ 5 Interface gráfica
◼ 6 Reiniciar

18.cat +nome_do_arquivo

Obtém conteúdo do arquivo (imprime conteúdo do arquivo)

  • cat > nome_do_arquivo: Pode redirecionar entrada da tela para o arquivo (ctrl+d termina entrada, ctrl+c encerra processo)
  • cat arquivo1 > arquivo2: Sobrescreve conteúdo do arquivo2 com conteúdo do arquivo1 (sobrescreve, não acrescenta)
  • cat arquivo1 >> arquivo2: Acerta conteúdo do arquivo1 ao conteúdo do arquivo2
  • cat >>arquivo: Acerta entrada da tela para conteúdo do arquivo
  • cat arquivo1 arquivo2 >arquivo3 Sobrescreve conteúdo do arquivo1 e arquivo2 no arquivo3

19.more

Quando o conteúdo de um arquivo excede as linhas que uma tela pode exibir, use more para exibir conteúdo em telas separadas. Uso: more main.c

20.less

Visualizador de conteúdo de texto, exibe conteúdo do arquivo em telas separadas, mas o conteúdo não aparece na interface. Use teclas de seta para cima/baixo para controlar, q para sair

21. head

Exibe conteúdo das primeiras 10 linhas por padrão
head -2 +nome_do_arquivo Primeiras 2 linhas

22.tail

Exibe conteúdo das últimas 10 linhas por padrão
tail -2 +nome_do_arquivo Últimas 2 linhas

III. Tipos de Arquivo e Permissões no Sistema Linux

1. Tipos de Arquivo

Tudo no Linux pode ser considerado um arquivo. O Linux divide arquivos nos seguintes tipos:
◼ Arquivo comum '-'
◼ Diretório 'd'
◼ Arquivo de pipe 'p'
◼ Arquivo de link 'l'
◼ Arquivo de dispositivo (dispositivo em bloco 'b', dispositivo em caractere 'c')
◼ Arquivo de socket 's'

2. Permissões de Arquivo

  1. -rwxrwxr-x 1

    • O primeiro caractere indica o tipo de arquivo (arquivo comum)
    • rwxrwxr-x indica as permissões do arquivo: respectivamente permissões do proprietário(u), grupo(g), outros(o)
    • O número 1: Se for diretório, indica quantos subdiretórios possui. Se for arquivo, indica quantos links existem.
  2. Permissões de arquivo
    ◼ r permissão de leitura Valor: 4
    ◼ w permissão de escrita Valor: 2
    ◼ x permissão de execução Valor: 1
    ◼ - sem permissão Valor: 0

IV. Editor Vim

1.vi +nome_do_arquivo

(Cria arquivo) e entra no modo de edição

2. Três modos:

  • 1.Modo de edição:
    a: Inserir após o cursor
    i: Inserir antes do cursor
    o: Inserir na linha abaixo do cursor
    A: Inserir no final da linha
    I: Inserir no início da linha
    O: Inserir na linha acima do cursor
  • 2.Modo de comando:
    dd: Corta linha atual do cursor
    yy: Copia linha atual do cursor
    p: Cola na linha abaixo do cursor
    ndd, nyy, np: Cortar, copiar, colar n linhas
    u: Desfazer
    ctrl+r: Desfazer ação de desfazer (refazer)
    r: Modifica (1 caractere)
  • Modo de última linha::
    :w Salvar
    :q Sair
    :wq Salvar e sair
    :q! Sair forçadamente (sem salvar)
    :set number Definir números de linha
    :set nonumber Remover números de linha
    :10 Navegar para linha 10
    /"string" Buscar esta string
    :set hlsearch Definir destaque
    :set nohlsearch Remover destaque

V. Compressão e Descompressão de Arquivos

◼ c Cria arquivo de pacote
◼ f Especifica destino como arquivo em vez de dispositivo
◼ v Exibe processo detalhado
◼ t Exibe conteúdo do pacote sem liberar
◼ x Libera conteúdo do pacote
◼ z Nova versão GNU, permite que tar tenha funções de compressão e descompressão

1.tar -cvf +pacote_destino +arquivos_para_empacotar

Empacota arquivos no pacote_destino

2.tar -tvf +pacote_destino

Visualiza conteúdo detalhado do pacote_destino

3.tar -xvf +pacote_destino -C +endereço_destino

Desempacota para o endereço_destino

4.tar -zcvf +pacote_destino +arquivos_para_empacotar

Empacota arquivos no pacote_destino e comprime (pacote comprimido)

5.tar -zxvf +pacote_destino -C +endereço_destino

Descomprime o pacote comprimido para o endereço_destino

6.gzip +pacote_destino

ex: gzip meu.tar
//meu.tar.gz(pacote comprimido)
tar zxf meu.tar.gz (descomprime)

Conhecimentos Avançados do Linux

I. Fundamentos de Processos

1. Comandos de Processo

  1. ps: Imprime informações de processos

bash: Interpretador de comandos, sempre presente, usado para analisar comandos externos

ps -e: Imprime informações de processos do sistema
ps -f: Imprime informações detalhadas do processo
ps -ef: Imprime informações detalhadas dos processos do sistema
pstree: Exibe relacionamentos entre processos em forma de árvore

  1. kill +número_do_processo (PID)
    Encerra ou suspende um processo
  2. & : Executa processo em segundo plano
    sleep +segundos: Executa em primeiro plano, ocupando terminal
    sleep 100 &: Executa um processo em segundo plano
    Após Enter, exibe número_da_tarefa+número_do_processo
  3. jobs: Exibe tarefas em execução no terminal atual
  4. fg bg
    fg + %número_da_tarefa : Move processo em segundo plano ou suspenso para execução em primeiro plano
    (ctrl+s: Suspender temporariamente processo em primeiro plano)
    bg + %número_da_tarefa : Desperta processo suspenso para execução em segundo plano
  5. while() while(condição)
    do date {
    sleep 2
    done & }
    Em segundo plano, só ocupa tela mas não está no buffer, comandos ainda podem ser executados normalmente

2. Caracteres Curinga

  1. ?: Representa um caractere de qualquer posição
    rm 1.c 2.c ()
    rm ?.c "equivalente"
  2. *: Representa qualquer número de caracteres em qualquer posição
    rm 1.c 2.c 12.c
    rm * .c
    rm * : Remove todos os arquivos e diretórios no diretório atual
    rm -rf *: Remove todos os arquivos e diretórios de todo o sistema
  3. [abc] : Representa um caractere dentro do intervalo [ ]
    1.c 2.c 3.c 12.c
    rm [123].c (remove 1.c 2.c 3.c)
    [^123] : Representa um caractere fora do intervalo [ ]
    [a-z] : Representa um caractere dentro do intervalo [ ]

II. Compilação e Depuração de Programas C no Linux

1. gcc -o main.c main: Gera arquivo executável

rm .main.c.swp: Remove arquivo não fechado no último desligamento (ou pressione e) "swp: arquivo de troca"

Pré-compilação: Gera arquivo de pré-processamento .i, expansão de cabeçalhos, atribuição de definições de macro
gcc -E main.c -o main.i
Compilação: Gera arquivo de montagem .s
gcc -S main.i -o main.s
Montagem: Gera arquivo binário .o, contendo código de máquina e tabela de símbolos
gcc -c main.s -o main.o
Vinculação: Gera arquivo executável (Linux sem extensão, Windows extensão .exe)
gcc main.o -o main

gcc -o main.c main
./+main(nome do executável): Executa

Ou: gcc main.c
./a.out(só pode executar a compilação desta vez)

2.makefile; make: Compilação conjunta de múltiplos arquivos .c

  1. vi makefile

make gera executável chamado "MaxAdd";
./MaxAdd executa

2. Para executar operação clean: make +marcador: Só executa marcador, outras regras de dependência não rodam

3.Depuração com gdb

Na compilação deve adicionar -g para ter arquivo de depuração

  1. gdb +nome_do_executável (depurável) entra na depuração
    ps: Entrando end deveria encerrar programa, mas programa não termina, há \n no buffer, então é feita depuração
  2. Comandos gdb:
    l : Exibe código fonte (10 linhas padrão)
    b+número_da_linha : Adiciona ponto de interrupção
    r (run) : Executa programa
    n : Execução passo a passo (linha por linha)
    c (continue) : Continua execução, executa diretamente até próximo ponto de interrupção
    p val: Imprime valor de val p &val imprime endereço de val
    p *parr@len : Usa apontador para array para imprimir todos os elementos do array
    p buff : Imprime array buff
    q : Sai da depuração
    b +nome_da_função : Adiciona ponto de interrupção na primeira linha válida da função especificada
    info break : Exibe informações dos pontos de interrupção
    delete +número_do_ponto : Remove ponto de interrupção especificado
    disable +número_do_ponto : Define ponto como inválido, sem número define todos como inválidos
    enable +número_do_ponto : Define ponto como válido, sem número define todos como válidos
    s : Entra na função que será chamada para execução
    finish : Sai da função
    display : Exibição automática, argumentos iguais ao comando p
    info display : Exibe informações de exibição automática
    undisplay +número : Remove exibição automática especificada
    ptype val : Exibe tipo da variável
    bt : Exibe pilha de chamadas de função

Solução:
Mas só pode imprimir três primeiros caracteres cada vez
A função fgets também conta \n como caractere, então len calculado para end\n é 4

III. Geração e Uso de Arquivos de Biblioteca no Sistema Linux

1. Arquivos de Biblioteca

Arquivos de biblioteca são coleções de código reutilizável já escritas (podem ser entendidos como bibliotecas de funções)
Arquivos de biblioteca são todos compostos por arquivos binários .o

  1. No diretório /lib: Biblioteca estática: libxxx.a (empacotada)
    Biblioteca dinâmica (compartilhada): libxxx.so (compilada)

2. Geração de Biblioteca Estática:

  1. Todos os arquivos .c makefile para arquivos .o; (pode ser via makefile)
  2. Usar comando ar para gerar biblioteca estática a partir de arquivos .o (empacotamento)
    ar crv libxxx.a add.o max.o (não inclui main.o)
    ◼ c cria biblioteca
    ◼ r adiciona métodos à biblioteca
    ◼ v exibe processo (opcional)
  3. gcc main.c -o main -L. -lxxx (gerará executável main)
    -L +caminho_de_armazenamento_da_biblioteca (. é diretório atual)
    -l especifica nome da biblioteca (não precisa do 'lib' e extensão '.a')
  4. Executar main
    Mover libxxx.a para /home/estudante/código/0408 (mover para diretório 0408)
    gcc main.c -o main -L. -lfoo falhará
    Solução: gcc main.c -o main -L/home/estudante/0408 -lxxx

3. Geração de Biblioteca Dinâmica:

  1. Todos os arquivos .c makefile para arquivos .o;
  2. gcc add.o max.o -shared -fPIC -o libyyy.so (compilação)
    Mas gcc -o main main.c -L. -lyyy ./main está errado (porque o sistema por padrão só procura em localizações padrão de armazenamento de biblioteca (/lib ou /usr/lib etc.), e não na posição atual. Portanto, copie a biblioteca para /usr/lib, execute o programa e será bem-sucedido.)
  3. Necessário sudo mv libyyy.so /usr/lib (adiciona sudo antes do comando para temporariamente elevar permissões para administrador)
    Depois ./main

ldd +executável: Exibe quais bibliotecas compartilhadas o arquivo usa para execução
ldd main
(Se exibir no found, lembre-se de mover a biblioteca para lib
Ou pode definir variável de ambiente dinâmica no diretório atual: LD_LIBRARY_PATH=/home/estudante/código/0413
export LD_LIBRARY_PATH(executa)
Executar ./main novamente estará correto)

4. Cuidados ao deletar biblioteca: (4/20)

  1. Biblioteca estática gerada executável, quando biblioteca estática é deletada, executável ainda pode executar. (inclui definições da função, já copiou cópia da biblioteca)
  2. Biblioteca dinâmica gerada executável, quando biblioteca dinâmica é deletada, executável não pode executar. (apenas marca que tem a função, não copia definição, ao executar programa precisa carregar biblioteca dinâmica)

5. Momento de Vinculação:

  1. Biblioteca Estática: É vinculada na compilação, durante fase de compilação, compilador já copiou completamente todo código referenciado para executável. Uma vez vinculada, não depende da biblioteca estática
  2. Biblioteca Dinâmica: Durante fase de compilação, apenas registra relação de dependência do executável na biblioteca dinâmica, não copia código da biblioteca. É vinculada em tempo de execução, portanto deve depender da biblioteca dinâmica

6. Ocupação de Espaço em Memória:

  1. Biblioteca Estática:
    a. Desvantagem: Cada executável gerado por biblioteca estática terá uma cópia do código da biblioteca, resultando em tamanho grande, e múltiplos programas em execução ocuparão memória repetidamente (desperdício de recursos)
    b. Vantagem: Não depende de arquivos externos, alta portabilidade (basta copiar executável para executar)
  2. Biblioteca Dinâmica:
    a. Desvantagem: Depende de arquivos externos, baixa portabilidade, deve garantir que sistema correspondente tenha biblioteca dinâmica
    b. Vantagem: Economiza memória, só precisa armazenar uma cópia no disco, múltiplos executáveis compartilham mesma biblioteca (memória compartilhada)

IV. Uso de <math.h>:

vi 1.c

#include<math.h>
int principal()
{
   double n=4.0;
   double valor=sqrt(n);
   printf("%lld\n",valor);
   return 0;
}

gcc 1.c -o 1
./1
Execução falha (não encontrou <math.h>)
gcc 1.c -o 1 -lm (m é primeira letra de <math.h>) (pode omitir -L)
./1 Sucesso

V. Processos Avançados

VI. Replicação e Substituição de Processos no Linux

1. Momento de Saída do Buffer:

  1. Buffer é atualizado forçadamente, ex: \n buffer imediatamente imprime conteúdo do buffer, depois limpa
  2. Buffer está cheio, não consegue receber novo conteúdo, imprimirá conteúdo do buffer
  3. Quando processo termina, conteúdo do buffer será impresso
int principal()
{
    //1.
    printf("olá\n");
    dormir(4);//imprime primeiro, depois espera 4s e encerra programa
    //2.
    printf("olá");
    dormir(4);//espera 4s, imprime e depois encerra programa
    //3.
    printf("olá\n");
    dormir(4);
    sair(1);//imprime primeiro, espera 4s e encerra programa
    //4.
    printf("olá");
    sair(1);
    dormir(4);//imprime e encerra imediatamente
    //5.
    printf("olá");
    dormir(4);
    _sair(1);//espera 4s e encerra imediatamente
}

2.exit Encerrar Programa <stdlib.h>, <unistd.h>

exit: Função de biblioteca padrão C <stdlib.h>
_exit: Chamada de sistema <unistd.h> (descarta conteúdo do buffer)

  1. _exit: Modo de núcleo direto
    a. Dispara mecanismo de recuperação do núcleo, espaço do núcleo imediatamente recupera recursos do processo (PCB, descritores de arquivo), retorna código de saída para processo pai
  2. exit: Operações modo usuário + modo núcleo
    a. Executa funções de saída do usuário
    b. Atualiza e fecha fluxos de dados I/O, dados do buffer serão atualizados para terminal ou arquivo (stdout arquivo de saída padrão)
    c. Limpa recursos privados do processo (memória heap, memória de variáveis globais)
    d. Chama _exit do modo núcleo
  3. Cenário de uso
    Processo único comum: Usar exit
    Multiprocessos: Subprocesso usa preferencialmente _exit()

3. Replicação de Processo fork

Tecnologia cópia na escrita: Após fork do processo pai, subprocesso tem recursos iguais ao processo pai (não abre primeiro, empresta algumas informações do processo pai)
Ideia principal é "atrasar operação de cópia, executar apenas quando necessário", evitando desperdício de recursos (necessário: precisa escrever/modificar conteúdo)
Por que realizar cópia na escrita?
Antigamente ao copiar, fazia cópia completa, incluindo segmento de dados, segmento de código, heap e pilha, replicava completamente para espaço do subprocesso. Mas às vezes processo pai chamará exec (substitui processo) para carregar novo programa (sobrescreverá espaço de endereçamento original), ou modificará poucos dados, causando "desperdício de recursos de páginas de memória não usadas e páginas de memória não modificadas"
Atenção:
Só quando página de memória é realmente modificada, cópia é executada, páginas não modificadas sempre compartilhadas, evitando cópia inválida

  1. pid_t fork(void); //Tipo de retorno pid_t → essencialmente tipo int
    Três tipos de valores de retorno:
    a. Se retorno -1, indica falha na replicação de processo (recursos do processo cheios)
    b. Se retorno maior que 0, atualmente em processo pai
    c. Se retorno igual a 0, atualmente em subprocesso

Função fork gera novo processo, processo que chamou fork é processo pai, processo recém-gerado é subprocesso

  1. getpid() //Obtém número do processo atual
    getppid() //Obtém ID do processo pai do processo atual
    6 8 8 3
int principal()
{
    for(int i=0;i<2;i++)
    {
        printf("A");
        bifurcar();
    }
}
int principal()
{
    for(int i=0;i<2;i++)
    {
        printf("A\n");
        bifurcar();
    }
}

4. Endereço Lógico e Endereço Físico

  1. Endereço Lógico: É parte do espaço alocado durante execução do processo (endereço virtual), só válido durante execução do programa
    Endereço Físico: Endereço real armazenado na memória, é endereço direto da memória, numerado por linha
    Usuários:
    Endereço Lógico (desenvolvedor), cpu
    Endereço Físico (controlador de memória)
    Intervalo de espaço:
    Endereço Lógico (alocado pelo sistema operacional, independente de memória física, ex: 32 bits sistema operacional processo espaço lógico tamanho 4G)
    Endereço Físico (igual capacidade de hardware de memória física)
  2. Antigamente só havia endereço físico, três desvantagens:
    a. Conflito de memória, múltiplos processos acessando mesmo endereço físico causaria sobrescrita de dados
    b. Desperdício de memória, programa precisa de memória física contínua, mas se houver pequenos blocos de memória livre no espaço, causará desperdício, mesmo se espaço livre total for suficiente, não pode armazenar
    c. Baixa segurança: Programa pode acessar diretamente endereço físico, pode modificar maliciosamente dados de memória do sistema
    ps: Através de isolamento de espaço de endereçamento e mapeamento dinâmico, espaço lógico resolve os problemas acima
    a. Cada processo possui "espaço de endereçamento virtual" independente, não pode acessar diretamente endereço lógico do outro, não há conflito
    b. Endereço lógico não precisa corresponder a endereço físico contínuo, sistema operacional pode "emendar" fragmentos de memória física dispersa para endereço lógico contínuo
    c. Durante conversão de endereço, adicionar verificação de permissão, prevenir acesso não autorizado
  3. Endereço lógico não pode acessar diretamente memória física, precisa Unidade de Gerenciamento de Memória (MMU) completar conversão, depende de tabela de páginas
    Dividir memória em páginas: Sistema operacional divide espaço de endereçamento lógico e espaço de endereçamento físico em "páginas" de tamanho fixo, ex: 4KB por página
    Endereço lógico→ página lógica Cada página lógica tem número de página lógica (VPN)
    Endereço físico→ página física Cada página física tem número de moldura de página física (PFN)
    Ex: Sistema operacional 32 bits terá endereço lógico de 32 bits, páginas de 4KB 4GB/4KB=1048576 páginas
    Número da página lógica VPN ocupa 20 bits, deslocamento dentro da página ocupa 12 bits 4KB = 2 elevado a 12

Suponha int a=0x1234 (0x00401000) sistema 32 bits páginas de 4kb
Primeiro passo: Primeiro dividir endereço lógico VPN=0x00401(primeiros 20 bits) deslocamento dentro da página=0x000
Segundo passo: MMU usará VPN para consulta de tabela, suponha consulta resulte PFN=0x12345
Terceiro passo: Concatenar endereço físico juntar PFN com deslocamento dentro da página 0x12345(PFN)0x000(deslocamento dentro da página)0x12345000(endereço físico real)
Quarto passo: Controlador de memória acessa endereço físico 0x12345000, grava valor 0x1234 neste endereço, completa atribuição da variável
Suponha endereço lógico seja 0x33377722, páginas de 1KB, calcular endereço físico
0xxxxxxx 01 11 0x22

Endereço lógico é determinado durante compilação do programa

4. Processos Zumbis e Órfãos

  1. Processo Zumbi: Subprocesso termina antes do processo pai, e processo pai não obteve código de saída do subprocesso, este subprocesso se tornará processo zumbi

Resolver problema de código de saída ser decimal
WIFEXITED: Verifica se subprocesso terminou normalmente, retorna true false
WEXITSTATUS: Obtém código de saída normal

//No processo pai
int valor;
aguardar(&valor);//aguarda subprocesso terminar execução antes de continuar
if(WIFEXITED(valor))
   printf("%d\n",WEXITSTATUS(valor));//imprime código de saída do subprocesso
  1. Processo Órfão: Processo pai termina antes do subprocesso, subprocesso será redistribuído pelo sistema um novo processo pai (alocado automaticamente pelo gerenciador de processos), subprocesso não terminará por causa do término do processo pai

5. Introdução aos Parâmetros da Função Principal (Execução de Função com Parâmetros)

  1. int principal( int argc, char* argv[], char* envp[])
    (1) argc número de argumentos
    (2) argv conteúdo dos argumentos (array de strings)
    (3) envp variáveis de ambiente (array de strings)
int principal(int argc, char* argv[],char* envp[])//envp automaticamente obtém variáveis de ambiente do diretório atual (ver informações)
{
    printf("argc=%d\n",argc);
    for(int i=0 ;i < argc; i++ )
        printf("argv[%d]=%s\n",i,argv[i]);
    for(int  i = 0; envp[i] != NULL; i++ )
        printf("envp[%d]=%s\n",i,envp[i]);
    sair(0);
}

VII. Operação de Arquivo <fcntl.h>

1. Abrir Arquivo open(nome_do_arquivo, modo_de_abertura, (configuração_de_permissão))

  1. int open(const char* pathname, int flags); //Usado para abrir um arquivo existente
    int open(const char* pathname, int flags,mode_t mode); //Usado para criar novo arquivo,
    e configurar permissões de acesso (0600 só leitura/própria escrita então 6)
    Descritor de arquivo: Número atribuído pelo sistema ao arquivo (começa do 3)
    Razão: Descritor do sistema
    0: Arquivo de entrada padrão stdin
    1: Arquivo de saída padrão stdout
    2: Arquivo de saída de erro padrão stderr
    ps: Se arquivo aberto mas não fechado, descritor de arquivo será mantido, se fechado, descritor de arquivo será liberado e pode ser realocado
    Abrir e configurar permissão:

2. Ler Arquivo read(descritor_de_arquivo, array_buff, tamanho)

Valor de retorno é número de caracteres lidos com sucesso

3. Escrever Arquivo write(descritor_de_arquivo, const char*buf, tamanho)

write(1,"olá",5); <—>printf("olá");

No mesmo processo, operações de leitura e escrita farão ponteiro (cursor) mover
Iseek(int fd, off_t offset, int whence)//descritor de arquivo, representa deslocamento, representa posição inicial do cursor
Posição inicial do cursor:
SEEK_SET: Começar do início do arquivo
SEEK_CUR: Começar da posição atual do ponteiro
SEEK_END: Começar do final do arquivo

Movimento de ponteiro dentro do arquivo (configurar cursor)

4. Fechar Arquivo close(descritor_de_arquivo)

5.fflush(FILE*ponteiro_tipo_arquivo): Atualização forçada do buffer

Atualiza conteúdo do buffer para arquivo
ex:fflush(stdout); Pode atualizar arquivo do buffer para tela

6. Exercício: No mesmo processo, copiar conteúdo de 1.txt para 2.txt, e imprimir, exigindo nomes de arquivo como parâmetros de entrada ./main 1.txt 2.txt

  1. Função main com argumentos de linha de comando
    int principle(int argc, char *argv[ ])//número de argumentos da linha de comando, array de argumentos da linha de comando
    ex: ./p 1.txt 2.txt
    Argumentos são 3 Array: argv[0]=./p(nome do programa)
    argv[1] = "1.txt" (primeiro argumento)
    argv[2] = "2.txt" (segundo argumento)
  2. Implementação da função

7.fork e Arquivo:

Como PCB criado por fork de subprocesso é cópia do PCB do processo pai, ponteiro para tabela de arquivos abertos no PCB do subprocesso apenas copia valor do PCB do processo pai, portanto processo pai e subprocesso compartilham todos os descritores de arquivo abertos antes do fork

  1. Atenção à leitura de processos pai e subprocesso (ubuntu geralmente processo pai executa antes do subprocesso)
    Impressão será
    a
    b
  2. Se primeiro fork depois open impressão será
    a
    a

8. Diferença entre Chamadas de Sistema e Funções de Biblioteca

Diferença: Implementação de chamada de sistema está no núcleo, pertence ao espaço do núcleo, implementação de funções de biblioteca está na biblioteca de funções, pertence ao espaço do usuário.
Mas o nível inferior do espaço do usuário também transmite via registradores para núcleo e depois volta para chamar

VIII. Substituição de Processo com exec <unistd.h>

Série de funções exec podem realizar substituição de processo, substitui apenas conteúdo do processo, para relação entre processos pai e filho, e número do processo são mantidos

1.execl

//Nome do processo para o qual deseja substituir (preencher caminho), nome do processo, opções a executar, marcador de fim ((char *)0)
Substituição de processo: Imprime informações detalhadas do processo
-f: Imprime informações do processo em formato completo

2.execlp

//Nome do processo para o qual deseja substituir (preencher nome do arquivo), nome do processo, opções a executar, marcador de fim ((char *)0)
Substituição de processo: Imprime informações detalhadas e ocultas

3.execle

//Parâmetros da função principal devem ser passados (variáveis de ambiente envp)

4.execvp

execvp(const char *file,char *const argv[ ])
//Nome do processo, opções a executar, marcador de fim ((char *)0) ou NULL são todos encapsulados no array argv
Substituição de processo: Imprime informações detalhadas completas do processo
-f: Imprime informações do processo em formato completo
-e: Imprime informações de todos os processos
char*argv={"ps","-l","-e" ,"NULL"};
execvp("ps",argv);

execvp e execvpe são semelhantes

IX. Uso de Sinais no Linux

1. Conceitos Básicos de Sinais

  1. #define SIGINT 2 //Quando tecla Ctrl+c é pressionada, gera este sinal
  2. #define SIGKILL 9 //Resposta a este sinal não pode ser alterada
  3. . #define SIGPIPE 13 //Descritor de leitura fechado, gerado quando escrita ocorre, este sinal terminará o programa
  4. #define SIGTERM 15 //Sinal padrão enviado pelo comando kill do sistema
  5. #define SIGCHLD 17 //Após subprocesso terminar, por padrão envia este sinal para processo pai

2. Modificar Resposta ao Sinal – signal()

//Modificar comportamento do sinal

3. Enviar Sinal – kill()

./main 2 +pid :(2) é definição de macro

4. Projeto Um: mybash

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<pwd.h>

void imprimir_info()
{
  char *str = "$";
  int uid = obter_uid();//obtém UID do processo atual
  if( 0 == uid)//0 é administrador
     str = "#";

  struct passwd* ptr = obter_pwuid(uid);//via UID obtém nome de usuário, diretório pessoal, etc
     if( ptr == NULL)//NULL usuário não existe
     {
         printf("mybash $");
         fflush(stdout);
         return ;
     }
  char host[128] = {0};
  obter_nome_host(host, 128); //obtém nome do host local #include <unistd.h> 

  char caminho[128] = {0};
  obter_diretorio_atual(caminho, 128);     //obtém diretório de trabalho atual (caminho absoluto)

  printf("\033[1;28m%s @ %s\033[0m:\033[1;24m%s\033[0m%s ",ptr->pw_name, host, caminho, str);// \033[ é início de sequência de escape de cor do terminal (\033[ atributo;cor m), \033[0m restaura cor padrão
  fflush(stdout);
}


char *obter_cmd(char buff[], char *meu_argv[])//divisão de string
{
   int i=0;
   char *s = strtok(buff," ");
   while( s != NULL)
    {
      meu_argv[i++] = s;
      s = strtok(NULL," ");
    }

   return meu_argv[0];
}
int principal()    //  "ls -l" 
{
  while(1)
   {
     imprimir_info();

     char buff[128]={0};
     char *meu_argv[10] = {0};


     fgets(buff, 128, stdin);
     buff[strlen(buff) - 1] = '\0';

     char *cmd =  obter_cmd(buff, meu_argv);

     if( strcmp(cmd, "sair") == 0)
        break;

     if( strcmp(cmd, "cd") == 0)//navegar
       {
         if(meu_argv[1]==NULL)
         {
            alterar_diretorio("/home/estudante");
            continue;
         }
         alterar_diretorio(meu_argv[1]);  // cd ..  cd /home/zyg
         //alterar_diretorio altera diretório de trabalho atual
         //#include <unistd.h>    int alterar_diretorio(const char *caminho); Passa em: string de caminho (ex /home/estudante, .., ~/código) Retorna: 0 → sucesso -1 → falha
         continue;
       }
     pid_t pid = bifurcar();
     if(pid == -1)
       continue;
     if( pid == 0)
      {
       executar_cmd(cmd, meu_argv);
       printf("!!!!\n");
       sair(0);
      }
     else
     {
      aguardar(NULL);
     }
   }
   printf("mybash executou com sucesso!\n");
}

X. Comunicação entre Processos: mecanismo ipc

1. Pipes

  1. echo "linux" > meu_pipe
    Redireciona string de entrada para meu_pipe (arquivo)
    cat > meu_pipe
    Da tela de entrada para arquivo
    cat < meu_pipe
    Do arquivo para tela de entrada
1.1 Pipe com Nome

Pipe com nome pode se comunicar entre quaisquer dois processos

  1. mkfifo FIFO : (criação de pipe com nome)
  2. Características:
    a. Existe nome de arquivo (presente no sistema de arquivos)
    b. Essencialmente é arquivo especial, não ocupa espaço em disco
    c. Pode ser acessado por qualquer processo (com permissão)
    d. Transmissão de dados segue princípio primeiro a entrar primeiro a sair
    e. Após fechar processo que referencia este pipe com nome, pipe ainda existe
  3. Leitura Escrita Usando comunicação via pipe
1.2 Pipe Sem Nome

Pipe criado via arquivo .c
Requisito: Suporta apenas comunicação entre processos com relação de parentesco
Produzido usando chamada de sistema pipe

1.3 Comparação entre Pipe com Nome e Pipe Sem Nome:

2. Memória Compartilhada

2.1 Verificar coisas necessárias para comunicação entre processos: ipcs

Número de conexões: Quantos processos estão conectados a esta memória compartilhada agora, quanto mais conexões, mais processos esta memória compartilhada está mapeada, podendo realizar comunicação multiprocessos

2.2 shmget

#include<sys/shm.h>

  1. int shmget(key_t chave, tamanho_t tamanho, int shnflg);
    Representa criar um espaço de memória compartilhada (se existir obtém shmid do espaço compartilhado)
  2. chave: Valor definido pelo usuário, representa chave, geralmente obtido por ftok()
  3. tamanho: Tamanho do segmento de memória compartilhada (em bytes)
    Na criação, inteiro positivo. Para segmento existente, preencher 0
  4. shmflg: Marcadores comuns
    IPC_CREAT: Se shmid não existe, cria novo segmento
    IPC_EXCL: Usado com IPC_CREAT, indica que deve ser novo, se este segmento de memória compartilhada shmid já existe, gera erro
    Controle de permissão: 0600 etc
  5. Valor de retorno: shmid do segmento de memória compartilhada criado
2.3 shmat
  1. void *shmat(int shmid, const void *shmaddr, int shmflg);
    Representa anexar um segmento de memória compartilhada existente ao espaço de endereçamento de um processo
  2. shmid: id do segmento de memória compartilhada
  3. shmaddr: Especifica a qual endereço o segmento de memória compartilhada deve ser anexado no espaço de endereçamento do processo
    NULL: Permite que o sistema selecione automaticamente um endereço adequado (não usado) para anexar
    Não NULL: Especifica um bloco de espaço de endereçamento para anexar, se este espaço não puder ser usado, gera erro
  4. shmflg: Controla operação de anexação
    0: Permissões padrão, usa permissões de acordo com as permissões do segmento de memória compartilhada na criação
    SHM_RDONLY: Anexa memória compartilhada apenas para leitura, se processo tentar escrever, gera erro de segmento
2.4 shmdt
  1. int shmdt(const void *shmaddc);
    Representa desconectar mapeamento
  2. shmaddr: Endereço a desconectar mapeamento
Por meio de memória compartilhada, realizar leitura e escrita contínuas

3. Semáforos #include<sys/sem.h>

3.1 Conhecimento Básico

Controla ordem/número de execução de processos

  1. Semáforo é uma variável especial, geralmente com valor positivo. Seu valor representa número de recursos permitidos, ao obter recurso, é necessário decrementar atomicamente valor do semáforo (operação atômica: operação que não pode ser interrompida), esta operação é chamada operação P. Quando valor do semáforo é 0, representa nenhum recurso disponível, operação P bloqueará. Ao liberar recurso, é necessário incrementar atomicamente valor do semáforo, esta operação é chamada operação V.
  2. Se valor do semáforo só aceita 0,1, é chamado semáforo binário. Se valor do semáforo é maior que 1, é chamado semáforo de contagem.
  3. Recurso crítico: Recurso que só permite ser acessado por um processo ou thread por vez
    Seção crítica: Segmento de código que acessa recurso crítico
3.2 semget
  1. int semget(key_t chave,int nsems,int semflg);
    Usado para criar conjunto de semáforos (array de semáforos)
  2. chave: Valor chave do conjunto de semáforos
  3. nsems: Representa número de semáforos no conjunto
    Se for criar conjunto de semáforos, deve ser maior que 0
    Se já existir, é 0
  4. semflg:
    IPC_CREAT para criar conjunto de semáforos
    IPC_EXCL indica criação completamente nova 0600 permissão
  5. Valor de retorno: ID semid do conjunto de semáforos
3.3 semctl
  1. int semctl(int semid,int semnum, int cmd,…);
    Realiza operações no conjunto de semáforos
  2. semid: id do conjunto de semáforos
  3. semnum: Índice do semáforo no conjunto
  4. cmd: Operação no conjunto de semáforos
    SETVAL: Atribui valor especificado ao semáforo correspondente (com quarto argumento)
    GETVAL: Obtém valor do semáforo especificado
    IPC_RMID: Deleta todo conjunto de semáforos (incluindo memória compartilhada e conjunto de semáforos)
    (IPC_SET: Define atributos do conjunto de semáforos IPC_STAT: Obtém estado do conjunto de semáforos)
    … : Argumentos variáveis, precisam definir união (tipo union) para passar

ipcs visualiza informações ipcrm -m +shmid remove memória compartilhada

union//união
{
    int val//usado para atribuir valor especificado
    struct semid_ds*buf//usado para passar estado do conjunto de semáforos
    unsigned short* array//usado para operação em lote
    struct seminfo*_buf//usado para informações de restrição do sistema
}
3.4 semop
  1. int semop(int semid, struct sembuf *sops, size_t nsops);
    Obtém ou libera semáforo
  2. semid: ID do conjunto de semáforos
  3. sops: Apontador para struct sembuf, esta estrutura define uma operação em semáforo
struct sembuf
{
    unsigned short sem_num;//índice do semáforo no conjunto
    short sem_op;//tipo de operação +1 / -1
    short sem_flg;//comum SEM_UNDO (saída anômala)
}
  1. nsops: Total de operações a executar (quantos semáforos executar)
Configurar semáforos para formar sincronização de processos

(memória compartilhada configura ordem de leitura e escrita contínuas)

  1. sem.h
  2. sem.c
  3. texto_sem.c
    Escrita:
#include "sem.h"
 int principal()
 {
     int shmid = shmget((chave_t)1234, 1024, IPC_CREAT | 0600);
     inicializar_sem();
     if(shmid == -1)
     {
        printf("!!!\n");
        sair(1);
     }
     char *p = (char *)shmat(shmid,NULL,0);
     if((char *)-1 == p)
     {
        printf("????");
        sair(1);
     }
     char buff[128] = {0};
     while(1)
     {
        fgets(buff,128,stdin);
        sem_p(0);
        strcpy(p,buff);
        sem_v(1);
        if( strncmp(buff, "fim", 3) == 0)
           break;
     }
     //limpar_sem();
     shmdt(p);
 }

Leitura:

#include "sem.h"
int principal()
{
    int shmid = shmget((chave_t)1234, 1024, IPC_CREAT | 0600);
    inicializar_sem();
    if (shmid == -1)
    {
        printf("!!!\n");
        sair(1);
    }
    char *s = (char *)shmat(shmid, NULL, 0);
    if( (char *)-1 == s)
    {
        printf("????");
        sair(1);
    }
    while(1)
    {
        sem_p(1);
        printf("%s\n",s);
        sem_v(0);
        if( strncmp(s,"fim",3) == 0)
            break;
    }
    limpar_sem();
    shmdt(s);
}

XI. Threads

1. Fundamentos de Threads <pthread.h>

  1. Thread é um caminho de execução dentro de um processo
  2. Thread é a menor unidade de execução que o sistema operacional pode escalonar
  3. Processo é a menor unidade de alocação de recursos
  4. Características das threads:
    a. Leve: Criação e destruição de threads têm custo muito menor que processos, custo de troca de threads baixo (não precisa trocar espaço de endereçamento do processo)
    b. Compartilhamento de recursos
    Threads no mesmo processo compartilham variáveis globais, memória heap, descritores de arquivo, comunicação entre threads simples (podem acessar memória compartilhada)
    c. Fluxo de execução independente
    Cada thread possui seu próprio contador de programa, pilha, conjunto de registradores
    d. Execução concorrente
    Múltiplas threads em CPU multi-core realizarão verdadeira execução paralela, em CPU single-core realizam concorrência por fatia de tempo

2. Função de Criação de Thread pthread_create();

  1. thread: ID da thread criada
  2. attr: Atributos da thread (NULL indica atributos padrão)
  3. start_routine: Função de entrada da thread (função que nova thread começa a executar)
  4. arg: Argumentos passados para função de entrada da thread

Sucesso retorna 0, nova thread começa a executar função start_routine; Falha retorna código de erro não-zero

Características importantes
1. Execução concorrente, nova thread e thread que a criou (thread principal) executam concorrentemente, ordem de escalonamento específica é determinada pelo sistema operacional
2. Compartilhamento de recursos (memória compartilhada, heap, descritores de arquivo)
3. Espaço de pilha independente (variáveis locais)

3. Compilação de Funções de Thread

4. Função para Aguardar Thread Filha pthread_join();

5. Função de Saída de Thread pthread_exit();

6. Sincronização de Threads e Semáforos <semaphore.h>

6.1 sem_init() Função de Inicialização de Semáforo de Thread
  1. int sem_init(sem_t *sem, int pshared, unsigned int valor);
  2. sem: Apontador para sem_t, representa armazenar semáforo
  3. pshared: Determina escopo de compartilhamento do semáforo
    0: Indica compartilhamento apenas entre threads no processo atual
    Valor não-zero: Indica compartilhamento entre múltiplos processos (precisa estar em região de memória compartilhada)
  4. valor: Valor inicial do semáforo, número de recursos
6.2 int sem_wait(sem_t *sem);
  1. Operação atômica, decrementa semáforo em 1
  2. Equivalente a operação p
6.3 int sem_post(sem_t *sem);
  1. Operação atômica, incrementa semáforo em 1
  2. Equivalente a operação v
6.4 int sem_destroy(sem_t *sem);

Destrói semáforo criado, libera todos os recursos relacionados a este semáforo

6.5 Exercício:

Três threads imprimem ABC sequencialmente imprimem três rodadas cada thread imprime apenas um caractere

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>

sem_t sem_a,sem_b,sem_c;
void*fun_a(void* arg)
{
    for(int i=0;i<3;i++)
    {
        sem_wait(&sem_a);
        printf("A");
        fflush(stdout);
        sem_post(&sem_b);
    }
}
void*fun_b(void* arg)
{
    for(int i=0;i<3;i++)
    {
        sem_wait(&sem_b);
        printf("B");
        fflush(stdout);
        sem_post(&sem_c);
    }
}
void*fun_c(void* arg)
{
    for(int i=0;i<3;i++)
    {
        sem_wait(&sem_c);
        printf("C");
        fflush(stdout);
        sem_post(&sem_a);
    }
}
int principal()
{
    pthread_t id[3];
    sem_init(&sem_a,0,1);//inicialização de thread
    sem_init(&sem_b,0,0);
    sem_init(&sem_c,0,0); 
    
    pthread_create(&id[0],NULL,fun_a,NULL);
    pthread_create(&id[1],NULL,fun_b,NULL);
    pthread_create(&id[2],NULL,fun_c,NULL);
    
    for(int i=0;i<3;i++)
        pthread_join(id[i],NULL);
        
    sem_destroy(&sem_a);
    sem_destroy(&sem_b);
    sem_destroy(&sem_c);
    
    return 0;
}

7. Trava de Exclusão Mútua

7.1 pthread_mutex_init(); Inicialização de Trava de Exclusão Mútua
  1. int pthread_mutex_init(pthread mutex_t *restrict mutex,const pthread mutexattr_t *restrict attr);
  2. mutex: Apontador para mutex_t, indica qual trava de exclusão mútua
  3. attr: Indica atributos da trava de exclusão mútua
    NULL: Atributos padrão

pthread mutex_t mutex;//variável global
pthread_mutex_init(mutex,NULL);//em função principal

7.2 int pthread_mutex_lock(pthread_mutex_t * mutex);

Representa travar uma trava de exclusão mútua
Equivalente a operação p

7.3 int pthread_mutex_unlock(pthread_mutex_t *mutex);

Representa destravar uma trava de exclusão mútua
Equivalente a operação v

7.4 Precauções ao Travar e Destravar
  1. Travar e destravar devem ocorrer em pares, caso contrário facilmente leva a deadlock
  2. Evitar travar repetidamente
  3. Garantir que escopo da trava seja pequeno, garantir desempenho concorrente do programa
7.5 int pthread_mutex_destroy(pthread_mutex_t *mutex);

Destruição de trava de exclusão mútua

7.6 Exercício

Usando threads e trava de exclusão mútua saída BBAABBAABBAABBAABBAA

pthread_mutex_t mutex;
int n;
void* fun(void* arg)
{
    pthread_mutex_lock(&mutex);//travar
    printf("A");
    fflush(stdout);
    n = rand() % 3;
    dormir(n);
    
    printf("A");
    fflush(stdout);
    pthread_mutex_unlock(&mutex);//destravar
    n = rand () % 3;
    dormir(n);
}
int principal()
{
    pthread_t id;

    pthread_mutex_init(&mutex,NULL);
    pthread_create(&id, NULL, fun, NULL);
    for(int i=0;i<5;i++)
    {
        pthread_mutex_lock(&mutex);
        printf("B");
        fflush(stdout);
        n = rand() % 3;
        dormir(n);

        printf("B");
        fflush(stdout);
        pthread_mutex_unlock(&mutex);
        n = rand () % 3;
        dormir(n);
    }
    pthread_join(id,NULL);
    return 0;
}

8. Variáveis de Condição:

8.1 int pthread_cond_init()

Inicialização: int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);

8.2 int pthread_cond_wait()

Aguarda thread: int pthread_cond_wait(pthread_cond_t*cond, pthread_mutex_t *mutex);

8.3 int pthread_cond_signal ()

Desperta única thread: int pthread_cond_signal (pthread_cond_t *cond);

8.4 int pthread_cond_broadcast()

Desperta todas as threads aguardando: int pthread_cond_broadcast(pthread_cond_t*cond);

8.5int pthread_cond_destroy()

int pthread_cond_destroy(pthread_cond_t *cond);

8.6 Exemplo de Código

//Quem entra primeiro na fila de espera, é desperto primeiro

pthread_mutex_t mutex;//trava de exclusão mútua
pthread_cond_tcond;//variável de condição

char buff[128]={0};
void* funa(void* arg)
{
    while(1)
    {
        pthread_cond_wait(&cond,&mutex);//entra na fila de espera da thread, bloqueia
        printf(" a= %s\n",buff);
    }
}

void* funb(void* arg)
{
    while(1)
    {
        pthread_cond_wait(&cond,&mutex);//entra na fila de espera da thread, bloqueia
        printf(" b= %s\n",buff);
    }
}

int principal()
{
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    
    pthread_t ida,idb;
    pthread_create(&ida. NULL. funa, NULL);
    pthread_create(&idb, NULL, funb, NULL);
    
    while(1)
    {
        fgets(buff,128,stdin);
        if(strncmp(buff,"fim",3)==0)//quando entrada fim quebra loop da thread principal, mas loop da thread filha ainda está aguardando, portanto travará, pode adicionar destruição de variável de condição após loop principal sair
        {
            pthread_cond_broadcast(&cond);
            break;
        }
        else
            pthread_cond_signal(&cond);
    }
    pthread_join(ida,NULL);
    pthread_join(idb,NULL);
}

9. Trava de Leitura/Escrita:

Trava de leitura/escrita, resolve que threads com mesma operação não bloqueiam
Operações de leitura não são mutuamente exclusivas, leitura/escrita são mutuamente exclusivas, escrita/escrita são mutuamente exclusivas
Trava de leitura: Trava compartilhada, thread solicita esta trava quando precisa ler recurso compartilhado, pode ser mantida por múltiplas threads simultaneamente
Trava de escrita: Trava exclusiva, thread solicita esta trava quando precisa modificar recurso compartilhado, só pode ser mantida por uma thread, e neste momento nenhuma thread pode manter trava de leitura

9.1 int pthread_rwlock_init()

int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);

9.2 int pthread_rwlock_rdlock()

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

9.3 int pthread_rwlock_wrlock()

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

9.4 int pthread_rwlock_unlock()

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

9.5 int pthread_rwlock_destroy()

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

9.6 Exemplo de Código
pthread_relock_t trava;

void* funa(void* arg)//r
{
    for(int i=0;i<20;i++)
    {
        pthread_relock_rdlock(&trava);
        printf("funa início leitura\n");
        int n=rand()%3;
        dormir(n);
        printf("funa fim leitura\n");
        pthread_relock_unlock(&trava);
        
        dormir(1);
    }
}

void* funb(void* arg)//r
{
    for(int i=0;i<20;i++)
    {
        pthread_relock_rdlock(&trava);
        printf("funb início leitura\n");
        int n=rand()%3;
        dormir(n);
        printf("funb fim leitura\n");
        pthread_relock_unlock(&trava);
        
        dormir(1);
    }
}

void* func(void* arg)//w
{
    for(int i=0;i<20;i++)
    {
        pthread_relock_rdlock(&trava);
        printf("func início escrita!!\n");
        int n=rand()%3;
        dormir(n);
        printf("func fim escrita!!\n");
        pthread_relock_unlock(&trava);
        
        dormir(1);//libera recursos para outros loops for
    }
}

int principal()
{
    pthread_rwlock_init(&trava, NULL);
    
    pthread_t ida,idb,idc;
    pthread_create(&ida. NULL. funa, NULL);
    pthread_create(&idb, NULL, funb, NULL);
    pthread_create(&idc, NULL, funb, NULL);
    
    pthread_join(ida,NULL);
    pthread_join(idb,NULL);
    pthread_join(idc,NULL);
    pthread_rwlock_destroy(&trava);
}

10. Diferenciação entre Quatro Tipos de Sincronização de Threads:

11. Diferenciação entre Processo e Thread:

12. Funções Seguras para Threads:

Exercício: Thread principal divide "123456", thread filha divide "abcdef", em suas respectivas threads imprimem caracteres divididos

saveptr: Apontador para apontador de caractere (apontador duplo)
Último argumento é dar à função um "bloco de notas temporário", para lembrar onde dividiu.

char *buff="1 2 3 4 5 6";
char *buffer="a b c d e f";
char **ptr=NULL;

void* fun(void* arg)
{
    char*s=strtok_r(buffer,' ',ptr);
    while(s!=NULL)
    {
        printf("fun s =%s\n",s);
        dormir(1);
        s=strtok_r(NULL,' ',ptr);
    }
}

int principal()
{
    pthread_t id;
    pthread_create(&id, NULL,fun, NULL);
    char*s=strtok_r(buff,' ',ptr);
    while(s!=NULL)
    {
        printf("principal s =%s\n",s);
        dormir(1);
        s=strtok_r(NULL,' ',ptr);
    }
    
    pthread_join(id,NULL);
}

Exercícios Práticos

  1. kill -9 mata processo mas não limpa recursos
    kill -15 mata processo e limpa recursos simultaneamente
  2. Processo zumbi ocupa gerenciador de recursos de processo e não CPU
  3. /usr: Armazena informações de ambiente do usuário (bibliotecas, comandos)
    /bin: Comandos comuns do usuário
  4. Quais são as comunicações entre processos (IPC)? Quais são suas características?
    Pipes: Pipe com nome: Presente no sistema de arquivos
    Pipe sem nome: Não presente no sistema de arquivos, suporta apenas processos com relação de parentesco
    Memória compartilhada: Grande capacidade, alta velocidade, comunicação simples, requer sincronização
    Semáforos não podem transmitir dados
    (Sockets)
  5. Escrever programa C mycp.c, implementar funcionalidade similar ao comando cp:
    Uso: ./mycp arquivo_origem arquivo_destino
    Requisitos:
    Usar chamadas de sistema open(), read(), write() (proibir uso de fopen/fread e outras funções de biblioteca padrão)
    Tamanho do buffer definido como 4096 bytes
  6. Comunicação entre processos pai e filho usando pipe
    Processo pai cria um pipe, depois fork() subprocesso:
    Subprocesso: Escreve string "Olá do filho" no pipe
    Processo pai: Lê dados do pipe e imprime na tela
  7. Criar 1 thread principal + 2 threads de trabalho (thread ímpar, thread par), colaboram para completar a tarefa:
    Thread principal: Continua gerando números aleatórios, intervalo 1~100
    Após gerar número aleatório, coloca em variável compartilhada conforme par ou ímpar
    Se for par: Notifica thread par processar
    Se for ímpar: Notifica thread ímpar processar
    Thread ímpar: Aguarda chegada de número ímpar, acumula soma dos ímpares
    Thread par: Aguarda chegada de número par, acumula soma dos pares
    Finalmente imprime resultados após gerar 30 números aleatórios

Fase de Programação de Rede

I. Introdução Básica a Redes de Computadores

1. Conceitos Básicos de Rede

  1. Rede
    Equipamentos de rede incluem: switch, roteador, hub
    Meios de transmissão incluem: fibra óptica (par trançado, cabo coaxial)
  2. Internet
1.3. Endereço IP

Endereço IP é um identificador exclusivo atribuído em escala global para cada host (ou roteador) na Internet para cada interface.
Existem dois formatos de endereço IP: IPV4(255.31.255.1) e IPV6(2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b)
Formato IPV4 32 bits "dividido em quatro segmentos, cada segmento 8 bits (valor 0-255), representado em decimal.
Formato IPV6 128 bits "dividido em oito segmentos, cada segmento 16 bits, representado por 4 números hexadecimais.
Todos os hosts na mesma rede física usam o mesmo número de rede, apenas número do host é diferente.
◼ Classe A: Intervalo endereço IP 0.0.0.0~127.255.255.255.
◼ Classe B: Intervalo endereço IP 128.0.0.0~191.255.255.255.
◼ Classe C: Intervalo endereço IP 192.0.0.0~223.255.255.255.

1.4 Usar comando "ifconfig" no linux para visualizar seu endereço ip

Função principal do endereço de loopback local: Comunicação entre processos na mesma máquina (comunicação consigo mesmo), autoverificação e diagnóstico do sistema

1.5 Endereço MAC

ether é o endereço físico MAC

1.6 Protocolos de Rede

◼ HTTP: Protocolo de Transferência de Hipertexto
◼ TCP : Protocolo de Controle de Transmissão (TCP, Transmission Control Protocol) é um protocolo de comunicação da camada de transporte orientado a conexão, confiável e baseado em fluxo de bytes
◼ UDP : Protocolo de Datagramas do Usuário
◼ IP : Internet Protocol abreviado IP, também traduzido como protocolo inter-rede ou protocolo da Internet

2. Modelos de Camadas de Rede

2.1 Modelo OSI de 7 camadas (e estrutura de 4 camadas do conjunto de protocolos tcp/ip)
  1. Função do modelo de 7 camadas, protocolos de cada camada e formato de dados enviados

Camada de rede: Escolhe caminho de comunicação adequado (protocolo IP)
Camada de transporte: Diferente da camada de rede que usa comunicação hop-by-hop, camada de transporte só se preocupa com ponto inicial e final da comunicação, sem se importar com processo de retransmissão dos pacotes de dados.

  1. Estrutura de 4 camadas: Funde várias camadas em uma
    Camada de enlace de dados e camada física integradas para camada de enlace de dados
    Camada de aplicação, camada de apresentação, camada de sessão integradas para camada de aplicação
  2. Estrutura de 5 camadas: Camada de aplicação, camada de apresentação, camada de sessão integradas para camada de aplicação

3. Fluxo de Comunicação de Aplicações de Rede

Dados "olá" enviados da camada de aplicação para camada de transporte, camada de transporte adiciona cabeçalho de protocolo tcp ou udp na frente dos dados, envia mensagem inteira para camada de rede, camada de rede adiciona seu próprio cabeçalho IP, depois envia todos os dados para camada de enlace de dados. Camada de enlace de dados encapsula dados em unidade de dados que pode ser transmitida independentemente na rede, ou seja, quadro de dados. Quadro de dados encapsulado é transmitido via rede para outro host, depois desempacotado de baixo para cima, e parte dos dados é enviada para camada de aplicação.

II. Programação Socket de Rede

1. Sequência de Bytes do Host e Sequência de Bytes da Rede (Funções de Conversão)

Sequência de bytes do host é dividida em big-endian e little-endian, diferentes hosts podem usar sequências de bytes diferentes. Big-endian é quando bytes altos de um inteiro são armazenados em endereços baixos da memória, bytes baixos em endereços altos. Little-endian é quando bytes altos do inteiro são armazenados em endereços altos da memória, e bytes baixos em endereços baixos.
Big-endian torna-se sequência de bytes da rede. (Ao enviar dados para rede, dados inteiros usam big-endian)
Sequência de bytes do host padrão little-endian

#include <netinet/in.h>
uint32_t htonl(uint32_t hostlong); // Converte inteiro longo de sequência de bytes do host para rede
uint32_t ntohl(uint32_t netlong); // Converte inteiro longo de sequência de bytes da rede para host
uint16_t htons(uint16_t hostshort); // Converte inteiro curto de sequência de bytes do host para rede
uint16_t ntohs(uint16_t netshort); // Converte inteiro curto de sequência de bytes da rede para host

2. Funções de Conversão de Endereço IP

in_addr_t inet_addr(const char *cp); // Converte endereço IPV4 representado por string para sequência de bytes da rede
char* inet_ntoa(struct in_addr in); // Converte endereço IPV4 de sequência de bytes da rede para representação por string

III. Fluxo de Programação TCP

TCP provê serviço orientado a conexão, confiável e de fluxo de bytes. Fluxo de programação do lado servidor e cliente TCP segue abaixo:
socket() Método usado para criar socket, com socket pode enviar e receber dados via rede.
bind() Método usado para especificar endereço IP e porta que socket usa.
listen() Método usado para criar fila de escuta (socket de escuta). Existem dois tipos de fila de escuta, uma armazena conexões não concluídas com três-way handshake, outra armazena conexões concluídas. Segundo parâmetro de listen() especifica comprimento da fila de conexões concluídas.
accept() Processa conexões armazenadas na fila de conexões concluídas criada por listen. Para cada conexão processada, accept() retorna descritor de socket correspondente a essa conexão. Se fila estiver vazia, accept bloqueia.
connect() Método geralmente executado por programa cliente, precisa especificar endereço IP e porta do servidor alvo. Após execução, realiza three-way handshake, estabelece conexão.

Protocolo TCP:
Três características: Orientado a conexão, confiável, orientado a fluxo de bytes

  1. Orientado a conexão: Antes de comunicação precisa de confirmação de three-way handshake, ao final precisa de confirmação de four-way handshake (similar a ligação telefônica)
  2. Confiável: Numera cada byte, receptor usa sinal de confirmação ACK para informar número de sequência esperado para próxima recepção
    Mecanismo de retransmissão por timeout: Quando transmissor não recebe sinal ACK dentro do tempo, retransmite dados
  3. Fluxo de bytes: Não mantém informações de fronteira, divide dados em "segmentos TCP" para transmissão, receptor precisa analisar fronteiras das mensagens por conta própria
    "olaMundo"

1. Estabelecimento e Encerramento de Conexão

  1. Protocolo TCP provê: Orientado a conexão, confiável (retroalimentação após recepção), serviço de fluxo de bytes.
    Partes que usam protocolo TCP para comunicação devem primeiro estabelecer conexão, depois podem iniciar leitura e escrita de dados. Ambas partes devem alocar recursos internos necessários para esta conexão, para gerenciar estado da conexão e transmissão de dados na conexão.
    Conexão TCP é full-duplex, dados de ambas partes podem ser lidos e escritos através de uma conexão. Após troca de dados completa, ambas partes da comunicação devem desconectar para liberar recursos do sistema.
  2. Three-way handshake ocorre quando cliente executa connect(), se método retorna com sucesso, indica que three-way handshake foi estabelecido. (três confirmações)
  3. Four-way handshake ocorre quando cliente ou servidor executam close() para fechar conexão

2. Funções e Funcionalidades do Fluxo de Programação

No terminal touch ser.c cli.c cria lado servidor e cliente.c

  1. ser.c: Lado servidor
  2. cli.c: Cliente.c
2.1 int socket(int dominio,int tipo, int protocolo);

Cria um socket

  1. dominio (família de protocolos/família de endereços): Família de endereços de rede usada
    AF_INET: Família de endereços IPV4
    AF_INET6: Família de endereços IPV6
    AF_UNIX ou AF_LOCAL: Família de endereços de comunicação local, usada para comunicação entre processos diferentes no mesmo host
    AF_BLUETOOTH: Família de endereços Bluetooth, usada para comunicação entre dispositivos Bluetooth
  2. tipo: Modo de comunicação da camada de transporte
    SOCK_STRAM: Socket em fluxo TCP
    SOCK_DGRAM: Socket de datagrama UDP
  3. protocolo: Especifica protocolo correspondente
    Preencher 0 por padrão

Implementação do código:

2.2 int bind(int sockfd, const struct sockaddr*addr,socklen_t addrlen);

Vincula socket correspondante

  1. sockfd: Retornado por socket(), é descritor de socket correspondente
  2. addr: Apontador para sockaddr, esta estrutura contém endereço IP e porta necessários
struct sockaddr:
{
    sa_family_t sin_family;//família de endereços (mesma família de endereços usada ao criar socket)
    in_port_tsin_port;//porta (deve estar em sequência de bytes da rede)
    struct in_addr sin_addr;// endereço IPV4 (sequência de bytes da rede)
}
struct in_ado
{
    r sin_addr
    uin32_t s_addr;//endereço IPV4 (sequência de bytes da rede)
}
  1. addrlen: Tamanho da estrutura apontada, obtido por sizeof()
    Implementação do código: struct sockaddr_in//precisa incluir cabeçalhos: #include<netinet/in.h> e #include<arpa/inet.h>
    saddr: Estrutura do servidor
    caddr: Estrutura do cliente
2.3 int listen(int sockfd, int backlog);

Escuta se socket está sendo conectado

  1. sockfd: Descritor de socket monitorado
  2. backlog: Comprimento máximo da fila de escuta 5 (pode conectar infinitos, mas só escuta um por vez)
2.4 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Realiza conexão de socket

  1. sockfd: Descritor de socket a conectar
  2. addr: Apontador para sockaddr, informações do socket do cliente que inicia conexão serão armazenadas nesta estrutura
  3. addrlen: Tamanho da estrutura de armazenamento
    Valor de retorno: <0 falha; Sucesso retorna descritor de arquivo do socket de conexão
    Implementação do código:
    Usar while(1) para manter conexão contínua Após implementação funcional e while loop terminar, precisa close(c)
2.5 ssize_t recv(int sockfd, void *buf, size_t len, int flags);

Recebe dados

  1. sockfd: Descritor de socket de conexão receptora
  2. buf: Buffer de recepção
  3. len: Tamanho do buffer
  4. flags: Comportamento:
    0: Padrão, recv() bloqueará até receber dados
    MSG_PEEK: Observa dados, retorna conteúdo dos dados, mas não remove dados do buffer de recepção, propósito é para próxima chamada recv ainda ver dados

recv() não necessariamente recebe todos dados de uma vez

  1. Razão: TCP é protocolo em fluxo, não mantém informações de fronteira, se send enviou 1000 bytes de dados, receptor recv recebe 500 bytes, restantes 500 bytes precisam de próxima chamada recv() para receber completamente. (atraso de rede, fragmentação de pacotes, tamanho do buffer de recepção afetam número de bytes recebidos por recv)
  2. Solução da razão: Recepção em loop, até receber quantidade esperada de dados
    Implementação do código: Dentro de while loop receber e enviar dados
2.6 ssize_t send(int sockfd, const void *buf, size_t len, int flags);

Envia

  1. sockfd: Descritor de socket de conexão de envio
  2. buf: Buffer de envio
  3. len: Tamanho do buffer
  4. flags: Comportamento: Padrão 0
2.7 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Conecta (three-way handshake ocorre em connect)

  1. sockfd: Descritor de socket
  2. addr: Apontador para sockaddr, esta estrutura contém endereço IP e porta necessários
  3. addrlen: Tamanho da estrutura apontada, obtido por sizeof() Preencher diretamente informações do servidor alvo que queremos conectar
2.8 Comando netstat -natp e TIME_WAIT
  1. netstat -natp: Visualiza estado da rede durante conexão TCP
  2. TIME_WAIT: Estado TIME_WAIT geralmente aparece na parte que encerra ativamente conexão. Após estado aparecer, manterá tempo de 2MSL (duas vezes ciclo de vida dos dados), só pode fechar completamente. MSL é tempo máximo de sobrevivência do segmento TCP na rede.
    (Desconexão cliente: Precisa limpar dados)
  3. Razões para existência do estado TIME_WAIT:
    ◼ Terminação confiável de conexão TCP.
    ◼ Garantir tempo suficiente para identificação e descarte de segmentos TCP atrasados.

3. Implementação Multi-thread para Comunicação Multi-cliente

Comunicação multi-cliente:
Essência: Implementar criação de múltiplos sockets de conexão, alocar para cada cliente um socket para comunicação com servidor
Multi-thread ou multi-processo

4. Implementação de Servidor Multi-processo e Comunicação Contínua com Cliente

5. Diagrama de Transição de Estados TCP

6. TIME_WAIT

IV. Protocolo UDP (Protocolo Camada de Transporte)

1.Características UDP:

  1. Sem conexão
    Antes de comunicação não precisa de three-way handshake, ao enviar dados, diretamente especifica endereço destino do envio, reduz overhead de estabelecimento e manutenção de conexão, atraso de dados relativamente baixo
  2. Não confiável
    Não garante que dados cheguem, nem garante integridade e sequência dos dados sem confirmação, retransmissão, mecanismo de resposta, após envio considera transmissão completa
  3. Datagrama
    Dados transmitidos em unidade de "datagrama", cada datagrama contém endereço de origem e destino completos se tamanhos dos buffers do transmissor e receptor forem diferentes, causará truncamento de dados
  4. Leve e eficiente
    Cabeçalho UDP tem apenas 8 bytes, menor que 20 bytes do TCP, overhead de protocolo baixo não precisa manter estado de conexão, portanto servidor pode processar grande quantidade de clientes simultaneamente

2.Cenários de uso UDP:

  1. Chamada em tempo real: Áudio ou vídeo (permite perda de alguns pacotes, baixo atraso)
  2. Transmissão de mídia em fluxo: Streaming ao vivo (garante fluidez e não completude)
  3. Transmissão de dados de jogos: Jogos online multiplayer (sensível a atraso, perda leve de pacotes não afeta)
  4. Solicitação simples resposta: Consulta DNS (volume de dados pequeno, não precisa garantia complexa de confiabilidade)
  5. Broadcast: Suporta um-para-muitos, aplicável a distribuição de dados

Cenários de uso TCP: Confiabilidade prioritária (transferência de arquivos, comunicação HTTP)
Cenários de uso UDP: Tempo real prioritário (streaming ao vivo, jogos)

3. Fluxo de Programação e Funcionalidades das Funções

3.1 ssize_t recvfrom(); <sys/socket.h>

UDP recebe pacote + obtém endereço da outra parte
Recebe dados + simultaneamente obtém IP e porta do transmissor

  1. int sockfd, // socket UDP
  2. void *buf, // buffer de recepção
  3. size_t len, // tamanho do buffer
  4. int flags, // geralmente 0
  5. struct sockaddr *from, // saída: endereço da outra parte
  6. socklen_t *addrlen // entrada/saída: comprimento do endereço
  7. Valor de retorno: (valor de retorno é comprimento dos caracteres recebidos)
    0: Quantos bytes recebidos
    0: Outra parte envia pacote vazio (UDP permite)
    -1: Erro, errno definirá valor
3.2 ssize_t sendto();

UDP envia dados + especifica para quem enviar (IP + porta)

  1. int sockfd, // socket UDP
  2. const void *buf, // dados a enviar
  3. size_t len, // comprimento dos dados
  4. int flags, // geralmente 0
  5. const struct sockaddr *to, // endereço destino
  6. socklen_t addrlen // tamanho da estrutura de endereço
  7. Valor de retorno: (valor de retorno é comprimento dos caracteres enviados)
    0: Número de bytes enviados com sucesso (UDP geralmente igual a len)
    -1: Falha, errno armazena código de erro

4.Projeto Dois: Meio de Comunicação TCP Multi-thread:

Servidor e cliente precisam de duas threads
Estabelecer múltiplas conexões de cliente, quando cliente se junta, servidor transmite para todos clientes, avisa que novo cliente se juntou; quando um cliente envia mensagem, outros clientes podem receber mensagem enviada por este cliente (exibe qual cliente enviou), quando cliente sai, outros clientes também podem receber aviso de que cliente saiu da sala de bate-papo (usa multi-thread)

  1. Quando cliente se junta, pode inserir seu apelido, para servidor transmitir
    Servidor usa array para armazenar socket de conexão de cada cliente, quando precisa transmitir, usa loop for para enviar informação para cada cliente send
    Atenção: Cliente que sai, servidor precisa excluir este cliente no array, pode precisar sincronização de threads, por exemplo ao excluir, só deve haver uma thread operando
    ps: Mesma porta pode ser usada por TCP e UDP diferentes:
    Porque protocolos diferentes, TCP usa número de protocolo 6, enquanto UDP usa 17, ao vincular porta, servidor automaticamente atribuirá número de porta

V. Protocolo HTTP - Protocolo de Transferência de Hipertexto

Principalmente responsável por comunicação entre navegador e servidor

1. Principais características:

  1. .Sem estado: Cada requisição de comunicação é independente, servidor não lembra informações da requisição anterior, ex: após atualizar site, servidor não reconhece automaticamente "este é o usuário anterior"
  2. Baseado em "solicitação—resposta": Comunicação deve ser iniciada por cliente (digitar endereço web), servidor então retorna resposta conforme conteúdo da solicitação (carregar conteúdo da página)
  3. Transmissão em texto claro: Dados não são criptografados durante transmissão, podem ser interceptados por terceiros

2. Soluções para Desvantagens:

2.1 Solução para "sem estado":
  1. cookie: Servidor envia pequeno segmento de dados para cliente via cabeçalho de resposta, cliente automaticamente carrega este segmento em requisições subsequentes, servidor reconhece se usuário já logou por detecção
  2. session: Servidor criará uma sessão para cada usuário logado, armazena estado da sessão, depois envia sessionID via cookie, usuário reconhecido por este ID subsequentemente
  3. token: Após cliente logar com sucesso, servidor retornará um token criptografado, cliente ao发起 requisição, carregará token no cabeçalho da requisição, servidor reconhece usuário por decriptografar token
2.2 Solução para "transmissão em texto claro" (https://www.baidu.com/)
  1. Usar HTTPS com criptografia melhor para resolver:
    Portas usadas diferentes: HTTP usa porta 80, HTTPS usa porta 443
    Identificação de endereço diferente: HTTP começa com http: HTTPS começa com https
    HTTPS baseado em criptografia SSL/TLS
    HTTP aplicável a dados não sensíveis (artigos públicos, documentos governamentais públicos)
    HTTPS aplicável a pagamentos, logins, transferências

3. Processo de Transmissão HTTP baseado em "quatro interações cliente—servidor"

  1. Estabelecer conexão TCP (three-way handshake)
  2. Cliente envia solicitação HTTP:
    Cliente (navegador) encapsula e envia dados de solicitação para servidor conforme formato do protocolo HTTP
    Conteúdo da solicitação dividido em três partes:
    a. Linha de solicitação (define método de solicitação GET/POST), fornece caminho do recurso destino, versão HTTP
    b. Cabeçalho de solicitação: Carrega informações adicionais, tipo de navegador, formato de dados aceito, cookie
    c. Corpo da solicitação (opcional): Só aparece no método POST, usado para传递 dados de formulário, arquivos, etc
    d. Métodos de solicitação HTTP:
  3. Servidor processa solicitação do cliente e responde
    a. Analisa solicitação do cliente (linha de solicitação, cabeçalho de solicitação, corpo de solicitação) análise, processa recursos relacionados()
    Por exemplo , servidor então lerá arquivo /index.html
    b. Executa lógica de negócio (consulta banco de dados, gera página dinâmica)
    c. Encapsula dados de resposta, dividido em três partes
    Linha de resposta: Contém versão HTTP, código de status (200 ok,404(recurso não existe),403(acesso não autorizado)), descrição de status
    Cabeçalho de resposta: Carrega informações da versão do servidor, tipo de dados, comprimento dos dados
    Corpo da resposta (conteúdo principal): Código HTML, dados binários de imagens, dados JSON
  4. Fechar conexão TCP (four-way handshake)

4. Conexão longa - Conexão curta

Atenção:

  1. Conexão longa não é "conexão permanente", durante conexão longa, valor de "tempo de timeout inativo" é configurado, se tempo inativo exceder este valor, desconexão automática, evitando desperdício de recursos
  2. Alguns servidores antigos não são compatíveis, quando precisa de conexão longa, deve declarar manualmente, declaração explícita

Cenários de uso:

  1. Conexão curta: Chamada de interface, consulta simples de dados
  2. Conexão longa: Cenários de múltiplas solicitações (carregar página, recursos da página inicial do APP)

5. Protocolos e Funções

  1. DNS (Protocolo de Resolução de Nome de Domínio): Resolve nome de domínio (www.xxxx.com) para endereço IP (conveniente e fácil de lembrar)
  2. ARP (Protocolo de Resolução de Endereço): Resolve endereço IP para endereço físico MAC (conveniente para encontrar endereço físico)
  3. NAT (Tradução de Endereço de Rede): Converte IP privado para IP público (múltiplos dispositivos compartilham mesmo endereço IP público, resolve escassez de recursos)
  4. DHCP (Protocolo de Atribuição Dinâmica de Host): Quando host se conecta, automaticamente atribui endereço IP privado (não precisa configurar IP manualmente, completa configuração de rede do dispositivo automaticamente)

Quando bind porta pequena, precisa trocar para administrador

Usar navegador para acessar servidor: Abre arquivo real Obtém conteúdo do arquivo Envia para cliente

//Após execução, terminal mostra dados da solicitação

6. Framework de Frontend VUE

HTML: Linguagem de Marcação de Hipertexto, usada para criar linguagem de marcação padrão de páginas web, configura página web através de tags
CSS: Folha de Estilos em Cascata, usada para controlar estilo de exibição de elementos HTML, cor, fonte, layout
JS: Linguagem de script para interação web, pode implementar efeitos dinâmicos, validação de formulários, processamento de dados

/index.html é arquivo de página inicial padrão, quando usuário visita algum domínio, mas não especifica nome de arquivo específico, servidor automaticamente procurará este arquivo e retornará conteúdo deste arquivo, este arquivo é arquivo básico que compõe página web
Pesquisar: Tabela de cores RGB

Tags: Linux processos threads redes TCP

Publicado em 6-19 03:47