Character device 2026-11-05
A character device is a device interface accessed through a character-special device node. In the Linux model, the node's file metadata identifies character-device type and a represented device number; the device driver defines the supported read, write and control operations. The word character does not restrict the exchanged data to readable text.[ref]
A useful concrete example is /dev/null. Data written to it are discarded, and a read returns zero bytes to indicate end of file. /dev/zero also discards writes but supplies zero-valued bytes on reads.[ref] These two interfaces have different behaviours even though both use character-special files.
For an original byte-level trace, suppose an application successfully writes the three bytes 41 00 42 to /dev/null. A later read does not return those bytes: the interface is a data sink. A read requesting three bytes from /dev/zero, if completed without interruption, instead supplies 00 00 00. The embedded zero byte in the write and zero bytes in the read are ordinary binary values, not restrictions to a text alphabet.
The familiar file descriptor operations provide a common access framework, but their meanings depend on the device. A video device node can offer format negotiation and buffer controls through Video4Linux; another character device may expose a stream. Character-device type alone does not promise ordinary-file storage, a particular message boundary or a useful seek operation.
The distinction from a block device concerns the interface category. A device node is the named file system object, while the character device is the interface it represents. Several nodes can identify the same represented interface, and a pathname's inode identity does not by itself identify a distinct physical peripheral.
Character-device lifetime after deletion is the interval during which a removed character device still needs valid backing state because existing users can invoke its operations. Linux cdev_del removes the device from new-open lookup; it does not invalidate already-open file descriptors. The kernel explicitly permits their file-operation callbacks after deletion returns.[ref]
Consider an original event sequence. Process A opens an interface and retains a pointer to driver state. Removal calls cdev_del; process B can no longer obtain a new open through that registration. Process A then calls read. Freeing the state immediately after deletion would make that callback access dead storage: a use-after-free. The lookup lifetime and the backing allocation lifetime are different.
A useful ownership model gives the allocation one registration reference and one reference for each successful open. In a simplified reference counting design with opens, the reference count is . Removal drops the registration reference; each final close drops its open reference. With two opens, the sequence is , and storage is reclaimed only at zero. This is a model of ownership, not a complete driver implementation: taking references must itself be synchronized with removal, and asynchronous work can require additional references.
The character device may be disconnected from its hardware before its storage is reclaimed. Existing operations can observe a synchronized disconnected state and return an appropriate error while the allocation remains valid. Reference protection alone does not make removed hardware usable or serialize simultaneous operations.
For combined device objects, cdev_device_add and cdev_device_del have their own documented pairing and parent-reference rules. The add helper can expose callbacks even when it reports failure; the delete helper distinguishes stopped sysfs callbacks from still-callable operations on existing opens.[ref] Therefore unwinding a failed character-device registration must follow the actual publication and ownership state. Releasing the device number reservation is separate from proving that every user of private storage has finished.
Device node 2026-11-05
A device node is a special file system object through which a program accesses a device interface. In the Linux model, its inode records a device-special file type and a device number; the path supplies a name in the file-system namespace. Opening it supplies a file descriptor through which the applicable device driver handles operations. Device-special objects differ from ordinary files containing a stored byte sequence.[ref]
Two device identifiers must be distinguished. The metadata field st_dev identifies the device containing the file system that holds the inode. For a character device or block device, st_rdev instead identifies the represented device. Its major and minor numbers belong to that represented-device identification. The inode's own number is a third identifier.[ref]
For an original metadata example, stipulate that names /dev/example-a and /dev/example-b reach different inodes numbered 42 and 43 in one file system. Both inodes have character-device type and represented-device numbers (240,7). They name the same device interface in this example despite being different named file-system objects. Different permissions on the nodes can still affect access. Their inode numbers alone do not identify two physical devices.
A character device presents a driver-defined stream or control interface; it need not contain human-readable characters. A block device presents block-oriented storage access. The special-file type is part of the identification, so equal numerical major/minor pairs do not alone equate character and block interfaces. A video device node is a concrete Video4Linux example, whose supported functions must still be queried.
Creating a device-special file supplies a namespace entry and device identification; it does not manufacture hardware, load a guaranteed working driver or establish a supported data format. Conversely, a symbolic link pointing to a device node is an ordinary naming indirection: its target's special-file metadata determines the accessed interface.
Dynamic character-device number allocation asks the Linux kernel to reserve a range of character-device numbers while choosing its major device number at runtime. The API is alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name). It returns zero on success or a negative error code; the first assigned combined device number is written through dev. The return status is not the allocated major number.[ref]
For an original request, suppose alloc_chrdev_region(&first, 5, 3, "sample") succeeds and the returned first has major . Its reserved identifiers are , and , not . If one illustrative successful run returned major 243, the caller would use 243; another allocation can return a different major. This is a hypothetical range example, not a kernel operation executed here.
Keep first and the successful reservation count for cleanup. If a later initialization step fails, unregister_chrdev_region(first, 3) releases this reservation; it must not be treated as ownership of every minor under the returned major. Unwinding a failed character-device registration releases only resources actually acquired.
Number reservation is separate from associating file operations with a live character-device object and from creating a device node. The related cdev_add operation makes a registered object live; its name or bookkeeping label does not itself establish a pathname under /dev.[ref]
Removal must also respect existing users. cdev_del prevents new opens, but already open file descriptors can still call device operations, so deleting the registration alone does not prove that backing state can be freed immediately.[ref] A dynamic number is a reserved interface identifier, not a persistent hardware identity or an inode number.
Linux device-number allocation assigns or reserves the numerical identifiers through which device-special interfaces are registered. The relevant identity includes device type together with major device number and minor device number. It is distinct from naming a device node, assigning its inode number, or obtaining a process-local file descriptor.
The kernel's allocated-device list records established assignments. For example, character major 1 includes minor 3 for /dev/null and minor 5 for /dev/zero. The same integer major 1 has a separate block-device allocation for RAM disks. These entries concern different interface namespaces, not interchangeable interpretations of a single untyped integer.[ref]
For character devices, the kernel API separates requesting a particular range with register_chrdev_region from dynamic character-device number allocation with alloc_chrdev_region. The latter receives a first minor and count, chooses the major dynamically, and returns the first assigned combined device number. The caller must check whether reservation succeeded.[ref]
An original allocation example requests four minor values starting at 20. If the returned major is , the reserved range in this example is
It does not contain . Code should use the returned rather than guess a major from an earlier machine or boot. A numerical allocation can serve software interfaces as well as physical hardware, so four minors do not necessarily imply four physical peripherals.
Reserving numbers is only one step. The character-device object must also be associated with its operations and added to the kernel; cdev_add makes it live. Separately, userspace needs an appropriate device node or other naming arrangement. The registration name used for bookkeeping is not itself a pathname under /dev.[ref]
This separation explains a common diagnostic trap. Creating a node containing a chosen major/minor pair does not reserve that range or supply a working device driver. Conversely, allocating a range does not make every invented pathname reach it. Inspect the actual registered interface, file type and stored device number together when identifying a device.
The allocation is also not a persistent hardware serial number. Dynamic assignment can differ between configurations or registration histories. Releasing a number range with unregister_chrdev_region returns the reservation; a complete driver teardown must also handle the device object's lifetime and existing users. The kernel documentation notes that deleting a character-device object prevents new opens while operations on already open descriptors can remain possible.[ref]
Major device number 2026-11-05
A major device number is the first component of a device number. In Linux interfaces it identifies a device class or registered device-number allocation; the minor device number distinguishes interfaces within that context. major(d) extracts the major component from the combined dev_t value.[ref]
The device type is also significant. The Linux registry assigns character major1 to memory-related interfaces, including /dev/null, while block major1 describes RAM disks. Reusing the integer1 across these two types does not equate their behavior.[ref]
An original metadata example makes the components explicit. Suppose a character-special node represents . Renaming that node within the file system does not by itself change its stored pair. A second node can represent the same pair, while a node with pair has a different major context despite matching minor12.
Linux device-number allocation can be dynamic, so a major number should not automatically be treated as a permanent hardware serial number or a universally fixed driver label.[ref] Its role is part of the device interface and registration scheme.
The structured conversion satisfies
for supported values. An inode number identifies a file-system object, while the represented device's major/minor pair answers a different question. The containing file system's own device identity is another distinct field.[ref]
Minor device number 2026-11-05
A minor device number is the second component of a device number. In the Linux interface, minor(d) extracts it from a value of type dev_t; the matching major device number supplies its device-class context.[ref]
A concrete Linux allocation distinguishes two character-special interfaces sharing major1: /dev/null has minor3, while /dev/zero has minor5.[ref] The former supplies end-of-file when read, while the latter supplies zero-valued bytes. Their different minor values select different interface behavior within the same major allocation.[ref]
For an original pair comparison, let two registered character-device interfaces have numbers and . Both have minor7, but their complete device numbers differ. Conversely, and share a major while selecting different minor values within it. A minor value alone is therefore insufficient to identify the interface.
Minor numbers are interpreted in the relevant device allocation; there is no universal rule that every minor counts disks, partitions or physical pieces of hardware. The device may also be a purely virtual interface. The conversion functions preserve the structured components:
for supported component values. Extracting the component with the documented interface avoids assuming an implementation-specific bit packing.
Unwinding a failed character-device registration releases resources acquired before an initialization failure while preserving the lifetime of anything that concurrent users can still access. In Linux, reserving device numbers, publishing the character-device object and creating userspace-visible names are distinct steps. The cleanup path must know which steps actually succeeded.
Consider an original abstract initialization sequence: reserve a number range, initialize a character-device object, publish it, then finish a later setup step. The following table tracks successful acquisitions; it is not a complete driver implementation.
State reachedRange reserved?Character device live?Required response to a later failure
Before reservation succeedsNoNoDo not release a range that was not acquired.
Reservation succeeded, publication not reachedYesNoRelease that reserved range and other private resources already acquired.
Publication succeededYesYesStop new access, unwind the reservation, and preserve storage needed by existing users.
alloc_chrdev_region reserves a range and reports failure through its return value. cdev_init initializes the object with its operations; cdev_add publishes it and makes it live immediately. unregister_chrdev_region releases a successful number reservation. Pair each release with the acquisition that actually succeeded, using the same first number and count.[ref]
For a concrete bookkeeping example, suppose a successful reservation contains four interfaces starting at returned pair . Failure before publication requires releasing that four-number range. Releasing only three leaves part of the reservation behind; releasing a guessed starting major or an unacquired range is a different error. The returned major from dynamic character-device number allocation must be retained rather than reconstructed from a pathname.
Publication changes the lifetime problem. cdev_del prevents new opens, but existing open file descriptors can still invoke their file operations. Therefore removing the device from lookup is not a proof that its backing state can immediately be freed. Character-device lifetime after deletion must account for the references and callbacks already in progress.[ref]
The combined cdev_device_add helper has an especially important failure contract: callers must assume userspace could open the device and call operations even if the helper returns failure. A blanket error handler that immediately frees all backing state would violate that contract.[ref] Design ownership and reference handling around the documented publication boundary, then release each owned resource once. The general lesson is that reverse acquisition order must be combined with concurrency-aware lifetime management.