Jump to content

Felipe.Silva

Membros
  • Posts

    78
  • Joined

  • Last visited

  • Country

    Brazil

4 Followers

Recent Profile Visitors

3,163 profile views

Felipe.Silva's Achievements

66

Reputation

  1. Você copiou e colou o código que te passei ou redigitou tudo? Se for o segundo caso, cole seu código aqui para eu ver (por completo). Ah, quando for exibir uma mensagem de erro é melhor mostrar ela por completo. Cada linha e cada letra. Pode até tirar um print do terminal também se quiser. Só com essa mensagem aí é impossível eu dizer o que está errado. Eu preciso das duas coisas: A mensagem de erro completa e o código completo que você escreveu. Uma dica quando for colar o código aqui, clique nesse botão com o símbolo "<>" que ele formata o texto como código:
  2. A linha `global_assembly:` deveria ser global seguido de assembly. Onde global é uma diretiva que será explicada posteriormente, e assembly seria o nome do rótulo logo abaixo (será explicado também). bits 64 global assembly assembly: mov eax, 777 ret
  3. Nunca usei a API deles mas confira na documentação na parte que fala sobre o POST de notificação. Segundo o que tá lá um PaymentId é enviado na requisição, o que te permite consultar o status da transação. O fluxo seria tipo: Recebi o POST com PaymentId. Uso o PaymentId para consultar na API o status da transação. Na documentação também diz que é possível cadastrar headers que serão enviados nessa requisição com um valor fixo. Isso pode servir como filtro para ter uma segurança mínima de que foi mesmo a CIELO que enviou o POST. Mas eu não confiaria tanto assim. 🙂
  4. Cara, você tem que primeiro obter a entrada do usuário e depois fazer o cálculo. Tu tá calculando o valor da variável `imc` antes, impossível obter o resultado correto. Mova a atribuição da variável imc para ficar após a leitura das variáveis peso e altura. Exemplo: imc = peso/(altura*altura); cout << "O seu IMC eh: " << imc << endl; return 0;
  5. Veja sobre a syscall mremap. * https://man7.org/linux/man-pages/man2/mremap.2.html
  6. 413 downloads

    Livro em Português sobre Assembly em constante desenvolvimento. É de autoria do @Felipe.Silva, membro da comunidade aqui! 🤗
  7. Você parou de atualizar head e tail em myMalloc()... Mas atualizar esses ponteiros é importante para o funcionamento desse sistema aí. Por exemplo: O que a função find_block() retorna se head for NULL? Ela só pode retornar NULL... Se você nunca inicializar head para apontar para um bloco, find_block() vai sempre retornar NULL. Você pode fazer algo mais ou menos assim: void *myMalloc(size_t size) { t_block b = find_block(size); if (b) { b->free = 0; } else { b = extend_heap(size); if (!b) return NULL; if (!head) head = b; tail = b; } return (b + 1); } Repare como eu inicializo head quando extend_heap() é chamado pela primeira vez. E repare que sempre é necessário chamá-lo eu atualizo tail. Entendeu a ideia? Assim head está sempre apontando para o primeiro bloco alocado e tail sempre apontando para o último. Não esquece disso também na myFree(). Ela precisa atualizar tail toda vez que liberar um bloco. E lembre-se que existe a possibilidade de myFree() ser usada no bloco apontado por head, neste caso o ponteiro head passaria a apontar para uma memória que já foi liberada. Talvez a solução mais simples neste caso seja redefinir head para NULL e "recomeçar". 🙂
  8. void *myMalloc(size_t size) { // TODO t_block b; if(head){ tail = head; // .... Esse "tail = head" não me parece certo. Pela lógica do código tail deveria ser atualizado para o novo bloco alocado na memória toda vez que a função extend_heap() precisar ser chamada... Do jeito que está tail volta a apontar para head a cada nova chamada de myMalloc() (exceto a primeira chamada onde head ainda será NULL). Sugiro atualizar o código para as vezes que precisou chamar "b = extend_heap(size);" atualizar tail para apontar para b. Ah, e qual é a lógica do else dentro da função myFree()? Também toma cuidado com a diferença entre = e ==. Dentro do if deveria ser == certo? Outra coisa que não entendi é isso: b = tail->next = NULL; Você quer modificar b para NULL? Mas logo em seguida você executa brk(b); o que não faria sentido passar NULL para esta função. E b também não é inicializada caso o valid_addr retorne false, pois você declarou a variável mas só a inicializa dentro do if. Essa estrutura s_block foi o professor que passou assim? Pois eu vejo um probleminha ao precisar desalocar memória e atualizar o valor de tail. Como você vai pegar o endereço do penúltimo bloco sem um ponteiro para o bloco anterior? Até daria para percorrer a lista para isso, mas seria mais eficiente ter um ponteiro t_block previous na estrutura. 🙂 E uma coisa interessante é se sua myFree() liberasse todos os últimos blocos que estejam marcados como free, e não somente o último. Ou então deixar apenas N blocos no final e liberar o resto, assim poupa tempo pro myMalloc() não precisar de extend_heap() o tempo todo. Ah, uma dica: Antes de mais nada sugiro implementar logo a função debugBlockList(). Ela vai te ajudar a "visualizar" a lista e assim ficará mais fácil entender o que está funcionando conforme deveria e o que não está.
  9. Esse conceito de "unidade de armazenamento" associado à uma letra "só" existe no *Windows. No Linux (e outros UNIX-Like) os volumes são montados em diretórios. * "Só" porque MS-DOS e ReactOS também usam esse conceito. Se bobear pode haver algum sistema operacional desconhecido que faz isso também.
  10. "Nova" entre aspas. A linguagem existe desde 2005, mais velha que Go. 😁
  11. A heap é uma estrutura especial de memória usada pelo processo. O que tem de especial nela é o fato de seu tamanho ser variável, já que sua memória pode ser alocada ou desalocada dinamicamente pelo processo. Isso pode ser feito usando syscalls do sistema operacional e o mesmo é responsável por alocar mais páginas de memória para a seção caso seja necessário. No Linux o segmento de dados pode ser aumentado ou diminuído usando a syscall brk, e é esse espaço de memória que os programas normalmente usam para a heap. Em C nós normalmente não usamos a heap diretamente e, ao invés disso, usamos a função malloc() e semelhantes para lidar com a alocação dinâmica de memória. O que alguns não sabem é que na verdade chamadas para a função malloc() ou free() não necessariamente irão resultar na alocação ou desalocação de memória para o processo. Isso se dá porque é a libc quem de fato gerencia a heap e nós não estamos diretamente solicitando ou liberando memória para o sistema operacional. *libc é a biblioteca padrão da linguagem C que contém funções essenciais para tarefas básicas como: Manipulação de strings e arquivos, entrada e saída de dados, funções básicas de matemática etc. A função malloc(), de acordo com a implementação da glibc, usa o segmento de dados, o dividindo em uma ou mais regiões de memória que eles chamam de “arenas”. A arena principal corresponde à heap original do processo, porém outras arenas também podem ser alocadas para o processo. Inicialmente cada thread criada no processo tem uma arena individual até atingir um certo limite pré-definido de arenas que podem ser alocadas no processo. Atingindo esse limite, as threads posteriores passam a compartilhar a mesma arena. Em cada arena de memória existem divisões da memória que são chamadas de maneira homônima de heap, e são nessas heaps que a função malloc() de fato aloca memória para o usuário da libc. Cada bloco de memória que malloc() aloca na heap é chamado de chunk, e cada chunk contém metadados usados pelo sistema interno de malloc para organizar a heap como uma lista duplamente encadeada. Para fins de ilustração, abaixo está a estrutura de um chunk, usada na glibc: struct malloc_chunk { INTERNAL_SIZE_T mchunk_prev_size; INTERNAL_SIZE_T mchunk_size; struct malloc_chunk* fd; struct malloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; struct malloc_chunk* bk_nextsize; O valor mchunk_prev_size seria o tamanho do chunk anterior e mchunk_size o tamanho do chunk atual. Os ponteiros *fd e *bk são usados somente quando o chunk está livre, e seriam ponteiros usados para a lista circular duplamente encadeada de chunks que apontam para o chunk posterior e anterior, respectivamente. No entanto, essa estrutura não representa muito claramente como o chunk é de fato usado pelo sistema de malloc, na figura abaixo isso é ilustrado com mais precisão. O ponteiro que malloc() retorna não aponta para o início do chunk mas sim para o início do espaço de memória que pode ser usado pelo usuário. O tamanho do espaço de memória de um chunk é alinhado pelo tamanho de um double word na arquitetura. Caso malloc() seja chamado passando um tamanho desalinhado como argumento, um espaço extra é alocado para manter o alinhamento. Por exemplo, se o alinhamento está sendo feito para 8 bytes e malloc é chamada com 9 como argumento, malloc irá te devolver um chunk com 16 bytes de espaço de memória usável. Além do alinhamento no tamanho do chunk, também existe um alinhamento no endereço de memória retornado por malloc() que é sempre alinhado para o tamanho de uma word. Isso é feito porque em algumas arquiteturas esse alinhamento de memória é necessário para se evitar uma exceção. Em outras arquiteturas (x86, por exemplo) o alinhamento melhora a performance do processador no acesso à memória. Como existe esse alinhamento no tamanho de um chunk isso garante que os três bits menos significativos de mchunk_size não sejam necessários para definir o tamanho do chunk. Se aproveitando disso, os três últimos bits são usados como flags para determinar alguns metadados usados pelo sistema de chunks. O bit M indica que o chunk não pertence a nenhuma arena e, ao invés disso, foi alocado dinamicamente em uma memória mapeada. Caso este bit esteja ligado, os outros dois são ignorados. No contexto de um chunk livre este bit está sempre desligado, tendo em vista que a lista encadeada de chunks livres somente se aplica a chunks que estão em alguma arena. Os chunks diretamente mapeados na memória (com bit M ligado) são criados para chunks muito grandes. Esses chunks quando liberados com a função free() são imediatamente liberados da memória. Por outro lado, usar free() em um chunk comum não necessariamente irá liberar memória para o sistema operacional. O que free() faz nesse caso é marcar o chunk como livre o adicionando de volta à lista de chunks livres. Assim como é indicado nesse trecho da glibc: /* Mark the chunk as belonging to the library again. */ (void)tag_region (chunk2mem (p), memsize (p)); Repare como o comentário descreve a ação como “marcar o chunk como pertencente à biblioteca novamente”, e é efetivamente isso que a função free() faz, não sendo necessariamente uma liberação de memória para o sistema operacional. Inclusive um recurso de otimização que a glibc usa é o que eles chamam de tcache (Thread Local Cache), que se trata de uma lista de chunks existente em cada thread individualmente. Quando você aloca um novo chunk na thread e posteriormente o libera, ele é adicionado ao tcache daquela thread e pode ser reutilizado em uma nova alocação posterior. Um adendo que a função free() pode efetivamente liberar memória para o sistema operacional se houver vários chunks livres no topo do segmento de dados (o que raramente acontece). Ela faz isso chamando a função interna systrim(), que por sua vez (no Linux) usa a syscall brk para diminuir novamente o segmento de dados. Um detalhe interessante que vale citar aqui é que na glibc (no Linux) existem as funções brk e sbrk que servem como wrappers para aumentar/diminuir o segmento de dados. O sistema de liberação de memória do systrim() espera que essas funções não sejam utilizadas diretamente para poder fazer a liberação de memória apropriadamente. Se você usá-las em seu código por algum motivo, irá “quebrar” o sistema de liberação de memória automático do free(), o fazendo não mais liberar memória quando é usado em chunks de arenas. Logo, não é recomendável que você use essas funções diretamente a menos que você esteja implementando seu próprio sistema de gerenciamento de memória dinâmica. O código abaixo é um experimento a fim de vermos na prática os metadados do chunk no Linux: // gcc test.c -o test #include <stdio.h> #include <stdlib.h> int main(void) { size_t *content = malloc(8); size_t chunk_size = content[-1] & ~0b111; size_t chunk_flags = content[-1] & 0b111; printf("size: %zu\nflags: %zu\n", chunk_size, chunk_flags); return 0; } No meu Linux é retornado 32 como tamanho do chunk e 1 como flag, indicando que somente o bit P está ligado. Sugiro ao leitor variar o tamanho passado para malloc a fim de comprovar que o alinhamento do tamanho do chunk de fato ocorre. Também sugiro passar um número grande para malloc() a fim de ver a partir de qual tamanho malloc() irá deixar de usar uma arena e irá alocar o chunk com mmap(). Caso isso ocorra o bit M será ligado e o número 2 (decimal) será indicado como flags. Nota: Esse código propositalmente não utiliza free() antes de finalizar o programa. É redundante e desnecessário usá-la quando o programa é finalizado, tendo em vista que todas as páginas de memória usadas pelo processo serão liberadas pelo sistema operacional. Referências https://man7.org/linux/man-pages/man2/brk.2.html https://sourceware.org/glibc/wiki/MallocInternals https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=e2d7b1b58396906375ba0e953a20ac57f0904378;hb=refs/heads/master http://c-faq.com/malloc/freeb4exit.html
  12. O próprio Wine tem um debugger (winedbg) que você pode utilizar para depurar um .exe, dá para usar ele como gdb's server com a opção --gdb, caso você saiba usar o gdb... Porém o que o edb (e outros debuggers) faz é depurar processos e não arquivos executáveis. Se você pode iniciar um processo para o .exe então você pode depurá-lo. O passo-a-passo fica: Execute o .exe com o Wine. Abra o edb com privilégios root, menu "File" clique em "Attach". Procure pelo processo, dá para pesquisar pelo nome ou pelo PID. É necessário executar o edb como root porque é necessário privilégios root para poder fazer esse attach no processo em execução. Ah, um detalhe: É necessário que o processo se mantenha em execução para você poder fazer isso. Se ele finaliza imediatamente após iniciar você não vai conseguir fazer o attach.
  13. Na programação orientada a objetos existe o conceito de encapsulamento, que resumidamente é o conceito de não alterar uma variável diretamente mas sim usando uma função. Isso faz com que a variável fique "encapsulada" dentro do objeto e permite que qualquer lógica seja aplicada na alteração daquela variável. E é exatamente o que você quer fazer... Toda vez que as variáveis A e B são alteradas existe uma certa lógica ocorrendo com elas, não é? Então eu sugeriria você colocar tudo dentro de uma struct e implementar funções para modificar os valores de A e B. Tipo assim: struct var { bool mode; // A int value; // B int counter; }; void var_mode(struct var *variable, bool mode) { // etc... } void var_value(struct var *variable, int value) { // etc... }
  14. opcode = (ir & 0xf000)>>12; Quando você fez isso daqui, você pegou os 4 bits mais significativos de ir. Para pegar só 1 é a mesma coisa, só vai mudar o número de bits setados no valor com que está sendo feito o AND. if ( ac & (1 << 15) ) pc = address; // Negativo! ac é menor que zero if ( ac && !(ac & (1 << 15)) ) pc = address; // ac é diferente de zero e positivo.
×
×
  • Create New...