There are excellent answers in this thread, but I just wanted to add mine as I too once had the same question. (Also, I would like to point out that the book suggested by Joe-Internet is an excellent resource.)
First is the question of how does a computer work ? This is how: Input -> Compute -> Output.
First consider the “Compute” part. We’ll look at how Input and Output works later.
A computer essentially consists of a processor(or CPU) and some memory(or RAM). The memory is a collection of locations each of which can store a finite number of bits, and each such memory location can itself be referenced by a number, this is called the address of the memory location.The processor is a gadget which can fetch data from the memory, perform some operations based on the data and write back some data back to the memory. How does the processor figure out what to read and what to do after reading the data from memory ?
To answer this, we need to understand the structure of a processor. The following is a fairly simple view. A processor essentially consists of two parts. One is a set of memory locations built inside the processor that serve as it’s working memory. These are called “registers”. The second is a bunch of electronic machinery built to perform certain operations using the data in the registers.There are two special registers called the “Program Counter” or the pc and the “Instruction Register” or the ir. The processor considers the memory to be partitioned in three parts. The first part is the “program memory”, which stores the computer program being executed. The second is the “data memory”. The third is used for some special purposes, we’ll talk about it later. The Program Counter contains the location of the next instruction to read from the Program Memory. The Instruction Counter Contains a number which refers to the current operation being performed. Each operation that a processor can perform is refered to by a number called the opcode of the operation. How a computer essentially works is it reads the memory location referenced by the Program Counter into the Instruction Register (and it increments the Program Counter so that it points to the memory location of the next instruction). Next, it reads the Instruction Register and performs the desired operation. For example the instruction could be to read a specific memory location into a register, or to write to some register or to perform some operation using the values of two registers and write the output to a third register.
Now how does the computer perform Input / Output ? I’ll provide a very simplified answer. See http://en.wikipedia.org/wiki/Input/output and http://en.wikipedia.org/wiki/Interrupt. for more. It uses two things, that third part of the memory and something called Interrupts. Every device attached to a computer must be able to exchange data with the processor. It does so using the third part of the memory mentioned earlier. The processor allocates a slice of memory to each device and the device and processor communicate via that slice of memory. But how does the processor know what location refers to what device and when does a device needs to exchange data ? This is where interrupts come in. An interrupt is essentially a signal to the processor to pause what it is currently and save all it’s registers to a known location and then start doing something else. There many interrupts, each is identified by a unique number. For each interrupt, there is a special program associated with it. When the interrupt occurs, the processor executes the program corresponding to the interrupt. Now depending on the bios and how the hardware devices are connected to the computer motherboard, every device gets a unique interrupt and a slice of memory. While booting up the operating system with the help of the bios determines interrupt and memory location of each device and sets up the special programs for the interrupt to properly handle the devices. So when a device needs some data or wants to send in some data, it signals an interrupt. The processor pauses what it is doing, handles the interrupt and then gets back to what it is doing. The are many kinds of interrupts, such as for the hdd, keyboard etc. An important one is the system timer, which invokes a interrupt at regular intervals. Also there are opcodes that can trigger interrupts, called software interrupts.
Now we can almost understand how an operating system works. When it boots up, the os sets up a the timer interrupt, so that it gives control to the os at regular intervals. It also sets up other interrupts to handle other devices etc. Now when the computer is running a bunch of programs, and the timer interrupt happens the os gains control and performs important tasks such as process management, memory management etc. Also an os usually provides an abstract way for the programs to access the hardware devices, rather than let them access devices directly. When a program wants to access a device, it calls some code provided by the os which then talks to the device. There is a lot of theory involved in these which deals with concurrency, threads, locks, memory management etc.
Now, one can in theory write a program directly using opcodes. This is what is called machine code. This is obviously very painful. Now an assembly language for the processor is nothing but mnemonics for these opcodes, which makes it easier to write programs. A simple assembler is a program that takes a program written in assembly and replaces the mnemonics with the appropriate opcodes.
How does one go about designing a processor and assembly language. To know that you have to read some books on computer architecture. (see chapters 1-7 of the book refered by joe-internet). This involves learning about boolean algebra, how to build simple combinatorial circuits to add, multiply etc, how to build memory and sequential circuits, how to build a microprocessor and so in.
Now how does one write computer langauges. One could start off by writing a simple assembler in machine code. Then use that assembler to write a compiler for a simple subset of C. Then use that subset of C to write a more complete version of C. Finally use C to write a more complicated language such as python or C++. Of course to write a language you must first design it ( the same way you desigh a processor). Again look at some textbooks on that.
And how does one write an os. First you target a platform such as x86. Then you figure out how it boots and when will your os get invoked. A typical pc boots this way. It starts up and bios performs some tests. Then the bios reads the first sector of the hdd and load the contents to a specific location in the memory. Then it sets up the cpu to start executing this loaded data. This is the point you os gets invoked. A typical os at this point loads rest of itself memory. Then it initializes the devices and sets up other things and finally it greets you with the login screen.
So to write an os you must write the “boot-loader”. Then you must write code to handle the interrupts and devices. Then you must write all the code for process-management, device-management etc. Then you must write an api which lets the programs running in your os to access devices and other resources. And finally you must write code that reads a program from disk, sets it up as a process and starts executing it.
Of course my answer is overtly simplified and probably of little practical use. In my defence I’m now a graduate student in theory, so I have forgotten a lot of these things. But you can google a lot of these stuff and find out more.