In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)05/31 Report--
This article introduces the knowledge of "how to achieve device blocking / non-blocking reading and writing by Linux". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Implementation of device blocking IO
When we read and write the IO of the device file, we will eventually call back the corresponding interfaces in the driver, and these interfaces will also appear in the process (kernel) space of the reading and writing device process. If the conditions are not satisfied, the interface function will make the process go to sleep. Even if the user process of the reading and writing device goes to sleep, that is, we often say that blocking has occurred. In a word, the essence of read-write device file blocking is that the driver blocks the device file in the driver. The read and write process can be summarized as follows:
1. Define-initialize the waiting queue header
/ / define waiting queue header wait_queue_head_t waitq_h;// initialization, wait for queue header init_waitqueue_head (wait_queue_head_t * Q); / / or / / define and initialize waiting queue header DECLARE_WAIT_QUEUE_HEAD (waitq_name)
Among the above choices, * will directly define and initialize a waiting header, but it is not convenient to use global variables to pass parameters in the module, depending on the requirements.
We can follow the source code and see what the above lines have done:
/ / include/linux/wait.h struct _ _ wait_queue_head {spinlock_t lock; struct list_head task_list;}; typedef struct _ _ wait_queue_head wait_queue_head_t
Wait_queue_head_t
The spin lock used in this queue
-- 27music-- > A link that strands the whole queue together.
Then let's take a look at the initialized macro:
# define _ WAIT_QUEUE_HEAD_INITIALIZER (name) {.lock = _ _ SPIN_LOCK_UNLOCKED (name.lock), .task _ list = {& (name) .task_list, & (name) .task_list}} define DECLARE_WAIT_QUEUE_HEAD (name)\ wait_queue_head_t name = _ _ WAIT_QUEUE_HEAD_INITIALIZER (name)
DECLARE_WAIT_QUEUE_HEAD ()
Create a waiting queue header named name based on the string name passed in
Initialize the above task_ list field without using the kernel standard initialization macro, speechless.
two。 Add this process to the waiting queue
Add an event to the waiting queue, that is, the process goes to sleep until condition is true. The version of * * _ interruptible indicates that sleep can be interrupted, while the version of _ timeout** indicates the timeout version, and the timeout will be returned. This naming convention can be found everywhere in kernel API.
Void wait_event (wait_queue_head_t * waitq_h,int condition); void wait_event_interruptible (wait_queue_head_t * waitq_h,int condition); void wait_event_timeout (wait_queue_head_t * waitq_h,int condition); void wait_event_interruptible_timeout (wait_queue_head_t * waitq_h,int condition)
This is the core of the waiting queue. Let's take a look.
Wait_event
└── wait_event
└── _ wait_event
├── abort_exclusive_wait
├── finish_wait
├── prepare_to_wait_event
└── _ wait_is_interruptible
# define wait_event (wq, condition) do {if (condition) break _ _ wait_event (wq, condition);} while (0)
Wait_event
If condition is true, return immediately.
-- 248 color-> otherwise call _ _ wait_event
# define _ _ wait_event (wq,condition,state, exclusive, ret, cmd)\ ({\ for (;;) {\ long _ int = prepare_to_wait_event (& wq, & _ wait, state) \\ if (condition)\ break \ if (_ _ wait_is_interruptible (state) & & _ _ int) {\ _ _ ret = _ _ int \ if (exclusive) {\ abort_exclusive_wait (& wq, & _ wait,\ state, NULL);\ goto _ _ out \ break;\}\ cmd \}\ finish_wait (& wq, & _ wait);\ _ out: _ _ ret \})
-- 206Mutual-> endless cycle polling
If the condition is true, jump out of the loop and execute finish_wait (); the process is awakened
-- 212 abort_exclusive_wait-> if the sleep mode of the process is interruptible, then the process will be awakened when the interrupt comes
If neither of the above is satisfied, the incoming schedule () will be called back, that is, go back to sleep.
Template
Struct wait_queue_head_t xj_waitq_h; static ssize_t demo_read (struct file * filp, char _ _ user * buf, size_t size, loff_t * offset) {if (! condition) / / condition can be set to wait_event_interruptible (& xj_waitq_h,condition) in the interrupt handler function;} static file_operations fops = {.read = demo_read,} Static _ init demo_init (void) {init_waitqueue_head (& xj_waitq_h);}
Implementation of IO Multiplexing
For ordinary non-blocking IO, we only need to register the read/write interface in the driver without blocking mechanism. Here I want to discuss IO multiplexing, that is, when the read/write in the driver does not implement the blocking mechanism, how to use the kernel mechanism to support IO multiplexing in the driver. This is the API we're going to use.
Int poll (struct file * filep, poll_table * wait); void poll_wait (struct file * filp, wait_queue_head_t * wait_address, poll_table * p)
When the application layer invokes the select/poll/epoll mechanism, the kernel actually traverses the poll interface in the driver of the callback related file, and determines whether the file IO has a corresponding event by the return value of the poll interface of each driver. We know that the core difference of these three IO multiplexing mechanisms lies in the way monitoring files are managed in the kernel, namely bits, arrays, linked lists, but for each driver. The APIs for callback are all poll.
Template
Struct wait_queue_head_t waitq_h;static unsigned int demo_poll (struct file * filp, struct poll_table_struct * pts) {unsigned int mask = 0; poll_wait (filp, & wwaitq_h, pts); if (counter) {mask = (POLLIN | POLLRDNORM);} return mask;} static struct file_operations fops = {.owner = THIS_MODULE, .poll = demo_poll,} Static _ init demo_init (void) {init_waitqueue_head (& xj_waitq_h);}
Other API
We have just discussed how to use wait queues to implement blocking IO and non-blocking IO. In fact, about waiting queues, the kernel also provides a lot of other API to complete related operations. Let's take a look at this.
/ / Sleeping sleep_on (wait_queue_head_t * wqueue_h); sleep_on_interruptible (wait_queue_head_t * wqueue_h); / / waking up waiting process void wake_up (wait_queue_t * wqueue); void wake_up_interruptible (wait_queue_t * wqueue); and "how Linux implements device blocking / non-blocking reading and writing" ends here. Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.