An article to understand the basic principles of computer operation (C language must understand before)

Source: Internet
Author: User

The main object of this article is CPU and memory. Why learn C language before must understand, because C language is very close to the underlying principle of the language, understand the principle of CPU and memory, to learn C language has a great help.

In fact, I am a personal advocate of computer science should first learn the principles of computer composition and then learn C language, but it seems not so dry, and learn C language before you need to learn the whole principle of computer to learn C, for want to get started, understand this article enough.

First of all, the memory is just a number of tools, the capacity can be quite large, such as now common 4GB capacity of memory, there are 4 billion memory units, or 4 billion bytes (bytes in b), each memory unit can put a 8-bit binary number. Everything in the computer is binary, and as for why it is 8, it is the rule.

(In fact, Byte B itself is a capacity unit, 1kb=1000 or 1024b,1mb=1000 or 1024kb,1gb=1000 or 1024MB, specifically 1000 or 1024 actually ... I hate it, too. The first guy to get this rate is 1000.

The basic operation of memory is only two kinds of reading and writing (for some CPUs and operations, but we only talk about reading and writing, because there are two enough). There are so many memory units, I have to read or write when I have to tell people what we want is the internal deposit, this is going to pass the number, for 4GB memory, the number is from 0 to 4 billion-1 (note is starting from 0, in the computer or C language, a lot of things are starting from 0), this number is the address. Read is to put a number of the address in the CPU, write is to put a certain number of CPU in the memory address (this address before the number is not).

It is also stated that each reading or writing can read and write 2, 4, or even 8 memory units. 32-bit CPU The most common is read 4 bytes at a time (because each byte is 8 bits, 32 bits is 4 bytes), in general, the requirement address must be a multiple of 4, such as read 4004 starts 4 bytes, then this will be 4004, 4005, 4006, 4007 the number of the 4 units are read out , making up a 32-bit binary number. Here's a question, how to group? From high to low to 4004 to 4007, or 4007 to 4004? Actually depends on the CPU, both of them. The writing is similar.

Let's talk about CPU. CPU has an important concept is register, such as 32-bit MIPS have r0 to R31 32 registers (actually not only these, but the other registers have their own particularity, discussed later), each register can put a 32-bit binary number. Registers are also used to store numbers (analogous to memory), but with so little register, there is not enough memory at all. And the CPU must have memory to work (the reason behind that). Unlike memory, the data in the register can be calculated directly, such as MIPS I can let the CPU do something like this: Add the number of R12 and R17, add the result into R1. Results can also be deposited in R12 or R17. This example should be able to see what registers are used for. and the number in memory, can not directly do the operation (in fact, not all the CPU can not, but those we do not discuss), can only be a number of an address to read a register, or a number of registers to the memory of an address. Also note that the operation of the internal access number is very slow (compared to the operation of the register operation, but compared to the external memory disk and so on memory is really fast), of course, there are some ways to solve this problem to some extent, but the common data to be placed in the register as much as possible.

The typical operation of a read-in register in memory, such as adding a number of R14 to 12 (constant), adds the result as an address and saves the number of memory addresses into the R6. Looks like this plus 12 is a little weird? If you want to r14 the number of direct address, plus 0 can be, but in fact, this first add a constant and then as the address of the operation is very common, so there will be a strange look at this operation. Here are three things to tell the CPU, which register to put the address in, the constant to add the address to, and which register to read the number into. Memory-write operations are similar. For 32-bit MIPS, is generally directly 4 bytes read in or write out, but can also write 1 bytes or 2 bytes, and register the number of bits is not the same, there will be a problem: what to do with the extra bit? But I'm not going to discuss it here, either.

32-bit MIPS all registers are 32-bit, read and write memory, can represent the address only 2^32, so if the memory capacity of more than 2^32 bytes (is 4GB, but the rate is 1024), it is useless, the CPU does not access them.

We have to tell the CPU what to do, so how to "tell"? Through instructions. Previously said, add the number of R12 and R17, add the result into R1, which can be written as an instruction (for 32-bit MIPS). Add the number of R14 to add 12 as the address, the number of memory address into the R6, which is also an instruction. For 32-bit MIPS, all instructions are represented by a 32-bit binary number. Yes, another 32-bit binary number. In these 32 bits, some indicate the type of instruction (addition or subtraction, or read and write memory), and some indicate the register number. Anyway, as long as you know the instruction can be expressed in binary number. For x86 CPU, each instruction length is not fixed, some instructions are 8 bits, some are very long.

OK, instructions are finished, how to send to the CPU? In fact, the instructions are placed in the memory in order. This is why the CPU is out of memory and does not function properly. The CPU needs to take instructions from memory to the register to execute, this register is often the same as the previous r0 to R31, is specialized for special purpose registers. In addition, a register PC (called a PC in MIPS) is used to indicate the address of the fetch instruction, and it is not r0 to R31. CPU has been in the loop to do such a few things: first put the value of the PC as the address, the memory of the command to take out, and then change the PC to the next instruction address (for 32-bit MIPS is generally the value of the PC plus 4, because each instruction 32 bits 4 bytes), then go to execute this command. And then repeat the process.

Here to insert a sentence, C language to write the program, to first translated into such a piece of instructions, to run, of course, the process of conversion is not our own to do. Many operations in the C language can correspond directly to the operation of these instructions. Therefore, understanding these instructions is very helpful for learning C language.

Conditional execution instructions? First to determine whether a condition is set up, if the establishment of the implementation of this paragraph, does not set up the implementation of the next paragraph? Can you do it? Of course, you can change the PC! Of course, the MIPS directive can not directly do some kind of arithmetic to deposit a number into the PC, but MIPS have other instructions, like unconditional jump, you can change the PC. In arm you can change your PC like any other register. There are times when the conditional jump, first determine whether a condition is set up. What are the general conditions? The general is to determine whether a number is greater than 0, is equal to 0, and so on, in fact, in the instruction level these are easy to achieve, here does not expand said. In short, the way to jump is to change the PC.

There is a special jump command to emphasize that this command can be used to save the old PC before jumping (point to the next instruction of the jump command). For MIPS, it is saved in a register (that is, R31). For x86, it is saved to the stack (which will say what the stack is later). After the jump, execute a piece of code, you can also jump back, because the old PC saved. This is important if there are several places where you want to jump to this code and then go back to where it was. For example, the function of a piece of code is to do something based on the values of R4 and R5 (such as calculating the value of the R4+R5, storing the R4 in the memory of the R5 address, or anything more complex), and writing a value (such as the value of R4+R5) into R2. Then at any time can be R4 and R5 set up after the use of this jump in the execution, when the R2 calculated and then jump back, then you can use the value of R2. This is called a procedure call, or in the C language called a function call. Of course, if you just add R4 and R5 so simple, it is not necessary to write a piece of code to use the procedure call to implement, but sometimes this operation is very complex, it is not worthwhile to write in every place, this time you can use the procedure call.

However, this will lead to new problems, for example, after the execution of the code, in addition to R2, R4, R5, the other registers will not change? There is a real possibility of change, especially when the program is complicated. A relatively easy to think of is that after jumping to that code, if you want to use other registers, the first to save it in memory, and so things are done, from memory to read back the number, and then jump back, you can ensure that the other register value is not destroyed. A detailed discussion of how the registers are saved is discussed later.

However, 32 registers, and does not necessarily have a very important, can not be lost data, often do not need to save them all. Accessing memory is a slow operation. Therefore, the 32-bit MIPS gives a convention that the registers must be guaranteed to be unchanged and which can be changed in the procedure call. The specific content of this agreement does not begin to say. For the former, if you want to use these registers in the process, you must save their original values to memory and run out to recover. For the latter, the process can be changed and no need to be saved. This also means that if you use this jump instruction, you will not be able to guarantee that the value in the latter's register is not destroyed after you return. The typical is r31, as long as the jump command executed, R31 immediately destroyed (become the value of the old PC). Therefore, you must ensure that no important data is stored in these registers before jumping.

The following is a question about this register save. To save must be in memory, if the memory is not enough, then don't save the register and even the program does not have to run, so we assume that the memory is large enough. Where is it saved to memory? For example, the program calls a piece of code (which jumps to a section of code with this special jump instruction), and then the code needs to call another code, which is a procedure nested call. Think about it, just jump to the process, R31 saved the return address, if you want to call again, then R31 will be destroyed, so again call to save the value of R31. After the new call is completed, restore the R31 back and return to normal. Where does the R31 save to memory? An easy-to-think approach is to fix an address that stores r31 at this address when it is saved, and then restores it from that address. This needs to be guaranteed, after the save, until the recovery, can not be executed to the "save" this position, or the previously saved value is destroyed. So can this be guaranteed? Once saved, this problem should not occur until the jump statement jumps to the save before resuming.

But in fact, there is a concept called recursion, which is to call the process itself again (before returning) in a process. Looks strange, doesn't it? But in fact it can really do this, and will not go into the cycle of death, this situation is often encountered. Consider that, in recursion, it is obvious that there is a nested procedure call (because it is constantly nested to call itself), which involves saving r31, then invoking, and then recovering R31. Is there a jump statement between R31 save and restore before jumping to save? Obviously, that's the call itself! Call is a special kind of jump (and the difference between the normal jump is to automatically save the PC before jumping), and this jump directly to the beginning of the process, and then execute to save R31 statement, will destroy the previously saved R31.

Therefore, if recursion occurs, it is not possible to save to the same location each time. It can be thought that it is not possible to write the save address directly in the instruction, because the instruction has been written dead and can not be changed, each execution to this command will be saved to the same address. So we can only think of another way, is to write the saved address in a register, and then to this address to save. Now basically all CPUs are so dry, there is a special register SP (or other name), for example, just enter this process, the SP value is 1996, then the next time you want to save the register, the SP minus 4 becomes 1992, Then save to 1992 addresses (because each register is 32 bits and 4 bytes). The next time you save, the SP minus 4 to 1988, and then save the data to 1988, it is always the case, that is, the SP always point to the last saved number of the location, to save the SP minus 4 and then save to the SP address. This effectively avoids the problem of saving to the same address every time. Recovery, also according to the SP's value to the corresponding address of the recovery, in addition, before returning the SP value must be restored to 1996, otherwise it will cause SP chaos. SP is a very important value, it is easy to imagine that if the value of the SP is wrong, the data stored here is wrong, so the actual CPU of this SP register absolutely can not be changed, there will be nothing I need to use the SP so first save the SP so that will be messy. Of course, the use of SPS in actual programming can be simplified, such as the total number of 4 registers to be saved, then the SP minus 16, and then save these numbers to Sp+12, Sp+8, Sp+4, SP. At the command level this is entirely possible. Perhaps you will wonder why SP is always minus, not always add, in fact, in theory, but now a lot of popular operating systems, SP is the entire memory address from the highest point of descent, which may be historical reasons, but it is also good.

To put it simply, register the SP to record how much data is now saved and where to save it next time. This is actually the stack, the essence of the stack is to use a "stack top pointer" to record where the next step into the stack. This "stack-top pointer" is the SP. But this stack is a bit different from the stack in the data structure. If a process, local variables (is the concept of C language, first look at the good) too much, register is not enough, this time also need to use the stack, a lot of variables saved to the stack, to solve the problem. As long as there is a process call place, the basic will be used in the stack, the stack is a very important concept. I don't seem to have a specific definition here, but you can understand the usage of SP registers.

An article to understand the basic principles of computer operation (C language must understand before)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.