Course Content#
Understand the true meaning and differences between the two, and be able to answer the above questions.
Literal Meanings of Both#
Going to Work
- Blocking: There is traffic on the way to work, wait or use other methods, in short, you have to go to work.
 - Non-blocking: There is traffic on the way to work, just don’t go.
 
Mom Asked to Buy Soy Sauce
- Blocking: There is no soy sauce, ask mom if I should get something else, or buy something else.
 - Non-blocking: There is no soy sauce, just don’t buy it.
 
Let Xiao Ming Write a Report
- Blocking: Wait for Xiao Ming to finish writing.
 - Non-blocking: No need to wait for Xiao Ming to write, inform the result later [ask Xiao Ming myself - synchronous / Xiao Ming tells me proactively - asynchronous].
 
Boiling Water
- Blocking: During the process of boiling water, you cannot do other things, you can only stand there and wait for the water to boil.
 - Non-blocking: During the process of boiling water, you can do other things, like go to the living room to watch TV.
 - Reference Simple Explanation of Synchronous and Asynchronous, Blocking and Non-blocking——CSDN
 
Introduction#
- Review O_NONBLOCK in the open function.
- 
 - Everything is a file, so everything can be blocking/non-blocking.
 
 - 
 - Analyze the process of writing a file.
- Open → write → close.
 - Among them, write is a system call, the specific process is as follows:
- Data → kernel → disk: The kernel copies the data and places it in the buffer [block buffer]. When flushing the buffer, it schedules the IO device, finds the inode and block, and writes the data to the disk.
- The first step data → kernel is user mode, the second step kernel → disk is kernel mode, there is a conversion.
 - 
 - Reference How to Understand Linux User Mode and Kernel Mode?——Zhihu
 
 - NON_BLOCK sets the user mode [data → kernel or kernel → data] process.
- Most of the blocking occurs when taking data out from the kernel.
 
 - The process from kernel → disk is actually blocking.
- To allow upper-level applications to quickly return the write status, the kernel writes the data to the disk, which ordinary users do not need to perceive.
 - After the kernel copies the data, the program returns, and ordinary users think the write was successful, but the data is likely still in the buffer [Buffered IO].
 
 
 - Data → kernel → disk: The kernel copies the data and places it in the buffer [block buffer]. When flushing the buffer, it schedules the IO device, finds the inode and block, and writes the data to the disk.
 
 
fcntl#
Operate on file descriptors [can make files non-blocking].
- man fcntl
 - Prototype

- fd: file descriptor. The most common file descriptors: 0, 1, 2.
 - cmd: operation mode.
 - ... /* arg */
- Variable parameters.
 - arg indicates that this parameter is the parameter of the previous parameter [cmd], its meaning depends on cmd.
 
 
 - Description

- cmd in parentheses indicates whether there are variable parameters, at most one.
 - The variable parameter type is generally int, using macro definitions, essentially a bitmask, changing the state through bitwise operations.
 - Among them, there is a category of cmd: related to [file status flags].

- You can obtain or set the file status [such as O_NONBLOCK].
 
 
 - Return Value

- Observe the return value, consider the judgments in the program.
 - Errors return -1, successful setting operations return 0.
 
 
select#
Synchronous I/O multiplexing [interface].
- man select
 - Prototype

- nfds: number of file descriptors.
 - fd_set: set of file descriptors.
- Readable, writable, exceptions.
 - Implemented using arrays at the bottom.
 
 - timeout: time interval.
 - Four macros are used to operate on the set, see below.
 
 - Description

- Allows the program to monitor multiple file descriptors, waiting for one or more file descriptors' I/O operations to be "ready".
- ready: ready, can perform corresponding IO operations on the file, such as non-blocking reads or sufficiently small writes.
 
 - The number of file descriptors that can be monitored is less than FD_SETSIZE [generally 1024].
 
- When exiting, each file descriptor set will be modified, leaving only the file descriptors whose status has changed, serving as an indication.
- Therefore, if select is used in a loop, each set needs to be re-initialized before each call to select.
 
 - The set can be NULL, indicating that no files are being monitored in this type of event.
 - Macros for operating on the set:
- FD_ZERO: clear a set.
 - FD_SET: add a file descriptor to a set.
 - FD_CLR: remove a file descriptor from a set.
 - FD_ISSET: determine which set a file descriptor belongs to, can be used after select returns, as the set has been modified.
 
 - The value of nfds is the maximum number of file descriptors in the three sets plus one.
- The index of file descriptors starts from 0.
 
 
- struct timeval timeout
- Specifies the time interval for select to block while waiting for each file descriptor to be ready.
- Here, blocking means purely blocking, not blocking I/O.
 - [Personal understanding] The synchronization in synchronous I/O multiplexing is reflected here.
 
 - Three conditions for stopping blocking:
- A file descriptor is ready.
 - Signal interruption [kill].
 - Timeout.
 
 - The time interval is not precise, it is difficult to achieve true precision.
- System clock granularity, kernel scheduling delay.
 
 - The timeval structure has two members: seconds, microseconds.
- If both are 0, it will return immediately, can be used for polling.
 - If NULL, it will wait indefinitely.
 
 
- The timeout update function only works on Linux.
 - For compatibility, try not to use it, and use more common functions.
 
 - Specifies the time interval for select to block while waiting for each file descriptor to be ready.
 
 - Return Value

- Returns the number of file descriptors [ready] in all sets at that time.
- Based on the number, use the macro FD_ISSET to inquire about the status change of all file descriptors.
 - 0: Time is up, and no interesting events occurred.
 - -1: error, and sets errno; at this time, the set will not change, and timeout becomes undefined.
 
 
 
[PS]
- select → poll → epoll, increasingly advanced.
- man poll
 - man epoll
 - All perform similar tasks to select.
 
 
Code Demonstration#
Implementing Interfaces to Make Files Non-blocking and Blocking#
- common.h
- 
 - Two interfaces.
 
 - 
 - head.h
- 
 - The order of header files matters, generally placing your own project's header files at the end.
 
- Reference The Path and Order of #include——Google C++ Programming Style.
 
 - 
 - common.c
- 
 - When setting flags, do not change the original flags.
 - 👉 First get, then add/remove flags using bitwise OR / AND.
 
 - 
 
Non-blocking File Descriptor 0#
- 
 - Common files 0, 1, 2 do not need to be opened manually.
- These three files are automatically opened when creating a process.
 - They are inherited.
 
 - Compile command: 
gcc 1.test.c ../common/common.c -I ../common/- Remember to add the common.c file during compilation.
 
 - After setting file descriptor 0 to non-blocking, the key change is that reading and writing of file descriptor 0 becomes non-blocking.
- ① No sleep.
- 
 - scanf reads file descriptor 0.
 - But there is no data, it proceeds directly without blocking to wait for data in file descriptor 0.
 
 - 
 - ② With sleep.
- 
 - During the 5 seconds of sleep, input data in the terminal [+enter, line buffering].
- The process pauses, not occupying CPU.
 - But the standard input stream remains open, and the data entered by the user in the terminal will be passed to file descriptor 0 by the kernel.
- The file is maintained by the kernel, and during the sleep process, the kernel can write data to the file.
 
 
 - After sleep ends, scanf reads the data from file descriptor 0.
 
 - 
 - 【Clarification】 Non-blocking refers to a certain object, such as file descriptor 0; not to a certain function or operation.
 
 - ① No sleep.
 - [PS]
- Blocking: may waste time.
 - Non-blocking: may waste resources.
- Generally used in large programs and high-concurrency servers.
 
 
 
Select on File Descriptor 0#
- Copied from the man manual — EXAMPLE in man select.
 - 
 - select can perceive the arrival of I/O.
 - Running effect.
- 
 - Input ls in the terminal, and after the program ends, ls will still execute.
 - Because the content of the buffer has not been taken away [like scanf], it is ultimately taken by zsh.
 
 - 
 - select was blocking during the monitored time interval.
- In all user-level programs, so-called blocking means sleeping [sleep()].
 - Blocking termination conditions: ready, signal interruption, timeout.
 
 - [Extended Application]
- Set a timer for blocking places, use default values on timeout.
- Avoid blocking for too long due to exceptional situations [SSH host unreachable...].
 - More user-friendly.
 
 
 - Set a timer for blocking places, use default values on timeout.
 
Additional Knowledge Points#
- There are many ways to perceive I/O, among which the kernel fully knows the arrival of IO.
 
Points to Consider#
- When using scanf, when entering a carriage return in the terminal, is the data placed in the buffer, or is the buffer cleared and given to scanf?
 
Tips#
- Course Preview: Multiprocessing [fork...].
 - Consider the implementation of cp.
- Examine file read and write operations.
 - Must be blocking.
 
 
