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.