the kernel use sysfs to export device nodes to user space to be used by udev
No. Sysfs does not contain device nodes. Sysfs mainly contains files that provide information about devices, as well as some files that allow processes to control how devices operate. But for the most part devices cannot be used through what sysfs provides.
Let's take a hard disk, for example. There's a directory for it somewhere under
/sys/devices, with a path that depends on how it's connected to the computer (e.g.
/sys/devices/pci0000:00/… for a disk connected to a controller that's connected to the computer's primary PCI bus). In that directory, you can find various information such as its size, whether it's removable, its power state, etc. There are subdirectories for partitions as well. But there's nothing in there that provides access to the content of the disk. Elsewhere in
/sys, there are symbolic links to the directory corresponding to that disk: in
/sys/class/block, etc. But still nothing to access the content of the disk.
/dev, the entry for the disk is a special file — a block device. This file allows processes to read and write the content of the disk. (Though for a disk that usually doesn't happen; instead the content of the disk — or of a partition — is mounted, so the kernel is accessing the device, processes don't.)
Device files allow some operations other than reading and writing content to be made through ioctl. It would be possible to provide all the information and control interfaces that sysfs provides through ioctl on the device file. However this would be less convenient for several reasons:
- With separate files in
/sys, permissions can be set on a fine-grained basis. With a single file per device in
/dev, it's all or nothing.
- Separate files can be read and written easily by applications. You can just use
echo. With ioctl, it's a lot harder: there's no shell interface, often no interface in other high-level languages.
- With ioctl, the command has to be encoded in a number rather than a name, and the format of the arguments has to be defined at a binary level. Using names and simple text formats makes it easier to write software.
Going in the other direction, it would be possible to provide access to the device content via a file in sysfs. But this would require extra work in the kernel: sysfs is designed primarily to serve small files and symbolic links, and without the
ioctl support that existing applications expect. I don't think there would be a significant benefit to expanding sysfs to support existing device types, hence the continued existence of device files.
Sysfs is automatically populated by the kernel, reflecting the actually available devices in real time. The meaning of a file in sysfs comes from its path, which is chosen by the driver that provides that file.
/dev works differently: the meaning of a file comes from the device file's type (block or character) and its major and minor numbers (that's what
ls -l lists instead of the file size for a device). Traditionally,
/dev was static, with device files created during system installation; but that doesn't work so well when devices can be hotplugged, hence the wish for a dynamic
/dev that reflects connected devices in real time.
Linux has gone through several iterations of a dynamic
/dev. Linux 2.4 has devfs, where the kernel automatically created entries to reflect connected devices. But that was not so nice, because it hard-coded device naming and permission policies into the kernel, so it was replaced by the userland program udev to manage policy, and
/dev on a simple tmpfs filesystem (an in-memory filesystem with no special meaning to the kernel). And then devfs made a partial comeback, with devtmpfs, which is an instance of tmpfs where entries for available devices are automatically created by the kernel, but udev does all the management it wants on top of that.