Operating system basics (two)

Process clone technique-thread

1. Overview The
previous blog talked about the shortcomings of processes, thus inventing threads.
To give an example: At some point in life, everyone wants to be able to clone themselves to accomplish many things. And a process is equivalent to a person, and I also hope to be able to clone and accomplish more missions. So there are threads.
A process can only do one thing at a time, and a thread can do multiple things at the same time. Since a process is a clone of a thread, the essence of each thread is actually the same, that is, it has the same program text.
A thread is also an execution context in a process, or called an execution sequence. All threads in the same address space constitute a process.
In thread mode, a process has at least one thread, and there can be more than one!
Insert picture description here
Decomposing a process into threads can also effectively utilize multi-processors and multi-core computers. Without threads, adding a processor will not increase the execution speed of the process. But if it is broken down into multiple threads, different threads can be run on different processors at the same time, thereby increasing the execution speed of the process.
For example, to open excel, multiple threads may be opened:
Insert picture description here
2. Thread management
If you want to run a thread normally, you must maintain various information about the thread, including various key information, and the data structure that stores these threads is called the thread control table or Thread control block. So what does this thread block have?
You must know that threads share a process space, precisely because of sharing, the shared resources obviously do not exist in the thread control block, but are stored in the process control block. But there are always resources that are not shared, and these resources obviously exist in the process control block!
In order to achieve our goal, although some resources can be shared, then we will make the probability of sharing these resources as large as possible! This is also our original intention to create threads. Therefore, we have directed a judgment criterion:
if a certain resource is not exclusive and will cause a thread to run incorrectly, then the resource is exclusively shared by a certain thread! Other resources are shared by all threads in the process!
For example: According to this standard, the shared resources are: address space, global variables, files, subprocesses, and so on. Timers, signals and CPU time can also be shared. But the program counter cannot be shared, because the execution sequence of each thread is different. Similarly, registers and stacks cannot be shared. This is the context (running environment) of the thread . As shown in the figure:
Insert picture description here
3. Implementation
1) Process manages threads by itself
2) Operating system manages threads
These two types determine whether it is a kernel-mode thread or a user-mode thread.
So why is there no user mode and kernel mode in the process? This is because the process implements concurrency (multi-programming) on ​​the CPU, and the CPU is managed by the operating system. Therefore, the implementation of the process can only be carried out by the operating system kernel, and there is no user mode.
4 kernel mode thread realization
Thread is the clone of the process, the different execution sequence of the process, and the basic unit of CPU scheduling. But the CPU scheduling is implemented by the operating system, so how to manage it? Like the management process, various data of the thread must be maintained, that is, the thread control block is stored in the kernel space of the operating system. In this way, the thread and process control block information are in the kernel, as shown in the figure: The
Insert picture description here
operating system has many benefits to manage the thread. The most important thing is that the user programming is kept simple, and the operating system can monitor all threads at this time.
Disadvantages:
low efficiency, because the thread is in the kernel mode, each thread switch has to fall into the kernel, which is scheduled by the operating system, and it takes a relatively large time to fall into the kernel from the user mode. As the number of processes increases, the kernel space will rapidly decrease!
The most terrible thing:
the implementation of the kernel mode requires modification of the operating system. If that day, you are a scientist and invented the kernel mode thread, you have to find an operating system developer and ask it to modify the operating system. Others will tell you to go away and enjoy the cool. go with. Because of this, there is a user-mode thread.
5. User-mode thread The
user does the thread switching and manages the thread information by himself, and the operating system does not need to wait until the thread exists.
The user writes an execution system as a scheduler, that is, in addition to the normal execution thread, there is also a thread that is specifically responsible for thread scheduling. It has no ability to forcibly take control of the CPU, so it must cooperate. Because in user mode execution, no one has an advantage over others. If you want to gain control, you can rely on everyone's voluntary cooperation. After a thread executes for a period of time, it actively releases resources to others, but this is not necessary in the kernel mode, because the operating system can seize control through periodic clock interrupts.
Insert picture description here
Advantages The
user mode is very flexible and can be applied on all operating systems, and the switching speed between threads is fast. There is no need to modify the operating system. The
disadvantages of
programming are different and the efficiency varies greatly. What is more serious is that it cannot fully meet the requirements of the thread. Achieved goal: If the thread is blocked during the execution of process-level multi-programming
, it will not be able to hand over control, because the instructions that handed over to the CPU cannot be executed, then this thread will affect the entire process, which is the fatal weakness of the thread !
How to solve
1) Do not let the thread call blocking operation
We write a wrap (wrap) to wrap the system call, he will detect whether the issued system call will be blocked, and if it will, the call is forbidden.
For example: When reading the disk, if it can be obtained quickly, it is allowed to execute. On the contrary, it can be called after it is not blocked. This can solve the blocking problem to a certain extent, but the disadvantages are:
(1) The operating system needs to be modified to wrap the system call wrap. .
(2) Reduce the efficiency of system threads.
(3) The blocked thread will not necessarily become non-
blocking afterwards , so it is not necessarily true. 2) After the process is blocked, other blocked threads are activated (scheduler activation).
This method must rely on the operating system, because after the thread is blocked, the control of the CPU has returned to the hands of the operating system. The only way is to let the operating system not switch when the process is switched, but to notify the blocked process to call the execution system and ask if there are other threads that can be executed. This practice is called a second chance. Because after a process hangs, the operating system cake does not immediately switch to another process, but gives the process a second chance to continue its execution. But if the process has only one thread, or other threads have been blocked, the control will return to the operating system again. This time, the operating system will switch to another process.
Defects:
The security risks to the operating system are increased, because the process will not exit the first block, hackers can use this vulnerability to carry out various attacks, and this belongs to the lower layer calling the upper layer, which violates the principle of hierarchical architecture!
6. Thread implementation model of modern operating systems
Combining the two is what operating systems nowadays use.
The execution of the user mode is responsible for the switching of the internal threads of the process during non-blocking, the kernel mode is responsible for the switching of threads during blocking, the blocked threads are divided into a group of kernel mode, and the non-matched group is divided into a group of user mode, so that The advantages of both are obtained:
Insert picture description here
7. The relationship between
multithreading. The purpose of introducing the threading model is to achieve process-level concurrency. Because there are usually multiple threads in a process, but because some shared information will produce a dispatch, it can be summarized as the following two questions:
1) How to synchronize between threads?
2) How to communicate between threads?
The above two problems also exist at the process level. From a higher level, different processes also share a huge space, and this space is the entire computer!
8. From user mode to kernel mode
When will the thread user mode enter the kernel mode?
When an interrupt or exception occurs during the running of the program, the system will automatically switch to the kernel mode to run the interrupt or a processing mechanism. The picture shows the flow of situation switching caused by interruption, and the flow of exception handling is the same or similar.
Insert picture description here
In addition, the program making system calls will also cause the transition from user mode to kernel mode.
Example:
A C++ program calls the function cin. Cin is a standard library function. It will call the read function. The read function is a system call provided by the operating system. The process is as follows:
1) Execute the system call instruction in assembly language ( syscall)
2) Store the called parameters sys_read, file number, size in the specified register or stack (pre-arranged)
3) When the processor executes the syscall instruction, it realizes that this is a system call instruction, and the following operations will be performed:
a) Set the processor to kernel mode,
b) Save the current value (stack pointer, program counter, general register)
c) Point the stack pointer to the kernel stack pointer.
d) Set the program counter to the pre-appointed address, which stores the starting address of the
system call handler 4) The system call handler executes the system call and calls the read function in the kernel
to complete the slave Transition from user mode to kernel mode and complete the functions required by system calls.
9 Deterministic and non-deterministic
threads Threads achieve process-level concurrency, improve system efficiency or throughput, and improve the response time that users feel.
Problem
Due to the existence of multiple threads, as far as each single thread is concerned, there are uncertainties in its execution efficiency, accuracy, and accuracy. Of course, we can improve this uncertainty through the synchronization mechanism, but if an exception occurs during multi-threaded execution, the situation is quite troublesome!
The thread mechanism is very similar to the hardware pipeline mechanism. The pipeline is also concurrent, but at the instruction level (not at the program level). The picture shows the comparison between the instruction-level concurrency of the pipeline and the program-level concurrency of the thread:
Insert picture description here
To some extent, threads and pipelines are the sources of uncertainty in the software and hardware layers, respectively. Pipelining allows us to concurrently execute hardware instructions, and threads allows us to concurrently execute software instructions.

Thread communication

Thread Dialogue: Pipes, Named Pipes, Sockets
Thread dialogue is a certain kind of data message sent by one thread, and the other party receives the data message. These data are transmitted through a shared storage space.

pipeline

One thread writes information to one end of the storage space, and another thread reads information from the other end of the storage space. This is a pipe . The pipe can be either a memory or a disk. To create a pipe, a thread only needs to call the pipe creation. The system call can be.
A pipe is a linear byte array, similar to a file, which is accessed by file reading and writing, but it is not a file, because the existence of the pipe cannot be seen through the file system. The creation of a pipe is different between the shell command line and the program , Under the shell command, you only need to use the symbol "|".
Create pipes under UNIX

$ sort < file1 | grep zou

A pipe is created between the two utilities "sort" and "grep". Create a pipe
under the program. To create
a pipe, you need to use the system call popen() or pipe(). Popen needs to provide a target process as a parameter, and then create a pipeline between the process calling the function and the given target process.
You also need to provide a parameter to indicate the type of pipe when creating it: read pipe or write pipe, and the pipe call will return two file descriptors, one of which is used for reading and writing from the pipe, and the other for writing to the pipe. In other words, pipe connects two file descriptors so that one end can read and the other end can write. Under normal circumstances, after the pipe is called to create a pipe, fork is used to generate two processes, and the two processes communicate using the two file descriptors returned by the pipe.
example:

int pp[2];
pipe(pp);//创建管道
if(fork()==0)//子进程
{
    
    
read(pp[0]);//从父进程读
............
}
else
{
    
    
write(pp[1]);//写给子进程
.............;
}

Another important feature of the pipeline is that there must be a certain relationship between the two threads that use the pipeline. For example, using popen needs to provide the file name of the other end process, and the two threads using pipe belong to the parent and child processes respectively.

Named pipe

If you want to communicate between two unrelated threads, such as threads of different processes, you need to use a named pipe. The named pipe shares a name space with the file system, and we can see the named pipe from the file system, that is, the name of the named pipe cannot be the same as any file in the file system.
For example:
Use the ls command under UNIX to view the created named pipe.

% ls-l fifol
prw-r--r-- 1 jhon user 0  Sep 22 23:11 fifol |

After a thread creates a named pipe, another thread can use open to open the pipe (unnamed pipes cannot be opened) to communicate with the other end.
Named pipe name composition : it is composed of computer name and pipe name, for example: \[host name]\pipe[pipe name]\.

Advantages and disadvantages :
pipes and named pipes can communicate with another process without special design (in terms of application programs), but not all operating systems support this function. Currently, UNIX and UNIX-like operating systems support pipe communication. It is not applicable to other operating systems. Secondly, it is very inconvenient that pipe communication needs to be carried out between related processes (unnamed pipe), or needs to be opened by name (named pipe).

Wormhole: Socket

Sockets are another mechanism that can be used for inter-process communication. It penetrates almost all mainstream operating systems and can support different levels, different applications, and cross-network communication.
General process:
Both parties of communication need to create a socket, one of which acts as the server side and the other as the client side. The server side must first create a service area socket, and then listen on the socket and wait for the remote connection request. The client who wants to communicate with the server side creates a client socket, and then sends a connection request to the service area socket. After the server socket receives the connection request, it creates a client socket on the server and forms a point-to-point communication channel with the client socket on the remote client. After that, the client and server can communicate on the created socket through the send and recv commands.
Binding : The
server needs to be bound to provide web browsing services. The server socket can be bound to a well-known port of a public host. Of course, if it is bound to a specific IP address, it can only be local Use on the machine!
Note : The
server socket does not send data, nor does it receive data (user data, not connection request data), but only generates a "client" socket. After generation, the original service socket returns to the original On the monitoring operation.

Thread Telegram: Signal

Pipes and sockets are very inefficient and resource intensive. The signal is the next communication mechanism we are going to talk about.
**What is:** In a computer, it is a kernel object, or a kernel data structure. After the sender fills in the content of the data structure and indicates the target process of the signal, it sends a specific software interrupt to notify the operating system. The operating system knows that there is a process to send the signal, so it looks for the signal receiver in the specific kernel data structure , And notify, the process that receives the notification handles the signal accordingly.

Thread semaphore: semaphore

In a computer, a semaphore is a simple integer. A process advances when the signal becomes 0 or 1, and the signal becomes 1 or 0 to prevent other processes from advancing.
Semaphore is not only a communication mechanism, but also a synchronization mechanism.

Thread embrace: memory sharing

A process first creates a memory space dedicated to communication, and other processes map the memory space to its own virtual address space, so that when reading and writing the area corresponding to the shared memory in its own address space, it is communicating with other processes (random ).
Advantages and disadvantages :
high flexibility and much more complicated information transmission, but the two processes must be used on the same physical machine, and the security is low.
Insert picture description here
Note: The use of global variables to achieve communication between threads of the same process is not called shared memory.

Mail sending: message queue (applied only in memory)

A list of messages with head and tail. The new message is placed at the end, and the reading of the message starts from the head of the queue:
Insert picture description here
it is similar to a pipe, but it does not require a fixed read and write thread, any process can read and write, and it can also support multiple processes at the same time, and multiple processes can read Write message queue. That is many-to-many, not the point-to-point of the pipeline. Popular in almost all mainstream operating systems.

Other communication mechanisms

There are many communication mechanisms, but the principles are not much different. In the final analysis, they all come from AT&T's UNIX V system.

Process synchronization

A shared global variable, after a thread performs some operations, wants to check the value of the current variable, but before the check is about to be made, another thread modifies the variable at this time, causing a data error!
Why synchronize:
1) Shared global shared variables
between threads 2) The relative execution order between threads is uncertain.
Under normal circumstances, in any computer architecture, a line of code is not a microinstruction, that is, a high-level instruction Corresponding to multiple micro instructions.
Test:
Insert a picture description here.
Which thread will win? ? The answer is uncertain! !
It can be seen from the above that after the introduction of threads, it also introduces a huge problem, that is, the result of multi-threaded execution is uncertain. In this way, the threads must be synchronized! !

The purpose of thread synchronization:
synchronization is to let all threads execute according to certain rules, so that their correctness and efficiency are controlled. That is to control the interleaving between threads.
Why is there an error in the program? Because of competition.
Competition: A phenomenon in which two or more threads compete to execute the same code or access the same resource. This shared code segment or resource that may cause competition is called a critical section.
Of course, in the case of a single core, the same piece of code will not be executed at the same time, but it is possible that two threads are on the same piece of code at the same time. This is code competition. If two threads access a data at the same time, it is a data race.
Mutual exclusion: Only one thread can be in the critical section, and other threads are excluded.
The conditions for mutual exclusion are reached:
1) Two threads cannot be in the critical section at the same time;
2) It can run correctly on any number and speed of cpu.
3) The operation of another process cannot be prevented from outside the mutually exclusive area.
4) The process cannot wait indefinitely to enter the critical area.

lock

The basic operation of the lock:
1) Wait for the lock to reach the open state
2) Obtain the lock and lock the lock.
Readers may ask, it seems feasible, but what if cross execution occurs at this time?
What is needed is an atomic operation, that is, the execution of the instructions cannot be separated, and the execution can be completed at one time.
Atomic operations require hardware support, and software cannot achieve atomic operations.
If one thread acquires the lock, another thread can only wait. What if the event of acquiring the lock is very long? Waiting forever? We can only shorten the waiting time, but cannot eliminate the
solution:
after a thread acquires the lock, before releasing the lock, find a flag bit, set it to, then release the lock, and then perform an atomic operation. If the execution is completed, it will be cleared. After another thread acquires the lock, it checks the state value of the table flag bit, and if it is cleared, it performs an atomic operation, otherwise it performs a function other than the atomic operation.
But no matter what, there is waiting time. How to eliminate it?
That is sleeping and waking up.
Sleeping and waking up:
If the lock is held by the other party, you don’t have to wait for the lock to become open, but sleep. After the lock is opened, you will be woken
up. Insert a picture here to describe how
sleeping and waking up are. Language operation, after a program calls sleep, it enters the dormant state, and the cpu occupied by its lock is released. A program executing wakeup will send a signal to the receiving thread. For example, wakeup (producer) sends a signal to the producer.
Insert a picture description here,
then the problem comes again!
The count variable is not protected and there is no possibility of data competition, so a good way is to add lock and unlock before and after the count variable.
The problem stated above is solved, and there is another problem, that is, the producer and the consumer will not wake up from sleep, so if both go to sleep, they will naturally not be able to wake up. The reason for this is that the producer issues If the wake-up signal is lost (the consumer is not asleep at this time), then we want to accumulate the signal, instead of throwing it away!
For example, after the consumer executes sleep, the wake-up signal sent by the producer before this is still retained. Therefore, the consumer will get the signal to wake up immediately, and the operating system primitive that can accumulate the signal is the semaphore!

signal

Semaphore is the most powerful of primitives. It is not only a synchronization primitive, but also a communication primitive, and it can also be used as a lock.
To put it plainly, the semaphore is a counter whose value is the number of signals currently accumulated. There are two operations.
Down subtraction operation:
1) Determine whether the value of the semaphore is greater than or equal to 1
2) If it is, subtract the semaphore by 1, and continue to execute
3) Otherwise, wait on the semaphore (the thread is suspended)
Up addition operation :
1) Increase the value of the semaphore by 1 (this operation will wake up a thread waiting on the semaphore)
2) The thread continues to execute.
Note: Although these two operations are divided into several steps, they are both Atomic operation.
If we limit the value of the semaphore to 0 and 1, what we will obtain is a lock, which also becomes a binary semaphore:
binary semaphore Dowm subtraction operation:
1) Wait for the value of the signal to become 1.
2) Set the value of the semaphore to 0
3) Continue to perform the
binary semaphore Up operation:
1) Set the value of
the semaphore to 1 2) Wake up the first thread waiting on the semaphore
3) Continue Execute the
application down :
use binary semaphores for mutual exclusion:

down()
<临界区>
up()

It can be said that the binary semaphore is the combined
shortcomings of the two primitive operations of lock and sleep and wake up : we now have only two semaphores, which is relatively simple, but once there are more than a dozen or dozens of them, it is difficult for us to do it. No deadlock or inefficiency occurs, so we use the monitor

Tube (monitor)

Compiler management, correctness depends on the compiler

When you can’t do it, leave the difficulty to the victim

A monitor is a combination of subroutines, variables and data structures. In other words, you need to frame the synchronized code with a monitor structure, and place the code to be protected between the begin monitor and the end monitor, that is Get synchronization protection.

Guess you like

Origin blog.csdn.net/weixin_42271802/article/details/106137941