Skip to content

Day85 - poll() Internals

Objective

Understand how Linux poll() integrates with wait queues to implement event-driven I/O.

This lab builds a simplified poll subsystem step by step and demonstrates how a userspace poll() operation interacts with a driver's poll callback.


Learning Objectives

After completing this lab, you should understand:

  • The purpose of poll()
  • How poll_wait() works
  • The role of poll_table
  • Driver poll callback flow
  • Event masks (POLLIN)
  • Relationship between poll and wait queues
  • Blocking event-driven I/O

Lab1 - Poll Table Registration

Objective

Build a simplified poll_table.

Implement:

  • poll_table
  • poll_table_entry
  • poll_wait()

Verify that a poll operation records all monitored wait queues.

Expected Flow

poll_wait()
        │
        ▼
poll_table
        │
        ▼
poll_table_entry
        │
        ▼
wait_queue_head

Expected Result

  • Three wait queues are registered.
  • Dump shows every registered wait queue.
  • Destroy correctly releases all entries.

Lab2 - Driver poll Callback

Objective

Implement a simplified driver poll callback.

Driver Flow

driver_poll()
        │
        ▼
poll_wait()
        │
        ▼
Check device state
        │
        ▼
Return event mask

Validation

Case 1

data_ready = false

↓

mask = 0

Case 2

data_ready = true

↓

mask = POLLIN

Lab3 - Userspace poll()

Objective

Simulate a simplified userspace poll().

Flow

user_poll()

↓

poll_table_init()

↓

driver_poll()

↓

poll_table_destroy()

↓

return mask

Validation

When no data is available:

mask = 0

When data is available:

mask = POLLIN

Lab4 - Blocking poll

Objective

Combine poll(), wait queues and wakeup.

Poll Thread

user_poll()

↓

driver_poll()

↓

poll_wait()

↓

wait_event()

↓

driver_poll()

↓

return POLLIN

↓

read()

Producer Thread

sleep()

↓

data_ready = true

↓

wake_up()

Expected Output

[POLL] user poll start
[POLL] no event, sleep
[DRV] data ready
mask=0x00000001
[DRV] read data

Summary

This lab demonstrates the complete event-driven I/O flow:

Userspace poll()

↓

Driver poll()

↓

poll_wait()

↓

wait queue

↓

sleep

↓

wake_up()

↓

Driver poll()

↓

POLLIN

↓

read()

The most important takeaway is that:

  • poll_wait() only registers a wait queue.
  • It never blocks.
  • The driver determines readiness by returning an event mask.
  • read() consumes the data after poll() reports that data is available.