On a current Raspberry Pi OS release, how does a userspace program obtain a GPIO line, and why is the older /sys/class/gpio interface discouraged?
answer
- sysfs export is the old way
- one chip, one device node
- who owns the line, and for how long
- closing the fd frees the pin
- Pi 5 moved GPIO off the SoC
basics
~20 sLinux now exposes GPIO as character devices at /dev/gpiochipN, and a program requests specific lines through ioctls, usually via libgpiod. The kernel releases a line when the requesting file descriptor closes. The older /sys/class/gpio export interface is deprecated and leaves lines stranded.
solid answer
~50 sEach GPIO controller appears as a character device — `/dev/gpiochip0` and friends — and a process opens it and requests the lines it wants with an ioctl, receiving a file descriptor that *owns* those lines. In practice you go through `libgpiod` (the `gpiodetect`, `gpioinfo`, `gpioget`, `gpioset` tools, or its C/Python bindings) or a higher-level library such as `gpiozero`. Ownership tied to a descriptor is the whole point: two processes can't silently fight over a pin, and when your process dies the kernel reclaims the line. The old `/sys/class/gpio` interface, where you echoed a number into `export` and poked files, is deprecated: it is global mutable state with no owner, so a crashed program leaves a pin exported and configured, and there is no way to know who claimed it. Access is granted by group and udev rules on the device node rather than requiring root.
code
python · 10 linesfrom gpiozero import LED
from time import sleep
led = LED(17) # BCM GPIO17, claimed via the kernel GPIO driver
for _ in range(5):
led.on()
sleep(0.5)
led.off()
sleep(0.5)
# leaving scope releases the line; a crash releases it toogo deeper
Know that you drive pins through a library such as gpiozero rather than writing files by hand, and that BCM numbering is not the same as physical header pin numbering.
Explain the character device: you open /dev/gpiochipN, request lines by ioctl, and the kernel binds them to your file descriptor. Contrast that with the ownerless sysfs export interface and name the leaked-state and contention problems it causes.
Demonstrate operational judgment: run the daemon as a group member rather than root, expect line ownership to survive a crash cleanly, and diagnose contention with the consumer field rather than guessing. Know why memory-mapped register libraries did not survive the Pi 5.
Own the portability decision — choosing the kernel-mediated API over direct register access is what lets one codebase span hardware generations and other Linux boards. Weigh that against the latency cases where userspace register access or a dedicated microcontroller is genuinely required.
## Two generations of GPIO interface For years the way to toggle a pin from a shell was the sysfs GPIO interface: write a pin number to `/sys/class/gpio/export`, then write to `direction` and `value` in the directory that appears. It is easy to demonstrate, and it is why every old blog post looks the same. It is also deprecated, and the kernel maintainers have been pushing people off it for years. The replacement is the GPIO character device. Every GPIO controller the kernel knows about is exposed as `/dev/gpiochipN`. You open the chip, then issue an ioctl asking for specific *lines* with the flags you want — input or output, active-low, bias pull-up or pull-down, edge events — and you get back a file descriptor representing your request. ```bash gpiodetect # list the gpiochips on this board gpioinfo gpiochip0 # per-line name, direction, and current consumer ls -l /dev/gpiochip0 # the device node and its group ownership ``` ## Why ownership by file descriptor matters The sysfs interface has no concept of an owner. Exporting a pin mutates global state that outlives the process that did it. Three consequences follow, and every one of them bites in production: 1. **Leaked state.** A program that crashes between `export` and `unexport` leaves the pin exported, still configured as an output, still driving whatever it was driving. The next run finds the export already present and errors, or worse, inherits a half-configured pin. 2. **No arbitration.** Nothing stops a second process from writing to the same `value` file. Two daemons can drive one pin against each other with no diagnostic anywhere. 3. **No attribution.** When a pin behaves oddly you cannot ask the kernel who is using it. The character device fixes all three with one mechanism: the request is bound to a file descriptor. Close it — deliberately, or by exiting, or by being killed — and the kernel frees the line and returns it to its default state. A second request for a line that is already claimed fails with `EBUSY` instead of silently interleaving. And `gpioinfo` prints a *consumer* string for each claimed line, so you can see who has it. Edge events are the other big improvement. Under sysfs you polled a file or used `poll()` on it and read a timestamp of your own making. The character device delivers edge events with kernel timestamps on the same descriptor, which is what you need when the interval between edges is the measurement. ## Permissions: not a root-only device Raspberry Pi OS ships udev rules and a `gpio` group so that members can open the GPIO device nodes without being root. This is the right answer to "do I need sudo for GPIO?" — you add the service account to the group rather than running the whole daemon as root. It is also why a program that works interactively for the default user fails when you run it under a system account you forgot to add to the group. ## The Raspberry Pi 5 wrinkle Older Python libraries such as `RPi.GPIO` do not use the character device at all. They memory-map the SoC's peripheral registers (through `/dev/gpiomem`) and write GPIO control registers directly, because on a Pi 4 and earlier the GPIO block lives in the Broadcom SoC at known addresses. On the Raspberry Pi 5 the GPIO is provided by the separate RP1 I/O controller, so those hard-coded register layouts describe hardware that is no longer there and the library fails. Code written against `libgpiod`, `lgpio`, or `gpiozero` keeps working, because the kernel driver knows which controller it is talking to and the userspace API is the same on both boards. Raspberry Pi also publishes a drop-in shim, `rpi-lgpio`, that reimplements the old `RPi.GPIO` API on top of the modern interface for people with existing code. This is the practical reason the interviewer asks: the answer "use the character device" is not pedantry about deprecated ABIs, it is the difference between a program that survives a hardware generation and one that does not. ## Numbering One more source of confusion: the numbers. `gpiozero` and most Raspberry Pi documentation use *BCM* numbering — GPIO17 is the SoC line name, which happens to appear on physical header pin 11. Physical pin numbering is a separate scheme, and line offsets within a gpiochip are a third. Say which scheme you mean; mixing them is the most common cause of "my LED is on the wrong pin".
- A colleague's script leaves a pin driving high after it crashes. Which interface were they using, and why does that not happen with the character device?That is the sysfs interface: `export` mutates global state with no owner, so a crash between export and unexport leaves the line configured and driving. With the character device the request is bound to a file descriptor; when the process dies the kernel closes it and returns the line to its default state. It also lets `gpioinfo` name the current consumer, so contention is visible instead of silent.
- Why does an old script using RPi.GPIO fail on a Raspberry Pi 5?`RPi.GPIO` bypasses the kernel and writes SoC peripheral registers directly at addresses that were valid up to the Pi 4. On the Pi 5 the GPIO block lives in the separate RP1 I/O controller, so that register map no longer describes real hardware. Code on `libgpiod`, `lgpio` or `gpiozero` is unaffected because the kernel driver abstracts the controller; `rpi-lgpio` exists as a drop-in shim for the old API.
- Does GPIO access require root?No. Raspberry Pi OS ships udev rules that give a `gpio` group access to the GPIO device nodes, so you add the service account to that group instead of running the daemon as root. The usual failure is a program that works for the interactive user and fails under a system account nobody added to the group — check `id -nG` for the account that actually runs it.
saying these in an interview costs you the question
- Just echo the pin number into /sys/class/gpio/export
- GPIO always needs root on Linux
- Any GPIO library works on any Pi model
- Two processes can share a pin safely if they take turns
- BCM numbers and physical pin numbers are the same