When I read the Node HTTP module documentation, I noticed the server.timeout property. I wanted to introduce it briefly, but after sorting it out, I found that there is a huge content supporting timeout: server.timout -> node core timers -> uv timers -> linux msleep/hrtimer -> clocksource -> tsc -> cmos rtc -> clock After the end of the timer series, Noder can roughly understand: How clock cycle drives Linux msleep/hrtimer; The relationship between Linux timers and UV timers; Relationship between Node timers and UV timers.
The Timer in Libuv made a bold guess that epoll was implemented by low-precision timers in Linux, but unfortunately it was only half right. There are two things to note about the use of epoll in timers: Synchronous blocking and timing trigger, which is realized by the high precision timer of Linux (in fact, as long as Linux supports high precision timer, eventually all timers are high precision timer, but the low precision timer is simulated by high precision timer).
The timer
In the whole process of learning the timer to read the source code, I have been very concerned about the trigger source, what triggers the code execution. The essence of trigger source has two kinds: hardware trigger, software trigger.
Software trigger
while(true) { const now = getTimeFromTimeKeeper(); If (now === 'double 11') {emitEventInvokeActions(); // trigger source} schedule()}Copy the code
Hardware trigger
Hardware trigger by sending interrupt signal, inform CPU to execute the specified interrupt program, in the interrupt program to determine whether to reach the specified time, the cost is lower than software trigger. You can run /var/log/dmesg to view the process of hardware startup registration and switchover
#$ cat dmesg|grep -i -E "hz|clock|time|tsc|rtc|apic|hpet" ... [1.792655] Clocksource: Switched to clocksource TSCCopy the code
Epoll Indicates the timing trigger
In modern computers, both high and low precision timing is implemented by means of hardware interrupts, so there is no mystery about epoll timing triggering.
SYSCALL_DEFINE4(epoll_wait,… , int timeout) :
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, int maxevents, long timeout) { ... struct timespec64 end_time = ep_set_mstimeout(timeout); *to = timespec64_to_ktime(end_time); Schedule_hrtimeout_range (to, slack, HRTIMER_MODE_ABS); // If the timer is set to work... __set_current_state(TASK_INTERRUPTIBLE); // Interruptible sleep state... __set_current_state(TASK_RUNNING); // Run status. Not necessarily CPU in use, perhaps waiting for scheduling... }Copy the code
The basic principle of a timer is relatively simple, but it can be seen from the above that the timer is triggered asynchronously. How does epoll block synchronously?
Epoll Synchronization is blocked
Libuv timer relies heavily on epoll synchronization blocking function, triggering problem is solved by high precision timer, then how to solve the synchronization problem — process scheduling (operating system principle)?
In the ep_poll method, the __set_current_state method is actively called to force the process into a TASK_INTERRUPTIBLE state. The process exits the CPU schedule and enters a waiting state, which can be awakened by an interrupt created by the timer.
Epoll is long overdue
Epoll smells so good, why was it introduced only in the 2.6 kernel release?
As a big front-end developer, I’m not familiar with the history of Linux, but I’m familiar with React. Component +stackReconcile -> fiberReconcile+hook -> concurrencyReconcile+hook ConcurrencyReconcile +hook is so nice, especially hook. Why wasn’t react introduced in the first place? If…
History has no ifs. Software is evolved, not designed. The products of each era are closely related to the background of the era at that time, instead of thinking about the why of the old era in a new era. Time is developing, people are also progressing, live in the moment.
Follow my wechat official account “SUNTOPO WLOG”, welcome to leave a comment and discuss, I will reply as much as possible, thank you for reading.