OAK CODERS - Home Page
root@oakcoders:~# Dear Visitor, please leave a comment!
"When I read commentary about suggestions for where C should go, I often think back
and give thanks that it wasn't developed under the advice of a worldwide crowd." Dennis Ritchie.

Friday, June 29, 2012

Introdução ao conceito de processos no Linux

------------------------------------------------
Uma das fundamentais abstrações em sistemas operacionais Unix-like.

Esse artigo é destinado àqueles que gostariam de obter um simples entendimento do assunto, ou àqueles que desejam aprender por simples curiosidade.
Neste artigo, quando eu falar Linux, eu estarei referindo ao Linux Kernel. Estritamente dizendo, o termo Linux faz referência apenas ao Kernel.

- Definição
------------------------------------------------
O processo é um programa (código objeto armazenado em alguma mídia) no meio da execução.

É interessante mencionar que os processos são mais que códigos de um programa em execução (também conhecido como text section).
Além do código, os processos também possuem vários recursos que possibilita a transparência e eficácia do gerenciamento realizado pelo Kernel.

Alguns deles são:
o Arquivos abertos pelo processo (Open Files).
o Sinais Pendentes (Pending Signals).
o Estado do processo (Processor state).
o Internal Kernel Data.
o Espaço de memória (Com um ou mais mapeamentos).
o Um ou mais threads de execução.
o Data section contendo variáveis globais.

Definitivamente, processos podem ser definidos como o resultado final do código de programa em execução.

- Threads de execução
------------------------------------------------
Threads de execução (frequentemente referenciada como Threads), são objetos de atividade dentro de um processo. Cada thread contém um exclusivo program counter (registrador), stack, e um conjunto de registradores do processador (virtuais).

É muito interessante dizer que o Linux tem uma única implementação de threads. O Linux não diferencia entre processos e threads. Para ele, as threads são apenas um tipo especial de processo.

- Virtualizações

------------------------------------------------
Os sistemas operacionais modernos fornecem duas virtualizações:
o processador virtualizado e a memória virtual.

O processador virtual fornece ao processo a impressão que ele sozinho monopoliza o sistema. Na realidade, ele irá compartilhar o processador físico com um número mais elevado de processos.

Memória virtual segue o mesmo conceito, ela deixa o processo alocar e gerenciar a memória como se o processo fosse o único utilizador da memória do sistema (física).

Interessantemente, vale a pena notar que thread compartilha a abstração de memória virtual, porém cada uma possui seu próprio processador virtualizado.

- Note que...
------------------------------------------------
Um programa não é um processo, um processo é um programa em execução e seus respectivos recursos (tais recursos foram abordados acima).
De fato, dois ou mais processos podem existir executando o mesmo programa. Processos também podem compartilhar recursos entre si (arquivos abertos, espaço de memória, etc...).

- Criação de processos
------------------------------------------------
Um processo inicia sua vida quando, não surpresamente, ele é criado.
O Linux faz isso por meio da fork() system call (chamadas de sistema).

Essa chamada de sistema cria um novo processo duplicando o existente, ou seja, o caller(o chamador).
O processo que chama fork() é o parent(pai), enquanto o novo processo é o child(filho).
Dessa forma, o processo parent retoma a execução e o filho inicia a execução apartir do mesmo lugar (onde o fork retorna).

É muito interessante saber que a chamada de sistema fork() recebe dois valores do kernel, um é destinado ao pai e outro ao recém-nascido filho.

Término de um processo
------------------------------------------------
Um programa é finalizado via chamada de sistema exit().
Além de terminar um processo, ela libera todos os seus recursos.

Um processo pai pode indagar a respeito do status de um processo filho finalizado via chamada de sistema wait(), a qual ativa um processo para esperar pelo término de um processo específico.
Quando tal processo é finalizado, ele é colocado em um estado especial chamado estado zombie. Eles representam processos finalizados, a única forma de removê-los é o processo pai chamar wait().

Note: Outro nome para um processo é 'task'. Internamente, o Linux refere aos processos dessa forma.

Conclusão
------------------------------------------------
Eu tentei abordar de forma simples os conceitos necessários para seguirmos em frente. Abstraindo tais informações você será capaz de acompanhar o próximo artigo. Estaremos utilizando a árvore do Linux e criando módulos para obter um conhecimento profundo desse assunto.

Att, 

Raphael S.Carvalho

It's been a long time...

Unfortunately, It's been a long time since I posted on this blog, so I wish to resume our work and make effort to keep this blog up-to-date.

I also would like to introduce myself briefly.
I'm Raphael Santana Carvalho (also known as utroz), and I have been alive for 2 decades and 2 years.
I've created this blog in order to share my ideas. My main area of interest is computers and I'm studying Computer Science at the Fumec University, Brazil.

I've created a short list, which approaches the main subjects of the blog.
 * The list follows below:
- Operating Systems (KERNEL MAINLY). (I'm studying the Linux Kernel Development book. It's a good starting point for the Linux learners).
- Computer Programming Languages (Low / High Level).
- Cosmology (Well, It isn't a joke. I really would like to learn a little more about it).
- among other things...

 I have been programming since early February 2011, and I'm loving what I have been learning during my studies. However, I'm sure I would have to make a lot of effort to become a professional in this area.
Despite some professor's mistakes I'm liking the environment provided by the college. Indeed, I'm meeting few people who are studying similar areas of my interest, however, it's being nice to me.

I'm working on a scientific project in my university, so I'm trying to get many important things while I stay there. If I could give tips for everyone that are reading my 'shit' article, they would be: Stay studying, get involved in your area (whatever it is). The most important thing would be: It's worth meeting as many people as you can. Even just speaking with such people in your field will be invaluable.

PS: I'll make great effort to keep this blog updated regularly.

"When I read commentary about suggestions for where C should go, I often think back and give thanks that it wasn't developed under the advice of a worldwide crowd." Dennis Ritchie.

Please, leave your comments.
Regards, Raphael S. Carvalho

Saturday, February 18, 2012

Uma abordagem 'geral' sobre Stack.

 Antes de ir para o tema principal, eu irei abordar alguns conceitos simples que será fundamental para obter um aprendizado eficiente do conteúdo aqui contido.

COMO AS FUNÇÕES TRABALHAM
 Uma função é composta de diversas peças fundamentais para o seu funcionamento.

- Function Name: é um símbolo que representa um endereço onde o código da função inicia.

- Parameters: são dados explícitamente fornecidos para a função realizar o seu trabalho.

- Local Variables: são dados utilizados internamente pela função.

- Return Address: é um parâmetro 'invisível' que não é utilizado diretamente pela função. O return address comunica a função onde ela deve voltar depois de ter finalizado o seu trabalho. Na maioria das linguagens de programação esse parâmetro é passado automaticamente quando uma função é chamada.

- Return Value: é um método utilizado para retornar um valor. Na maioria das linguagens apenas um valor é permitido como retorno.
ex: Função x chama função y; função y retorna um valor para x.
obs: A linguagem C armazena o valor de retorno no registrador eax.

 Lembrando que é impossível generalizar como as funções são chamadas, como parâmetros são enviados ou como as funções retornam valores, pois isso varia de linguagem para linguagem.
A forma que os dados são transferidos entre funções é conhecida por calling convention.

 Vamos falar agora sobre o que realmente interessa, como a stack memory funciona.
 Utilizando uma analogia bem conhecida, pense na stack como uma pilha de papeís sobre a sua mesa, as coisas que são importantes ficam sempre no topo, porém o que não é utilizado será jogado fora.

A stack 'funciona' como uma pilha de papeís.

É importante ressaltar que a stack trabalha utilizando o método LIFO (Last In, First Out), ou seja, o último dado a ser armazenado será o primeiro a sair. Tudo que conhecemos por parâmetros e variáveis locais será armazenado lá.
Lembrando que cada programa de computador tem sua própria stack, para mais informações pesquise sobre 'Program Memory'.

Programas de computadores trabalham colocando dados no topo da stack e quando eles não são mais necessários vão sendo 'retirados'.
Embora possa parecer confuso, a stack inicia-se em um endereço de memória x e vai crescendo para baixo. Qualquer referência ao topo da stack, lembre-se disso.

 Antes de chamar uma função, um programa coloca na pilha em ordem reversa todos os parâmetros documentados[1] e utiliza a instrução call que faz duas coisas: Primeiro ela coloca o endereço da próxima instrução na pilha, vulgo 'return address'; Então ela modifica o registrador instruction pointer(%eip) para apontar para o início da função.

[1]: Ex: function(parameter1, parameter2).

Dessa forma, segue abaixo uma ilustração de como a stack irá ficar:
         High Address

             |                ...
             |              Parameter 2
             |              Parameter 1
             V              Return address <-- (Topo da pilha)

         Low Address                                                   

obs: O topo da pilha é marcado pelo registrador %esp.
 Quando uma função é chamada, ela 'automaticamente' salva o topo da pilha(%esp) no registrador base pointer(%ebp). Isso é totalmente necessário para saber onde as coisas encontram-se na stack, seja variáveis locais, parâmetros, etc.

 Entendendo melhor: se um novo valor é inserido na stack, o topo irá mover. Com base nisso, seria impossível saber onde as coisas estão.
O 'base pointer' (%ebp) pode ser considerado uma espécie de referência para a stack frame[1].

[1]: stack frame é uma area na stack que contém todos os dados utilizados pela função(parâmetros, variáveis locais, return address,...).

Neste ponto, a stack parecerá com isso:
         High Address

             |                ...
             |              Parameter 2
             |              Parameter 1
             |              Return address
             V              %ebp value <-- (Topo da pilha)

         Low Address                                                   

 Vamos supor que essa atual função(x) queira chamar a função(y), então ela armazenará na stack os parâmetros necessários para chamar a função y.
         High Address

             |                ...
             |              Parameter 2
             |              Parameter 1
             |              Return address
             |              %ebp value 
             V              Parâmetro para função y. (Topo da pilha)
                           
         Low Address                                                 

obs: o parâmetro para função y é uma variável local. Note também que ao movermos o topo da pilha(%esp), se não tivessemos armazenado o topo no início da função em (%ebp) seria impossível saber onde encontram-se os dados na stack frame. Podemos então concluir que o (%ebp) é utilizado como referência pelas funções.

- Quando a função é finalmente executada, acontece 4 coisas:
1. O valor de retorno é armazenado no registrador %eax.
2. Então colocamos o valor do registrador %ebp em %esp, dessa forma o topo da pilha será reiniciado para quando ela foi chamada.
         High Address

             |                ...
             |              Parameter 2
             |              Parameter 1
             |              Return address 
             |              %ebp value (Topo da pilha)
             V              Parâmetro para função y. 
                           
         Low Address                                                    

3. Ela 'retira' o valor que encontra-se no topo, no caso o %ebp value.
         High Address

             |                ...
             |              Parameter 2
             |              Parameter 1
             |              Return address (Topo da pilha)
             |              %ebp value 
             V              Parâmetro para função y. 
                           
         Low Address                                                    

obs: Como vocês podem notar, os valores não são removidos, apenas o topo da pilha é movido.

4. E para finalizar, independente do valor que está no topo da pilha, ele é colocado no registrador %eip (O registrador %eip aponta para a próxima instrução a ser executada).

Após a chamada da função, geralmente move-se o topo da pilha para acima dos parâmetros colocados na stack, segue a ilustração:
         High Address
                            ---Função que chamou x (main)---
             |              Return address 
             |              Local Variable
             |              Local Variable (Topo da pilha)
                            ---Função x---
             |              Parameter 2
             |              Parameter 1
             |              Return address 
             |              %ebp value 
             V              Parâmetro para função y. 
                           
         Low Address                                                 

Dessa forma, é interessante notar que todos os valores armazenados para a função x ainda encontram-se na stack, só que futuramente novas inserções irá sobreescrever os mesmos.

Será que isso é verdade? Eu lhe provo isso em código.
Se isso for verdade nós apenas precisaremos de salvar o endereço da variável local da stack em um ponteiro e posteriormente gravar em outra variável para que o valor não seja sobreescrito por outra stack frame.

/* -- Segue o código: -- */
int *p;

void function(void)
{
 int i = 3;
 p = &i;
}

main ()
{
 int i = 0;
 function();
 i = *p;

 printf("%d", i);
 return 0;
}

O resultado disso, foi exatamente o esperado: 3.

Gostaria de deixar claro que certas informações aqui não podem ser generalizadas, pois as implementações em chamadas de funções podem variar de linguagem para linguagem (calling convention).

Espero que todos os leitores possam tirar bom proveito da informações. Qualquer informação ou correção, favor me enviar um e-mail.
---------------------
Regards, Utroz.
E-mail Contact: utroz (at) oakcoders (dot) com

Thursday, February 02, 2012

How the computer processes keyboard signals.

For example, when you type, your typing goes through several programs before it hits your editor.

First, the kernel is what handles your hardware, so it is the first to receive notice about the keypress.

The keyboard sends in scancodes to the kernel, which then converts them to the actual letters, numbers, and symbols they represent.

If you are using a windowing system, then the windowing system reads the keypress from the kernel, and delivers it to whatever program is currently in focus on the user's display.

Example:
Keyboard -> Kernel -> Windowing System -> Application Program

---------------------
Regards, utroz.
E-mail Contact: utroz (at) oakcoders (dot) com

Saturday, January 21, 2012

Mesclando teória e prática: Processo de Compilação - Linguagem C

Antes de realizar qualquer introdução, gostaria de agradecer o KOV(Gustavo Noronha Silva) pela base das informações contidas aqui.

Uma situação bem comum que vejo acontecer com os iniciantes no mundo computacional é confundir certos conceitos, tanto em teoria ou prática.
Hoje tentarei abordar como realmente funciona o processo de compilação em sistemas Unix-Like. Lógico que será resumidamente, pois ao contrário disso, estariamos diante de um artigo colossal.

Vamos parar com as brincadeiras e focar no assunto que realmente interessa. Um detalhe que vale a pena ser abordado, quais são os passos necessários que são realizados pelo compilador, iniciando a partir do código-fonte até chegar no executável ?

Lembrando aos leitores, tentarei mesclar a teória e a prática para tentar explicar a todos como realmente funcionam esses processos, portanto abram o editor de texto da sua preferência, porque iremos colocar a 'mão na massa'.

Pré-processador (cpp):
Breve descrição: é um processador de texto(nada além disso), cujo objetivo é passar pelo código-fonte buscando por diretivas '#', que são informações legíveis que ele entende. Ele realiza inserções (#include), faz alterações a partir de macros (#define), além de fornecer compilação condicional
(#if, #else, #endif,...).

* Mãos em obra, criem os seguintes arquivos com os respectivos conteúdos:

- test.c:
#include "test.h"
main() { 
   return 0; 
}
----------------
- test.h:
hello world
----------------

Agora iremos utilizar o cpp, para trabalhar em cima do nosso código-fonte 'test.c'. Em seu terminal, no diretório respectivo ao arquivo,
digite isso: cpp test.c

A saída que você recebeu no console, deverá ser similar a essa:
rsc@utroz:~$ cpp test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"
# 1 "test.h" 1
hello world
# 2 "test.c" 2
main()
{
    return 0;
}
O que nos acabamos de executar é a primeira etapa do processo de compilação, ou seja, o pré-processador.
Todas as linhas que começam com uma diretiva "#", são informações direcionadas à outras etapas do processo, isso é realmente necessário para que eventualmente mais tarde o compilador possa dar informações de erros que façam sentido para o programador.

Agora troquem #include "test.h" por #include <stdio.h> no arquivo test.c e executem cpp test.c novamente.

Não irei colar a saída aqui, pois como vocês podem perceber ela é realmente grande. O que vocês podem perceber em relação a ação do pré-processador em ambos exemplos, foi realizar a troca do #include pelo conteúdo do arquivo cabeçalho respectivo.
No exemplo atual ocorreu a substituição de #include<stdio.h> pelo conteúdo do arquivo "/usr/include/stdio.h".

Desconectem a idéia que arquivo cabeçalho é algo especial, pois o mesmo é apenas uma maneira simples de copiar/colar códigos.
Se o cabeçalho tiver implementações de funções ou definições, no final elas estarão no seu código-objeto, é como se a todo momento elas estivessem presentes.
A extensão '.h' não é nada de especial, apenas uma convenção, podemos dizer que isso ajuda na modularização do projeto, uma forma mais organizada de escrever códigos.

Vamos realizar outro exemplo, no arquivo 'test.c', substitua o atual include por: #include "non-exists" e execute 'cpp test.c' novamente.

O pré-processador terminou o processo sem éxito, certo? Isso tudo ocorreu porque você está tentando incluir um arquivo cabeçalho que não existe.
Agora se você digitar 'mv test.h non-exists' tudo irá voltar a funcionar corretamente, em certas partes.
Se você analisar o conteúdo de test.h: 'Hello World', lhe parece um código válido?
O único motivo que fez o cpp não reclamar, irá confirmar o que eu disse anteriormente: Pré-processador é apenas um processador de texto, ele não conhece a estrutura da Linguagem C, ele apenas tem um papel importante em trabalhar o código-fonte a partir de diretivas para as próximas etapas do processo de compilação.

Outro exemplo: Quando você chama uma função que recebe um integer com um unsigned integer, o compilador não reclama? Ele apenas consegue associar isso pelo fato do seu código possuir a declaração com os parâmetros da função, lembrando que esse processo de análise semântica, é realizado pelo compilador (gcc).

Um dos motivos da criação de header files foi pelo fato que você pode chamar funções como printf sem a necessidade de incluir a implementação da mesma no seu código, a qual são distribuídas junto a biblioteca compilada.

Terminamos essa parte, vamos ver o nosso progresso:
- o GCC chama o pré-processador para trabalhar no código-fonte em cima das diretivas.
- o GCC pega o código que o cpp criou e realiza a compilação para o código Assembly.
OBS: no código Assembly há várias referências a símbolos que apenas são declarados no código (ex: printf).
- o GCC chama o Assembler que gera o código-objeto(extensão .o).

Voltando a parte de símbolos, o arquivo .o contém referências a símbolos que não existe no mesmo, o GCC nem o Assembler reclamará disso, pois o arquivo objeto não é um binário, nem uma biblioteca, é apenas um código C transformado em código binário.

Exemplos com <math.h>:
Um exemplo bem comum é quando usamos a biblioteca de matemática(math.h), qual parâmetro passamos ao GCC? -lm, certo?
Esse -lm na verdade o compilador ignora, ele é direcionado ao dinamic linker, que posteriormente irá saber sua necessidade de achar uma biblioteca chamada 'libm.so'.
Vale a pena analisar, o 'l' é correspondente à lib e '.so' à shared object (biblioteca compartilhada).

Substitua o conteúdo do arquivo 'test.c', pelo seguinte e digite no console:
gcc test.c -o test:
main ()
{
    return isless(2, 4);
}
Resultado da saída:
rsc@utroz:~$ gcc test.c -o t   
/tmp/ccAoYXnK.o: In function `main':
test.c:(.text+0x19): undefined reference to `isless'
collect2: ld returned 1 exit status

Podemos vizualizar que temos um símbolo indefinido para a função isless, sendo .text uma sessão respectiva a dados do código Assembly.

Agora iremos dizer para o GCC parar depois que chamar o pré-processador e salvar o conteúdo em test.
Adicione no ínicio do seu código: "#include <math.h>" e novamente chame o GCC utilizando os seguintes parâmetros: : gcc -E test.c -o test

Veremos que o código estará imenso, pois o pré-processador incluiu todo o código de '/usr/include/math.h' no código-fonte.

É super interessante notar algo em nossa função main:
main()
{
    return __builtin_isless(2, 4);
}
Se você acessar o cabeçalho math.h, você verá que isless(x,y) está declarado da seguinte forma: #define isless(x, y)           __builtin_isless(x, y).
Viu como tudo está 'encaixando'? O cpp encontrou a definição da macro isless e substituiu pelo texto correspondente.

Gerando o código Assembly:
Utilizando o mesmo arquivo anterior('test.c'), digite no seu console:
gcc -S test.c -o test.S

Resultado da saída:
rsc@utroz:~$ gcc -S test.c -o test.S
test.c: In function 'main':
test.c:4:5: error: non-floating-point arguments in call to function '__builtin_isless'

O motivo do GCC ter reclamado foi pelo fato de existir uma declaração que espera float e nos demos int.
Substitua os parâmetros no arquivo 'test.c' por '2.0' e '4.0' respectivamente.
Chame o GCC novamente usando os parâmetros anteriores, lembrando que o parâmetro '-S', diz para o compilador parar depois que gerar o código Assembly.

Resultado da saída:
rsc@utroz:~$ gcc -S test.c -o test.S
rsc@utroz:~$ cat t.S
        .file   "t.c"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $1, %eax
        popl    %ebp
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.5.2"
        .section        .note.GNU-stack,"",@progbits

Então passamos por 3 etapas do processo:  
CPP -> Análise sintática do código C -> Gerar código Assembly.

Agora é a hora de 'Assemblar', hora da montagem ou hora do rush, brincadeirinha.
Chegou a hora de gerar o nosso famoso e tão sonhado código objeto, depois desse longo caminho que pode agora ser resolvido com um único simples comando:
as test.S -o test.o

Link-Edição:
Então chegamos ao final, que é o processo de link-edição, lembrando que por padrão o GCC sempre 'linka' à biblioteca C, ou libc, então não existe a necessidade de especificar isso ao compilador.

Vamos agora ao processo final, digite isso no seu console:
Debian(64 bits): ld -o test /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o test.o -lm -lc
Slackware: ld -o test /usr/lib/crt1.o /usr/lib/crti.o test.o -lm -lc
OBS: não sei como isso comportará em outras distribuições, faça a busca pelos arquivos 'crt1.o' e 'crti.o', usando 'whereis'.

Parâmetros passados ao ld (dynamic linker ou loader)
test.o - arquivo objeto.
-lm - requisição da biblioteca libm.so (-lm -> lib + m + .so = libm.so)
-lc - requisição da biblioteca libc.so (-lc -> lib + c + .so = libc.so)

'crt1.o'  e 'crti.o' são arquivos objeto que vem no GCC, necessariamente utilizadas pelo LD, onde contém informações do ELF(formato binário) para montar os executáveis finais, para ser mais específico são funções auxiliares de inicialização e etc.

Agora vamos conferir as bibliotecas 'linkadas' ao nosso arquivo executável, usando: ldd nome-executável.

Resultado de saída:
rsc@utroz:~$ ldd test
        linux-gate.so.1 =>  (0xffffe000)
        libm.so.6 => /lib/libm.so.6 (0xb7854000)
        libc.so.6 => /lib/libc.so.6 (0xb76f1000)
        /usr/lib/libc.so.1 => /lib/ld-linux.so.2 (0xb78a3000)

linux-gate e ld-linux são bibliotecas especiais que fazem parte do dynamic linker; Elas que iniciam todo processo do mapeamento de símbolos para os respectivos endereços de memória, lembrando que isso só ocorre quando o primeiro aplicativo faz requisição da mesma.
E as outras duas, são as que pedimos: libc e libm.


Existe uma sessão do ELF, a qual fala que o binário é associado com tal lib, os símbolos não irão apontar para nenhum local específico(area de memória), lembra-se: ainda.
Só na hora da execução a qual chamamos uma biblioteca, denomina-se resolução de símbolos.
o LD monta uma tabela das respectivas bibliotecas no ELF, quando o programa usa um símbolo ainda não resolvido, ele faz a busca em todas as bibliotecas associadas, começando a partir do próprio binário;
Ao encontrar, ele criar uma espécie de link para a área de memória onde o símbolo da biblioteca encontra-se mapeado.


Você pode perguntar-se, pelo fato da LIBC ser uma referência da linguagem, ela é carregada no processo de boot?
Ela é apenas carregada quando o primeiro processo associado a ela é executado, não existe nenhuma relação direta entre ela e o kernel.
Existe uma biblioteca chamada KLIBC que é uma implementação reduzida da LIBC que fornece recursos aos programas que participam do processo da inicialização do sistema.

Você novamente pode perguntar-se, a biblioteca fica em uma única área de memória, se dois programas em um exato instante executar o mesmo símbolo, não tem perigo de colisão?
Não, pois é apenas código. Cada processo tem o seu próprio espaço de memória(stack, heap), apenas o código da biblioteca é compartilhado.




Galera, finalmente chegamos ao FIM.
Espero que realmente tenham gostado e possam tirar proveitos das informações contidas aqui.


---------------------
Regards, Utroz.
E-mail Contact: utroz (at) oakcoders (dot) com

Friday, January 13, 2012

How to become a Hacker (Eric.S.Raymond)

The most effective way to become a master is to imitate the mind-set of masters — not just intellectually but emotionally as well.

the following modern Zen poem has it:
    To follow the path:
    look to the master,
    follow the master,
    walk with the master,
    see through the master,
    become the master.


So, if you want to be a hacker, repeat the following things until you believe them:
The World is full of fascinating problems waiting to be solved.

---------------------
Regards, utroz.
E-mail Contact: utroz (at) oakcoders (dot) com

Friday, December 23, 2011

C Programming Language

Getting Started in C Programming Language Concepts:
Lesson 1: How to print a character string using the printf function?
/* Standard I/O Library */
#include <stdio.h>

/* This function(main) is necessary because the program starts from it. */
int main(void) 
{
        /* Even though this is always used by beginners, 
           you might use any other string.
           The character string as a parameter is necessary 
           for using the printf function. */
        printf("Hello world"); 

        /* This statement return a value to the Operating System */
        return 0; 
}
I want to post useful articles and tutorials that will help everyone who are learning the C Programming Language.

---------------------
Regards, Utroz.
E-mail Contact: utroz (at) oakcoders (dot) com