preface
The article has been synchronized to my personal website: xiaoflyfish.cn/!
Next article share website foundation!
The article is longer and can be liked
Computer architecture
Modern computer models are based on the von Neumann computer model
Computer at run time, first remove the first instruction from memory, through the decoding of the controller, according to the requirements of the directive, from storage to retrieve data from the specified arithmetic and logical operations such as machining, and then take the results to the memory by address, next, then the second instruction, in complete instructions, under the command of the controller to proceed accordingly. Until a stop command is encountered
Program and data storage, according to the sequence of program arrangement, step by step to take out the instruction, automatically complete the operation prescribed by the instruction is the computer’s most basic working model
The five core components of a computer
Controller: is the central nerve of the whole computer, its function is to explain the control information stipulated by the program, control according to its requirements, schedule the program, data, address, coordinate the work of each part of the computer and access to memory and peripherals.
Arithmetic unit: the function of arithmetic unit is to carry out various arithmetic operations and logical operations on data, that is, to process data.
Memory: The function of memory is to store programs, data and various signals, commands and other information, and provide these information when needed.
Input: input equipment is an important part of the computer, input equipment and output equipment together with you for external equipment, referred to as peripherals, the role of input equipment is to program, original data, text, characters, control commands or on-site data collection information input to the computer.
Common input equipment has keyboard, mouse, photoelectric input machine, tape machine, disk machine, cd-rom machine and so on.
Output: both input and output devices, is also an important part of the computer, it put the computer in the middle of the results, a variety of data within the machine or the last symbols and words or information such as all kinds of control signal output, the output of the commonly used microcomputer devices have a CRT display terminal, printer, laser printer, plotter, and magnetic tape, we, etc.
The computer structure is divided into the following five parts:
Input equipment; Output device; Memory; Central processing unit; The bus.
memory
In the Von Neumann model, programs and data are stored in a linear array of storage areas called memory.
The smallest unit of storage is called a byte, which is 8 bits. Each byte corresponds to a memory address.
Memory addresses are numbered from 0. For example, the first address is 0, the second address is 1, and then they are incremented. The last address is the number of bytes in memory minus 1.
We usually talk about memory is random access, that is, any address data is read at the same speed, any address data is written at the same speed.
CPU
In the von Neumann model, the CPU is responsible for control and calculation. To facilitate the calculation of large values, the CPU can calculate more than one byte of data at a time.
-
If the CPU can compute four bytes at a time, we call it a 32-bit CPU.
-
If the CPU can compute eight bytes at a time, we call it a 64-bit CPU.
The 32 and 64 here are called the bit widths of the CPU.
Why is the CPU designed this way?
The maximum value of a byte is 0 to 255.
For example, to calculate 20000*50 is out of the maximum byte representation range.
Therefore, the CPU needs to support multiple byte calculations. Of course, the larger the number of bytes, the larger the number of bytes can be calculated, but in real life, it is not necessary to calculate such a large number. For example, the maximum integer that a 32-bit CPU can calculate is 4294967295, which is already very large.
Control unit and logical operation unit
CPU has a control unit is responsible for the control of CPU work; There are also logical operations units that do calculations.
register
For a CPU to perform a calculation, such as the simplest sum of two numbers, the CPU is too far away from memory to need a memory close to itself to store the number to be computed.
The memory is a register, and the register is in the CPU, and the control unit is very close to the LOGICAL operation unit, so it’s very fast.
Common register types:
- The general purpose register is used to store data that needs to be calculated, such as two data that need to be added and summed.
- Program counter, used to store the MEMORY address of the CPU to execute the next instruction, note not store the next instruction to execute, at this time the instruction is still in memory, program counter just store the address of the next instruction.
- The instruction register is used to store the instruction pointed to by the program counter, that is, the instruction itself, where the instruction is stored until it is executed.
Multistage cache
In order to improve execution efficiency and reduce the interaction between CPU and memory (interaction affects CPU efficiency), modern cpus generally integrate multi-level cache architecture on the CPU
The CPU cache, or cache memory, is a small but high speed memory located between the CPU and main memory
Because the SPEED of the CPU is much higher than that of the main memory, it takes a certain period of time for the CPU to read or write data from the memory. The Cache stores some data that has just been used or recycled by the CPU. When the CPU uses this data again, the CPU can directly invoke the data from the Cache, reducing the CPU waiting time and improving the system efficiency. It includes the following types:
L1-Cache
L1-cache exists in CPU, compared with register, although its location is farther from the CPU core, but the cost is lower, usually l1-cache size in tens of Kb to hundreds of Kb, read and write speed in 2~4 CPU clock cycle.
L2-Cache
L2-cache is also in the CPU, located farther from the CPU core than L1-cache, its size is larger than L1-cache, depending on the CPU model, there are 2M, there are smaller or larger, speed in 10 to 20 CPU cycles.
L3-Cache
The L3-cache is also located farther from the CPU core than the L2-cache, and is usually larger than the L2-cache. The read/write speed ranges from 20 to 60 CPU cycles.
L3 Cache size also depends on the model, for example, i9 CPU has 512KB L1 Cache; 2MB L2 Cache; Has 16MB L3 Cache.
When the CPU needs some data in memory, if the data is in the register, we can use it directly. If this data is not in the register, we need to query L1 cache first; Not found in L1, then query L2 cache; L3 cache is not queried in L2; Not in L3. Go back to ram.
Conclusion:
Memory Storage space size: Memory >L3>L2>L1> register;
Order of memory speed: register >L1>L2>L3> memory;
Security level
CPU running security level
CPU has four running levels, which are:
- Ring0, ring1, ring2, ring3
Ring0 is for operating systems only, ring3 is for anyone.
Ring0 is the highest CPU running level, followed by RING1 and ring2…
The system (kernel) code runs at the highest run-time level, ring0, and can use privileged instructions to control interrupts, modify page tables, access devices, and so on.
The application code runs on ring3, the lowest run-time level, and cannot do controlled operations.
For example, if you want to access a disk or write a file, you need to execute a system call (function). When executing the system call, the CPU runtime level will change from ring3 to Ring0 and jump to the corresponding kernel code location of the system call. Then the kernel will complete the device access for you. Then return from ring0 to ring3.
This process is also known as switching between user and kernel mode.
Locality principle
When a CPU accesses a storage device, both data and instructions tend to be clustered in a contiguous area, which is known as locality
Temporal Locality:
If an information item is being accessed, it is likely to be accessed again in the near future.
Such as loops, recursion, repeated calls to methods, etc.
Spatial Locality:
If a memory location is referenced, locations near it will also be referenced in the future.
Examples include code executed sequentially, two objects created consecutively, arrays, and so on.
The execution of a program
The program is actually an instruction, so the running process of the program is to execute each instruction step by step, responsible for executing instructions is the CPU.
The CPU executes the program as follows:
- First, the CPU reads the program counter value, which is the memory address of the instruction, and then the CPU control unit operates the address bus to specify the memory address to be accessed, and then notifies the memory device to prepare the data. When the data is ready, the instruction data is transmitted to the CPU through the data bus. After the CPU receives the data from the memory, Store this instruction data into the instruction register.
- The second step, CPU analysis instruction register instruction, determine the type of instruction and parameters, if it is the calculation type of instruction, the instruction to the logical operation unit operation; If the instruction is of storage type, it is executed by the control unit;
- Third, after the CPU completes the instruction, the program counter increases in value, indicating that it points to the next instruction. The size of this increment depends on the bit width of the CPU. For example, on a 32-bit CPU, the instruction is 4 bytes and requires 4 memory locations, so the program counter will increment by 4.
A simple summary is that when a program is executed, the CPU will read the instructions to be executed from the memory to the instruction register according to the memory address in the program counter, and then read the next instruction sequentially according to the instruction length increase.
The CPU reads instructions from the program counter, executes them, and continues on until the program finishes executing. This process is called the CPU’s instruction cycle.
The bus
CPU and memory and other devices also need to communicate, so we use a special device for control, the bus.
- Address bus, used to specify the memory address that the CPU will operate on;
- Data bus, for reading and writing memory data;
- Control bus, used to send and receive signals, such as interruption, equipment reset and other signals, the CPU received the signal natural response, then also need to control the bus;
When a CPU reads or writes data from memory, it usually needs to use two buses:
- Firstly, the address bus is used to specify the address of memory.
- Then through the data bus to transmit data;
Input and output devices
The input device inputs data to the computer, which calculates and transmits the results to the outside world through the output device.
If the input device or output device wants to interact with the CPU, for example, when the user presses a button and the CPU responds, the control bus is needed.
Basic knowledge of
interrupt
Type of interrupt
-
According to the trigger of interrupt is divided into synchronous interrupt and asynchronous interrupt;
-
Interrupts can be classified into maskable interrupts and non-maskable interrupts according to whether interrupts are forced to trigger.
Interrupts can be triggered directly by CPU instructions. Such active interrupts are called synchronous interrupts.
There are several cases of synchronization interruptions.
-
For example, when a system call needs to switch from user mode to kernel mode, the program needs to trigger an interrupt, called a Trap, after which the system call needs to continue.
-
Another type of synchronization interrupt is a Fault, usually because some kind of error has been detected and an interrupt needs to be triggered. When the interrupt response is complete, the place where the error was triggered, such as the page-missing interrupt we’ll learn about later, is reexecuted.
-
Finally, there are exceptions for programs, which, like traps, are used to implement exceptions thrown by programs.
The other interrupts are not triggered by the CPU directly, but are triggered by the need to respond to external notifications, such as keyboard, mouse, etc. These interrupts are called asynchronous interrupts.
Cpus generally support an interrupt masking bit (a register), which is set to 1 and the CPU temporarily stops responding to interrupts.
Keyboard and mouse input, such as traps, errors, exceptions, etc., will be temporarily shielded.
However, for some particularly important interrupts, such as power outages caused by CPU failures, they still fire normally.
Interrupts that can be masked are called maskable interrupts, and most interrupts are maskable interrupts.
Kernel mode and user mode
What are user mode and kernel mode
The Kernel runs in super privilege mode, so it has high privileges.
Following the principle of permission management, most applications should run under minimal permissions.
Therefore, many operating systems divide memory into two regions:
-
Kernal Space, which only kernel programs can access;
-
User Space, the portion of memory dedicated to the use of applications.
Code in user space is restricted to using only a local memory space, and we say that these programs execute in user mode.
Code in kernel space can access all memory, and we say that these programs execute in kernel mode.
According to the level:
When a program is running at privilege level 0, it is said to be running in kernel mode
When a program is running at level 3 privilege, it is said to be running in user mode
Programs running in user mode cannot directly access the operating system kernel data structures and programs.
When we execute a program on the system, we run it in user mode most of the time, and switch to kernel mode when it needs the operating system to do something it doesn’t have the authority or ability to do (such as manipulating hardware).
The main difference between the two states
When executing in user mode, the memory space and objects that a process can access are limited, and the processor it occupies can be preempted
When executing in kernel mode, it can access all memory space and objects, and the occupied processor is not allowed to be preempted.
Why user mode and kernel mode
Due to the need to limit the access ability between different programs, prevent them from obtaining the memory data of other programs, or obtain the data of peripheral devices, and send it to the network
Switching between user mode and kernel mode
All user program is running in user mode, but sometimes the program really need to do some kernel mode, such as data read from the hard drive, or to get input from the keyboard, etc., and the only thing you can do these things is the operating system, so this program requires the operating system first requests in the name of the program to perform these operations
User mode and kernel mode conversion
The system calls
A user process uses a system call to request work from a service provided by the operating system, such as fork(), which actually performs a system call to create a new process
The core of the system call mechanism is to use an interrupt specially opened by the operating system for users to achieve, such as Int 80H interrupt of Linux
For example:
As shown in the figure above, the kernel program is executed in Kernal Mode, and the User program is executed in User Mode.
When a system call occurs, the user program initiates the system call. Because the system call involves privileged instructions, the user program does not have enough permissions, so it interrupts execution, namely Trap (Trap is a kind of interrupt).
After an interrupt occurs, the program currently executing by the CPU breaks, jumps to the interrupt handler, and the kernel program starts executing, that is, processing the system call.
After the processing is complete, the kernel actively triggers the Trap, which will interrupt again and switch back to user mode.
abnormal
When the CPU is executing a program running in user mode, some unexpected exception occurs, which triggers a switch from the current running process to the kernel-related program handling the exception, and then goes to the kernel state, such as a page missing exception
Interruption of peripheral devices
After the peripheral equipment to complete the operation of the user request, will send a corresponding to the CPU interrupt signal, the CPU will be suspended for the next article is going to execute commands to perform and interrupt signals corresponding handler, if previously executed instructions are under the user mode application, then the transformation process also occurs naturally from user mode to kernel mode switch
For example, after a disk read/write operation is complete, the system switches to the disk read/write interrupt handler for subsequent operations
thread
Thread: The basic unit of system allocation of processor time resources, the smallest unit of program execution
Threads can be regarded as lightweight processes, sharing memory space, each thread has its own independent running stack and program counter, switching between threads is low overhead.
Multiple threads executing simultaneously in the same process (program) (with CPU scheduling, only one thread executing in each slice)
Processes can create user-mode threads through apis or kernel-mode threads through system calls.
User-mode thread
User-mode threads, also known as user-level threads, are not known to the operating system kernel and are created entirely in user-space.
User-level threads have many advantages, such as:
-
Low administrative overhead: no system calls are required to create and destroy.
-
Low switching cost: user space programs can be maintained by themselves, do not need to go to the operating system scheduling.
But there are a number of disadvantages to this thread:
-
The cost of working with the kernel is high: for example, threads are managed entirely by user-space programs, which cannot take advantage of the kernel when it comes to I/O, requiring frequent user-to-kernel switching.
-
High cost of collaboration between threads: Imagine that two threads need to communicate, that communication requires I/O, and that I/O requires system calls, so user-mode threads require additional system call costs.
-
Unable to take advantage of multi-core: For example, the operating system still schedules the process that this thread belongs to, so no matter how many user-mode threads a process has at a time, only one thread can execute concurrently. Therefore, multiple threads of a process cannot take advantage of multi-core.
The operating system cannot optimize for thread scheduling: when a user-mode thread of a process blocks, the operating system cannot detect and deal with the blocking problem in a timely manner, and it does not replace another thread of execution, resulting in a waste of resources.
Kernel-state thread
Kernel-level threads, also known as kernel-level threads, execute in kernel-level mode and can create a kernel-level Thread through system calls.
Kernel-level threads have many advantages:
-
You can take advantage of multi-core cpus: The kernel has high permissions and can therefore execute kernel threads on multiple CPU cores.
-
Operating system-level optimization: Threading in the kernel does not require system calls for I/O; If one kernel thread is blocked, another can execute immediately.
Of course, kernel threads also have some disadvantages:
-
Expensive to create: system calls are required to create, i.e. switching to kernel mode.
-
Poor scalability: managed by a kernel program, it is impossible to have too many.
-
High switching cost: When switching, there is also a need for kernel operation, need to switch the kernel state.
Mapping between user-mode and kernel-mode threads
If you have a user-mode process with multiple threads, what if the process wants to execute one of the following threads?
A common way to do this is to let a kernel thread execute the program that needs to be executed.
After all, a kernel thread is a real thread because it allocates execution resources to the CPU.
If all threads of a process are scheduled by themselves, it is equivalent to implementing a time-sharing algorithm in the main thread of the process to schedule each thread, that is, all threads are executed with the time segment allocated to the main thread by the operating system.
This approach is equivalent to the main thread of the operating system scheduling process; The main thread of a process performs secondary scheduling, scheduling its own internal threads.
The disadvantages are obvious, such as the inability to take advantage of multiple cores, less time allocated to each thread, and the ability to surrender execution rights to the entire process in a blocking scenario.
Thus, the user – mode thread creation cost is low, the problem is obvious, can not use multi-core.
Kernel-state threads, high cost to create, can use multi-core, slow switching.
So we typically create threads in the kernel beforehand and reuse them over and over again.
coroutines
Coroutines, which are more lightweight than threads, are not managed by the operating system kernel, but are completely controlled by programs (that is, executed in user mode).
The benefit of this is that the performance is greatly improved and the resources are not consumed like thread switching.
A subroutine
Or functions, in all languages, are hierarchical calls, like A calls B, B calls C in the middle of execution, C returns when it’s done, B returns when it’s done, and FINALLY A finishes.
So the subroutine call is implemented through the stack, and a thread is just executing a subroutine.
A subroutine call is always an entry, a return, in a clear order.
The characteristic of coroutines is that they are executed by one thread. What is the advantage of coroutines over multithreading?
- High execution efficiency: because the subroutine switch is not thread switch, but controlled by the program itself, therefore, there is no overhead of thread switch, and multithreading than the number of threads, coroutine performance advantage is more obvious;
- Do not need multithreaded locking mechanism: because there is only one thread, there is no conflict of variables written at the same time, in the coroutine control of shared resources do not lock, only need to judge the state, so the execution efficiency is much higher than multithreading.
Thread safety
If your code is in a process that has multiple threads running at the same time, those threads may be running the code at the same time.
If the result of each run is the same as the result of a single thread run, and the values of other variables are the same as expected, it is thread-safe.
process
An application that is running in the system; Once a program runs, it is a process; Is the smallest unit of resource allocation.
In the operating system can run multiple processes at the same time;
At boot time, the disk’s kernel image is imported into memory as an execution copy, which becomes the kernel process.
Processes can be divided into user-mode processes and kernel-mode processes. User-mode processes are usually copies of application programs, and kernel-mode processes are processes of the kernel itself.
If a user-mode process needs to request resources, such as memory, it can request resources from the kernel through system calls.
Each process has an independent memory space to store code and data segments, etc. Switching between programs will have a large overhead;
Time sharing and scheduling
Each process gets a piece of time allocated by the operating system when it executes, and if it exceeds this time, the next process (thread) executes.
Note that modern operating systems schedule threads directly, not processes.
Allocate time segments
As shown in the figure below, process 1 requires 2 time fragments, process 2 only has 1 time fragment, and process 3 requires 3 time fragments.
Therefore, when process 1 is halfway through execution, it will suspend first and then process 2 will start execution. Process 2 can be executed all at once, and then process 3 can start execution. However, process 3 cannot be executed all at once. After executing a time segment, process 1 starts execution. And so on and so on, and so on and so forth, and so on and so forth.
Create a process
The most straightforward way for a user to create a process is to execute a program from the command line, or double-click to open an application, but for programmers, better design is clearly needed.
First, there should be aN API to open an application, such as a function to open an application.
On the other hand, if the programmer wants to make several copies of the state of the current program into separate processes after an expensive initialization, the operating system provides fork instructions.
That is, each fork creates a clone that has all the same states as the original process, but has its own address space.
To create two clones, fork twice.
What if I just want to start a new program?
The operating system provides an API for launching new programs.
If I just want to execute a small program with a new process, let’s say THAT every time a server receives a request from a client, I want to use a process to process the request.
If this is the case, it is recommended that you do not start the process individually and instead use threads.
Because processes are so expensive to create, it is not recommended to create items, allocate memory, and especially to form segments in memory into different regions. So in general, we prefer to create multiple threads.
Different programming languages provide their own apis for creating threads. For example, Java has a Thread class. Go has go-routine (not coroutines, threads).
Process status
Create a state of
Process creation is a very complex process, and generally requires multiple steps to complete: For example, the process first applies for a blank process control block (PCB), and fills in the PCB for controlling and managing process information; The process is then allocated resources necessary to run; Finally, put the process into the ready state and insert it into the ready queue
The ready state
This refers to the process has good luck to the state, the process has been assigned to in addition to the CPU after all the necessary resources, as long as get the CPU, will be executed immediately, if the system has many processes in the ready state, usually put them according to certain strategy in a queue, the queue is called ready queue, to be eligible for execution, no enforcement process
Running state
This indicates that the process has obtained the CPU and is in the executing state. At any one time, in a single-processor system, there is only one process in execution, whereas in a multiprocessor system, there are multiple processes in execution, processes that have both the right to execute and the right to execute
The blocking state
Here refers to the process being performed due to an event, such as I/O request, apply for buffer failure, etc.) is temporarily unable to continue, namely the process execution is blocked, at this time cause process scheduling, the operating system processor is assigned to another ready process, and make up process is in a state of suspension, generally to the suspended state called the blocked state
Termination status
Interprocess communication IPC
Each have their own different user address space, any process of the global variable in another process all could not see, so want to exchange data between processes must by the kernel, open up a buffer in the kernel, process 1’ll copy data from user space to the kernel buffer, process 2 go to read the data from the kernel buffer, The mechanism provided by the kernel is called interprocess communication
Pipes/Anonymous pipes
The pipe is half duplex, so the data can only flow in one direction; When two parties need to communicate, two pipes need to be set up.
-
Can only be used between parent and child processes or between sibling processes (related processes);
-
A separate file system: A pipe is a file for the processes at both ends of the pipe, but it is not a common file. It does not belong to a file system. Instead, it is a separate portal, which forms a file system and exists only in memory.
-
Reading and writing of data: What one process writes to a pipe is read by a process at the other end of the pipe, each time added to the end of the pipe buffer, and each time read from the head of the buffer.
Named pipe (FIFO)
Anonymous pipes, which have no name, can only be used for related interprocess communication.
To overcome this shortcoming, a named pipe (FIFO) is proposed.
Named pipe is different from anonymous pipe is that it provides a path name associated with it, in the form of a named pipe file exists in the file system, in this way, even with a named pipe to create process there is no related process, as long as it can access the path, to communicate with each other to each other through a named pipe, therefore, Unrelated processes can also exchange data through named pipes.
signal
Signals are a mechanism used by processes in Linux to communicate or operate with each other. Signals can be sent to a process at any time without knowing the state of the process.
If the process is not currently executing, the signal is stored by the kernel until the process resumes execution and is passed to it.
If a signal is set to block by a process, the signal is delayed until the blocking is canceled.
The message queue
Message queues are linked lists of messages stored in the kernel, and each message queue is represented by a message queue identifier.
And pipes (nameless pipes: files that only exist in memory; Named pipes: exist in the actual disk media or file system. The difference is that message queues are stored in the kernel and are deleted only when the kernel is restarted (i.e. the operating system is restarted) or when a message queue is explicitly deleted.
Also, unlike pipes, message queues do not require another process to wait on a queue for messages to arrive before one process writes to the queue
The Shared memory
Allowing multiple processes to read and write directly to the same memory space is the fastest form of IPC available and was designed for the low efficiency of other communication mechanisms.
In order to exchange information between multiple processes, the kernel sets aside an area of memory that can be mapped to its own private address space by the process that needs to access it. The process can read and write directly from this area of memory without copying data, thus greatly improving efficiency.
Because multiple processes share a memory segment, some synchronization mechanism (such as semaphore) is required to achieve synchronization and mutual exclusion between processes.
Schematic diagram of shared memory
Once such memory is mapped to the address space of the processes that share it, these interprocess data transfers are no longer kernel – in other words, processes no longer pass each other’s data by making system calls into the kernel.
A semaphore
A semaphore is a counter used by multiple processes to access shared data. The purpose of the semaphore is interprocess synchronization.
To obtain a shared resource, the process needs to do the following:
-
Create a semaphore: This requires the caller to specify an initial value, which is usually 1 or 0 for a binary semaphore.
-
Wait for a semaphore: This operation tests the value of the semaphore and blocks if it is less than 0, also known as a P operation.
-
Hang a semaphore: This operation increments the value of the semaphore by 1. Also known as the V operation.
The Socket (Socket)
A socket is a communication mechanism by which client/server (that is, the process to communicate) systems can be developed either locally on a single machine or across a network. That is, it allows processes that are not on the same computer to communicate with each other over a network.
signal
Signals are the only asynchronous communication mechanism in the interprocess communication mechanism and can be considered as asynchronous notification, notifying the receiving process of what has happened.
It can also be simply understood that the signal is some form of soft interrupt
To view the list of signals supported by Linux, run the kill -l command.
kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAXCopy the code
A few common signals:
signal | describe |
---|---|
SIGHUP | When the user exits the terminal, all processes started by the terminal will receive this signal, and the default action is to terminate the process. |
SIGINT | An interrupt signal that signals when the user types INTR characters (usuallyCtrl+C ) to notify the foreground process group to terminate the process. |
SIGQUIT | andSIGINT Similar, but with the QUIT character (usually isCtrl+\ ) to control the process before it is receivedSIGQUIT When you exitcore A file, in this sense, is similar to a program error signal. |
SIGKILL | Used to terminate the program immediately. This signal cannot be blocked, processed, or ignored. |
SIGTERM | Program terminate signal, andSIGKILL The difference is that the signal can be blocked and processed. Usually used to require the program itself to exit normally. |
SIGSTOP | Stopped the execution of the process. Note the difference between terminate and interrupt: the process is not terminated, but merely paused, and this signal cannot be blocked, processed, or ignored |
Process synchronization
A critical region
Access to common resources or a piece of code through the serialization of multiple threads, fast, suitable for controlling data access
Advantages: An easy way to ensure that only one thread can access data at a time
Disadvantages: Although the critical section is fast, it can only be used to synchronize threads within the process, not multiple processes
The mutex
Designed to coordinate common individual access to a shared resource
Mutex is similar to a critical section, but more complex than a critical section. There is only one mutex, and only the thread that owns the mutex has access to the resource
Advantages: Using mutexes enables safe sharing of resources not only between different threads of the same application, but also between threads of different applications
A semaphore
Designed to control a resource with a finite number of users, it allows multiple threads to access the same resource at the same time, but requires a limit on the maximum number of threads that can access the resource at the same time. A mutex is a special case of a semaphore, where the maximum number of resources equals 1
A Semaphore is an integer variable on which you can perform down and up operations, the common P and V operations
- Down: If the semaphore is greater than 0, the operation -1 is performed. If the semaphore is 0, the process sleeps and waits for the semaphore to be greater than 0;
- Up: Perform the +1 operation on the semaphore to wake up the sleeping process to complete the down operation.
The down and UP operations need to be designed as primitives and indivisible, and it is common practice to mask interrupts while performing these operations.
If the semaphore can only be 0 or 1, it is a Mutex, with 0 indicating that the critical region is locked and 1 indicating that the critical region is unlocked.
The event
Used to notify the thread that some event has occurred, thus initiating the start of subsequent tasks
Advantages: Event objects keep threads synchronized by notifying operations, and can implement thread synchronization operations in different processes
Tube side
One important feature of a pipe is that only one process can use a pipe at a time.
A process cannot hold a pipe until it can no longer execute, or another process can never use the pipe.
The pipe introduces condition variables and related operations: wait() and signal() to implement synchronous operations.
Performing wait() on a condition variable causes the calling process to block, leaving the pipe to be held by another process.
The signal() operation is used to wake up blocked processes.
Producer-consumer problems implemented using the semaphore mechanism require a lot of control from the client code, and the pipe separates the control code, which is not only less error-prone, but also makes it easier for the client code to call.
Context switch
For a single-core, single-threaded CPU, only one CPU instruction can be executed at a time.
Context Switch is a mechanism for allocating CPU resources from one process to another.
From the user’s point of view, computers can run multiple processes in parallel precisely as a result of the operating system’s rapid context switching.
In the process of switching, the operating system needs to first store the state of the current process (including the pointer to the memory space, the currently executed instructions, etc.), then read the state of the next process, and then execute the process.
Process scheduling algorithm
First come, first served scheduling algorithm
This algorithm can be used for both job scheduling and process scheduling. When this algorithm is used in job scheduling, each schedule selects one or more jobs that enter the queue first from the backup job queue, calls them into memory, allocates resources to them, creates processes for them, and then puts them into the ready queue
Short job first scheduling algorithm
Select one or more jobs with the shortest estimated running time from the backup queue and run them into memory
Time slice rotation
Each time, the CPU is allocated to the queue leader process, and the execution of a time slice, the size of the time slice from several ms to several hundred ms, when the execution of the time slice is used up, a timer issued by a clock interrupt request, the scheduler will stop the execution of the process according to the signal, and it is sent to the end of the ready queue
Then, the processor is assigned to the new queue leader process in the ready queue, and it is also allowed to execute a time slice, so that all processes in the ready queue can get the processor execution time of the time slice in a given time
Minimum remaining time is preferred
A preemptive version of shortest job first that schedules in order of remaining running time, and when a new job arrives, its entire running time is compared to the remaining time of the current process.
If the new process takes less time, suspend the current process and run the new process. Otherwise, the new process waits.
Multi-level feedback queue scheduling algorithm:
Described above several kinds of process scheduling algorithm has some limitations, such as short process priority scheduling algorithm, only take care of the short process and ignores the long process of multi-level feedback queue scheduling algorithm can not only make the operation of high priority response and make short work done quickly, so it is now recognized as a better process scheduling algorithm, The UNIX operating system adopts this scheduling algorithm.
For example:
Multi-level queue, that is, multiple queues perform scheduling, consider the simplest two-level model first
In the figure above, two queues with different priorities are designed. The priority rises from bottom to top, the upper queue schedules urgent tasks, and the lower queue schedules ordinary tasks.
As long as the upper queue has a task, the lower queue cedes execution permission.
The low-priority queue can be implemented by preemption + priority queue, so that one time segment can be executed to determine whether there is a task in the high-priority queue.
High-priority queues can be implemented with non-preemption (each task is executed before the next one is executed) + priority queues, so that urgent tasks are prioritized and can be processed first if there is an urgent situation.
Although the above model solves the problem of priority between tasks, it still does not solve the problem of short task first. It can be considered to add more queues to make more levels.
Take this model:
Emergency tasks are still queued high and executed without preemption.
Normal tasks are first placed in the queue with the next highest priority and only allocated a small time slice. If the execution is not complete, the task is not very short, and the task is lowered one level.
At the next level, the lowest-priority queue has a large slice of time, and longer tasks have a larger slice of time available.
In this way, short tasks are executed in higher priority queues, while long tasks are downprioritized, which is similar to the shortest job first problem.
In practice, you could have n layers, one layer after the other sifting out the big tasks, the longest tasks, and putting them into the most free time, because most of the time the CPU is not at full load.
Priority scheduling
Each process is assigned a priority, the process with the highest priority is executed first, and so on, processes with the same priority are executed FCFS, which can be prioritized based on memory requirements, time requirements, or any other resource requirements.
daemon
A daemon is a process that is detached from the terminal and runs in the background to prevent information from being displayed on the terminal during execution and to prevent the process from being interrupted by any terminal information generated by the terminal.
The typical life cycle of a daemon is from system startup to system shutdown.
There are many daemons in Linux systems, the most typical of which is the server process that we often see.
Of course, daemons are often used for many system or automated tasks.
An orphan
If the parent process is still running before the child process exits, the child process becomes an orphan process. Linux handles the orphan process by setting the parent process of the orphan process to process number 1, which is hosted by the init process, which is responsible for cleaning up after the child process exits
zombies
When the child process finds that the parent process does not exit, it sends the SIGCHLD signal to the parent process. However, the parent process does not use wait, WAITPID or other methods to process the SIGCHLD signal to reclaim the child process, and the child process becomes a zombie process that is harmful to the system
The process information left after the child process exits is not collected. As a result, the PCBS occupied by the process control block are not released and zombie processes are formed. The process is dead but process resources are not released
Problems and Hazards
If a large number of zombie processes exist in the system, their process ids will always be occupied, but the system can use a limited number of process ids, the system cannot create new processes because there is no available process ID
Any child process (other than init) does not disappear immediately after exit(), but leaves behind a data structure called a Zombie, which the parent processes at the end of each child process. If the child process passes through exit() before the parent process has time to process it, Using the ps command, you can see that the child process is in Z state.
If the parent process can handle it in time, it may be too late to see the child’s zombie state using the ps command, but this does not mean that the child process does not pass through the zombie state
The zombie process is actually created by its parent. If the parent is killed, the zombie process becomes an orphan and can be handed over to init for recycling
A deadlock
The reasons causing
Competition for system resources: Competition for system resources causes insufficient system resources and improper resource allocation, resulting in deadlocks.
Improper process sequence: When a process is running, the sequence of requesting and releasing resources is incorrect, resulting in a deadlock.
Four necessary conditions for a deadlock to occur
Mutually exclusive: A resource can be used by only one process at a time. That is, only one process occupies a resource in a period of time. If other processes request the resource, the requesting process can only wait
Request and hold condition: A process that has held at least one resource but makes a new request for the resource has been occupied by another process. In this case, the requesting process is blocked but does not release the resource it has obtained
Inalienable condition: a resource acquired by a process cannot be forcibly seized by another process before it is fully used, that is, it can only be released by the process that acquired the resource itself (only voluntarily)
Circular waiting condition: the relationship between several processes that are connected end to end is formed
These four conditions are necessary for deadlocks, and they must be true whenever a deadlock occurs on the system, and no deadlock occurs unless one of these conditions is met
If we break just one of them, we can successfully avoid deadlocks
There’s no way to break the mutual exclusion condition, because we’re using the lock for mutual exclusion
- For the occupy and wait condition, we can apply for all resources at once, so there is no wait.
- For the condition of non-preemption, the thread that occupies part of the resource can actively release the resource it occupies when applying for other resources. In this way, the condition of non-preemption is broken.
- For the condition of circular waiting, we can prevent it by applying for resources in order. The so-called sequential application means that resources are in linear order. When applying, we can apply for resources with small serial number first and then apply for resources with large serial number, so that there will be no cycle after linearization.
Processing method
There are four main methods:
- The ostrich policy
- Deadlock detection and deadlock recovery
- Deadlock prevention, destruction 4 prerequisites
- Deadlock avoidance, banker algorithm
The ostrich policy
Stick your head in the sand and pretend there’s nothing wrong.
Because deadlocks are expensive to resolve, the no-task approach of the ostrich strategy results in higher performance.
When a deadlock occurs that does not have much impact on the user, or the probability of deadlock is low, the ostrich strategy can be used.
Deadlock detection
Instead of trying to prevent deadlocks, take steps to recover them when they are detected.
-
Deadlock detection for one resource of each type
-
Deadlock detection for multiple resources of each type
Deadlock recovery
- Preemption recovery
- Use rollback recovery
- Restore by killing the process
The problem of eating for philosophers
Five philosophers stood around a round table, each with food in front of him.
The philosopher’s life alternates between two activities: eating and thinking.
When a philosopher eats, he should first pick up two chopsticks on his left and right, and only one at a time.
If all philosophers pick up the chopsticks on the left hand side at the same time, all philosophers wait for the other philosophers to finish eating and release their chopsticks, resulting in a deadlock.
The philosopher dining problem can be regarded as a representative problem of dealing with shared resources when concurrent processes are executed concurrently.
To prevent deadlocks, you can set two conditions:
- Two chopsticks must be picked up at the same time.
- Eating is allowed only if neither neighbor has eaten.
Banker’s algorithm
The name of the banker algorithm is that it can be used by the banking system, which will never allocate its capital if it cannot meet the needs of all its customers.
When a new process enters the system, it must state the maximum number of instances of each type of resource that it may require that cannot exceed the total number of system resources.
When a user applies for a set of resources, the system must determine whether and how the allocation of these resources is safe, and then allocate them. If not, the process must wait until instructing some other process to release enough resources.
Safe state
In the deadlock-avoiding method, processes are allowed to dynamically apply for resources. However, the system calculates the security of resource allocation before allocating resources. If the resource allocation does not cause the system to become insecure, the system allocates resources to processes. Otherwise, make the process wait
Therefore, the essence of deadlock avoidance is: how can the system not enter an unsafe state when allocating resources
The Fork function
The fork function is used to create a child process that is the same as the current process. The child process will copy the code segment, data segment, BSS segment, heap, stack and other user space information of the parent process. The operating system will re-apply a location for the child process in the kernel.
The fork system call creates a new process by duplicating an existing one. The new process is stored in a two-way circular list called a task queue. Each item in the list is the structure of a process-control block PCB of type task_struct.
Each process has a unique process identifier (PID). The process identifier of the current process can be obtained by getPID () function, and the process identifier of the parent process can be obtained by getppId () function.
An existing process can create a new process by calling fork child process. The new process created by fork is called once and returns twice. The only difference is that 0 is returned in the child process and ID is returned in the parent process.
whyfork
Will it come back twice?
Because the parent’s stack segment is copied during replication, both processes are stuck in fork waiting for a return, so they return twice, one in the parent and one in the child, with different values.
- The process ID of the new child process is returned in the parent process
- 0 will be returned in the child process
- Return a negative number if an error occurs
Therefore, the return value of fork can be used to determine whether the current process is a child or a parent.
Fork Executes the execution process
When the process forks control to the kernel, the kernel does four things:
- Allocate new memory blocks and kernel data structures to child processes
- Copy part of the parent process’s data structure content (data space, stack, etc.) to the child process
- Adds a child process to the system process list
fork
Returns to start scheduler scheduling
whypid
How different is it in the parent-child process?
The pid of the parent process points to the process ID of the child process. Therefore, the child process has no child process, so pid is 0. The PID here is equivalent to the pointer in the linked list.