mdev vs udev: What Fits a Small Embedded Rootfs
On a small embedded rootfs, device nodes and hotplug usually fork into BusyBox mdev (hotplug helper + mdev -s to seed /dev) versus full udev / systemd-udevd (daemon that consumes kernel uevents and matches .rules).
BusyBox docs/mdev.txt describes two primary uses: initial population and dynamic updates. Both need sysfs mounted at /sys; dynamic updates also need kernel hotplug. udev(7)-family docs describe udevd receiving add/remove/change uevents and matching configured rules against device attributes to create nodes, symlinks, and extra device info.
This post stays on three axes only: when mdev fits · when udev is needed · rule-migration caveats. No invented board timings, prices, or affiliates.
Grounded in BusyBox mdev.txt (and BusyBox examples/mdev.conf), udev(7), and systemd-udevd.
When does mdev fit?
One-line answer: Prefer mdev when the root is small, BusyBox is already present, and you only need node creation, ownership/mode, simple rename/helpers. If the contract is “ship distro .rules unchanged,” mdev is the wrong tool.
Typical BusyBox init sketch:
mount -t proc proc /proc
mount -t sysfs sysfs /sys
echo /sbin/mdev > /proc/sys/kernel/hotplug # or: sysctl kernel.hotplug=/sbin/mdev
mdev -s
# optional: tmpfs on /dev, mkdir /dev/pts, mount -t devpts …
| Condition | Lean mdev | Why (docs) |
|---|---|---|
| BusyBox image, initramfs, recovery root | Strong | Seed + hotplug without a separate udev daemon |
Default root:root 660 plus a few permission tweaks | Strong | /etc/mdev.conf is optional; unmatched → 0:0 660 |
Match by device-name regex, maj/min, or $ENV= | OK | First field: regex / @maj,min / $envvar= |
| Rename, symlink, suppress node | OK | =path, >path (move + symlink), ! (no node) |
| Short shell command on add/remove | Limited | @ / $ / * via system(); needs /bin/sh; stdio → /dev/null |
| Firmware load path | Supported | Load requested names from /lib/firmware/ |
Deep ATTRS{} parent walks / ID_* persistent names | Weak | Different matching model than udev rules |
mdev.conf parsing stops at the first matching line unless the line starts with -. To override the default, add a total-match line such as .* uid:gid mode (documented pattern).
Do not: generalize from invented “mdev was N ms faster on our board” numbers. Choose by rootfs budget × matching depth.
When is udev required?
One-line answer: You need udev/systemd-udevd when the contract is attribute-based matching, persistent names, distro .rules, or udevadm workflows. A small rootfs does not cancel that contract.
udev collects rules from /usr/lib/udev/rules.d, /run/udev/rules.d, /etc/udev/rules.d, and processes them in lexical order (same basename: higher-priority path wins). Match keys include KERNEL, SUBSYSTEM, ATTR{…}, parent walks via ATTRS{…} / KERNELS / SUBSYSTEMS, ENV{…}, PROGRAM / RESULT. Assignments include NAME, SYMLINK, OWNER/GROUP/MODE, RUN, and more.
| Condition | Lean udev | Why |
|---|---|---|
| Stable symlinks by USB/serial/disk ID or path | Strong | Everyday ATTRS{idVendor} + SYMLINK+= patterns |
Must consume vendor/distro .rules as shipped | Strong | Not compatible with mdev.conf syntax |
udevadm test / monitor / control --reload-rules | Strong | Debug/reload contract lives on the udev side |
| systemd image with device units / Wants integration | Strong | systemd-udevd + property/tag model |
Short foreground PROGRAM/RUN helpers | OK (careful) | Docs: not for long-running programs |
| Only create nodes and tweak modes | Overkill | mdev often enough |
Bottom line: udev’s value is not only “make a node,” but reading the attribute graph and pinning stable names, modes, and follow-ups in rules. If that rule set is product contract, ship udev even on a tight flash budget.
Rule migration caveats?
One-line answer: No 1:1 mechanical translation. mdev.conf and udev .rules differ in match model, evaluation order, and helper environment. Re-state the intent (owner/mode, name, when to run a helper).
| Axis | BusyBox mdev | udev / systemd-udevd |
|---|---|---|
| Config | optional /etc/mdev.conf | many *.rules dirs, lexical order |
| Stop condition | first match (continue if leading -) | multiple rules apply (=, +=, :=) |
| Keys | name regex, @maj,min, $ENV= | KERNEL / SUBSYSTEM / ATTR / ATTRS / ENV / PROGRAM… |
| Renames | =path / >path | NAME, SYMLINK+= |
| Helpers | @/$/* → system() shell | RUN{program}; short foreground; prefer absolute paths |
| Event path | /proc/sys/kernel/hotplug → mdev | daemon receives netlink uevents |
| Ordering | optional /dev/mdev.seq SEQNUM serialize | daemon queue + rule engine (different model) |
Migration checklist (doc-aligned sketch):
1. State intent in one line: owner/mode? stable name? helper on add?
2. If the udev rule needs ATTRS/parents/ID_* → do not invent a “similar” mdev regex
3. If staying on mdev: redesign regex + first-match / leading-
4. Helpers: mdev needs /bin/sh and stdio=/dev/null; udev RUN forbids long jobs
5. Do not enable both hotplug helper and udevd on the same events
6. Smoke real add/remove on the target (no host-only verification)
Warning: Pasting a
.rulesfile intomdev.confdoes not work. Likewise, do not treat a one-line mdev@modprobe "$MODALIAS"as equivalent to udev’s richer module-load and property rules.
FAQ
Does mdev -s alone cover hotplug?
No. mdev -s seeds nodes for devices already present (via /sys). Later add/remove needs mdev registered as the hotplug helper (echo /sbin/mdev > /proc/sys/kernel/hotplug, etc.—documented sketch).
Does firmware loading work with mdev?
BusyBox docs say place firmware under /lib/firmware/; the kernel requests a filename and mdev loads it. Exact names are hardcoded in the kernel/driver—check those docs.
If we run systemd, are devices “automatic” without udev?
Device management is usually systemd-udevd plus rules. “We use systemd” is not the same design decision as “our udev rule contract.”
Can both be registered as hotplug helpers?
Not recommended. Kernel hotplug helper and udevd handling the same events can fight over nodes and modes. Pick one device manager per image.
What should you remember?
Small root + simple nodes/modes → mdev. Attributes, persistent names, distro rules, udevadm → udev. Rule syntax does not port. Migrate intent and smoke-test on the target. No invented board footprints or affiliates.
Where are the official sources?
- BusyBox docs/mdev.txt — seed/hotplug, mdev.conf, firmware, SEQNUM
- BusyBox examples/mdev.conf — sample syntax and permissions
- udev(7) — rule keys, directories,
PROGRAM/RUNlimits - systemd-udevd.service — modern daemon entry point