The blunt!
It’s time to rush again, old man
Last time we talked about “What are processes, threads, and coroutines, why to use them, and the differences between them” (those who want to see them are here). Today we’re going to set our eyes a little higher and look at the application of processes, threads, and coroutines — concurrent programming
Concurrent programming
Concurrency, as the name implies, is multiple programs running together. Where does it run? Of course is the CPU
So concurrency is more than one program running on the CPU?
No no no! In terms of a single CPU can handle only one program at a time, was able to look “work together”, because the operating system USES a time-sharing system, what time slice, scheduling algorithm, program alternating is running on the CPU, looks as if he were running together (here is not opened, later update to everyone)
So is that what concurrency is all about? It’s when multiple programs run alternately through the operating system’s schedule, so it looks like they’re running together, so it’s called concurrency, right?
Of course not. It’s called concurrent programming because there are places where they actually run at the same time.
Review the basics of the operating system. As we know, the operating system adopts virtual storage, and its core is the kernel. In order to protect the security of the kernel, the operating system divides the virtual space into two parts, one part is the kernel space, the other part is the user space, just like the command room on the starship.
At this time the smart you will certainly find a problem
The result of the CPU operation is in kernel space, but we read the result in user space, so there is a process of data going from kernel space to user space
Yes, from the operating system level, there are “waiting for data, data copy to kernel space, CPU operation, data copy from kernel space to user space”
Concurrency occurs in these processes.
Here’s a classic diagram
The middle part is the concurrent part, so as long as the CPU processes are serial, all the other processes can be executed concurrently.
We refer to data from “wait” to “copy to user space” as an IO operation
Among them, “waiting for data – data copy to kernel space – CPU operation” is called data preparation phase, “data copy from kernel space to user space” is called data copy phase
Linux network I/O mode
Knowledge without application is soulless. In the same way, knowledge that is not asked about is lonely.
When we happily chat here, the interviewer will smile and ask you
Ok, can you tell me what network IO modes are available in Linux?
Okay, good for you.
There are five network I/O modes in Linux
- Blocking I/O
- Non-blocking I/O
- I/O multiplexing
- Signals drive I/O
- Asynchronous I/O
Because signal-driven I/O is not commonly used, it is generally necessary to understand the other four I/O modes
When we are trying to count the five patterns on our fingers, the sleepy interviewer’s eyes suddenly light up and says to you with a slight smile
Ok, what’s the difference between blocking/non-blocking IO and synchronous/asynchronous IO?
Is to me
But since they asked, we have to prepare. To distinguish these four concepts, it is important to keep in mind the previous data flow process
(Play it again)
Blocking I/O
User space, which has no direct access to kernel space, needs to make a read call when it wants results
But the preparation and calculation of the data need a process. For example, when we make a network request, the data has not yet returned, the process needs to wait, when the data is returned to kernel space, after a series of operations, finally copied to user space.
The process has been waiting since it made the call, which is the blocked state.
When the data copy is complete, the process cannot run, and the kernel needs to give an OK signal to the process that the data has been copied to you. Then the user process will be unblocked and run happily.
So obviously, the feature of blocking I/O is that the process is blocked until the kernel returns a signal that the data copy is complete. That is to say, blocking actually consists of two phases, one is waiting for data to be prepared, and the other is copying data from kernel space to user space
Ok, now that you understand the process of blocking I/O calls, the next I/O pattern is simple
Non-blocking I/O
And “blocking I/O” is different. When a “non-blocking I/O” process makes a read call, it immediately returns an error if the kernel data is not ready.
From the user process’s point of view, it initiates a read operation and immediately gets a result without waiting. When the user process determines that the result is an error, it knows that the data is not ready and sends the read call again, asking again and again.
When the kernel completes the computation and prepares the data and receives another call from the user process, it immediately copies the data into user space and returns the OK signal
So the feature of “non-blocking I/O” is that the user process is constantly asking the kernel if the data is ready
I/O multiplexing
“I/O multiplexing” is actually a variant of “blocking I/O”.
Select, poll, and epoll are all “I/O multiplexing” mechanisms that allow a single process to process I/O for multiple network connections simultaneously
Multiple network connections handled by a single process?? !
Yeah, like the select method. The user process first calls the select method, which does two things: one is to manage multiple sockets responsible for network connections, and the other is to let the kernel monitor the socket status
When the kernel receives the data from the socket and completes the operation, the select method returns and the user process makes a read call to copy the data from the kernel space to the user space
Note that the socket itself is non-blocking, but the entire user process is.
Blocking is the same as “blocking I/O”, which consists of two phases: one is blocking while the select method is waiting for the socket to return data, and the other is blocking while the data is copied from the kernel control to user space
So, the feature of “I/O multiplexing” is that a process can wait for multiple signals at the same time and make a system call when any of them is ready
Asynchronous I/O
Finally the last one, come on, come on!
Unlike the previous I/O modes, asynchronous I/O does not block
Once the user process makes the read call, it can immediately start doing other things. The kernel returns immediately, so there is no blocking of the user process.
When the kernel data is computed, it automatically copies the data into user space and sends a signal to the user process that the read call is complete. So the user process does not copy the data after receiving the OK signal, and the data copy phase does not block
Therefore, the feature of asynchronous I/O is that the user process does not need to wait for a signal from the kernel
conclusion
Finally, the introduction is over
To summarize
The difference between blocking AND non-blocking IO is
Does the kernel return data immediately when preparing data?
A blocking I/O returns only after the kernel has prepared the data, whereas a non-blocking I/O returns during the preparation.
The difference between synchronous AND asynchronous IO
Synchronous IO blocks processes, asynchronous IO does not block processes
So “blocking I/O”, “non-blocking I/O”, and “I/O multiplexing” are synchronous I/O
If you weren’t careful enough, you might ask
Why is “non-blocking I/O” synchronous I/O?
Look closely at the flow chart for “non-blocking I/O”
When the kernel data is ready and the user process asks again, the data is copied from kernel space to user space. The user process is blocked until the copy is complete and the kernel returns a completion signal.
Therefore, blocking occurs both “blocking I/O” and “non-blocking I/O”
The difference is that “blocking I/O” blocks both during the data preparation phase and during the copy from kernel space to user space, whereas “non-blocking I/O” blocks only during the copy phase
At the end
So far, we’ve looked at the four main Network I/O patterns in Linux, and this is the first article in our series on concurrent programming, so I’m not sure that’s clear enough
Every day I share an article about “Fundamentals of computers,” “Operating systems,” “Python,” and “Data analysis.
Technology should not be complex boring, just a knowledge point to master often need to contact a lot of fragmentary knowledge. I hope I can organize these knowledge in an orderly way and give you a clear arrangement from the perspective of an explorer ~
If you like it, you’re welcome to go to 👍, and if you do, I promise you’ll get more interesting updates later
Thank you for reading this far!! No thanks, can only “bang bang bang” I wish you a happy family, happy every day ~