📌 Introduction
In the Linux kernel, every character device is represented using the struct cdev structure.
📄 Structure Definition
Located in the header file: linux/cdev.h
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
} __randomize_layout;🛠 Key Functions
-
cdev_init()
- Purpose: Initializes a
cdevstructure. - Prototype:
void cdev_init(struct cdev *, const struct file_operations *);
- Purpose: Initializes a
-
cdev_alloc()
- Purpose: Allocates memory and returns a
cdevstructure. - Prototype:
struct cdev *cdev_alloc(void);
- Purpose: Allocates memory and returns a
-
cdev_add()
- Purpose: Adds a character device to the system.
- Prototype:
int cdev_add(struct cdev *, dev_t, unsigned int minor_count);
-
cdev_del()
- Purpose: Removes a
cdevstructure from the system. - Prototype:
void cdev_del(struct cdev *dev);
- Purpose: Removes a
🔍 Deep Dive: cdev_init vs cdev_alloc
cdev_alloc() dynamically allocates memory for a new cdev structure and returns a pointer to it.
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}On the other hand, cdev_init() initializes an already allocated cdev structure.
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}Usage example:
struct cdev *my_dev = cdev_alloc();
if (my_dev != NULL) {
my_dev->ops = &my_fops; // The file_operations structure
my_dev->owner = THIS_MODULE;
}
cdev_init(struct cdev *cdev, const struct file_operations *fops);💡 Note: It's essential to initialize the
ownerfield of the structure toTHIS_MODULE. This acts as a safeguard, ensuring that module unloads aren't performed improperly when the device is active.
-
Q: What is the primary purpose of
struct cdevin the Linux kernel?A: The
struct cdevis vital in the Linux kernel for representing character devices. It serves as a bridge between user-level operations and the corresponding driver-implemented functions for a character device.
-
Q: How does
cdev_initdiffer fromcdev_alloc?A: The
cdev_initfunction initializes an already allocatedcdevstructure, whereascdev_allocdynamically allocates memory for a newcdevstructure and initializes it.
-
Q: Why is it crucial to set the
ownerfield ofstruct cdevtoTHIS_MODULE?A: Initializing the
ownerfield toTHIS_MODULEensures that the module is protected against inappropriate module unloads while the device is still active. It ties the device's lifecycle to the module's lifecycle.
📚 Understanding the Linux Character Device Driver 📚
🔍 Code Flow
-
Initialization (test_hello_init)
- Register a device number with
alloc_chrdev_region. - If registration succeeds, create a device class and device within that class.
- Allocate a character device structure (
cdev). - Add the character device to the system with the previously registered device number.
- Register a device number with
-
Cleanup (test_hello_exit)
- Destroy the created device and class.
- Remove the character device from the system.
- Unregister the device number.
🔧 Built-in APIs
alloc_chrdev_region: Dynamically allocates a major number (or both major and minor) for the device.class_create: Creates a struct class pointer that can be used in calls todevice_create.device_create: Creates a device and registers it with sysfs.cdev_alloc: Allocates a character device structure.cdev_add: Adds the device to the system, making it live.device_destroy: Unregisters and cleans up a device that was created with a call to device_create.class_destroy: Destroys a struct class structure.cdev_del: Removes a character device from the system.unregister_chrdev_region: Releases the major and minor numbers.
📝 Interview Questions and Answers
Technical:
Q1. What is the purpose of the dev_t data type in a character device driver?
A1.
dev_tis a data type in the Linux kernel that encodes major and minor device numbers. It uniquely identifies a device in the system.
Q2. Why do we need both a class and a device within that class?
A2. In the device model, a
classrepresents a higher-level view, like a category of devices (e.g., "input" or "sound"). Adevicerepresents individual devices within that category. This helps in structuring device representations in the sysfs.
Q3. What is the difference between cdev_init and cdev_alloc?
A3.
cdev_initinitializes an already allocatedcdevstructure, whereascdev_allocallocates a newcdevstructure and initializes it.
Design:
Q1. How would you modify this driver to handle multiple devices?
A1. To handle multiple devices, one can increase the
countvariable to the desired number. You'd then need to manage each device's operations appropriately in the driver's callback functions (open, read, write, etc.).
Q2. How can you ensure data integrity when multiple processes access the character device simultaneously?
A2. Data integrity can be ensured using locking mechanisms such as mutexes or spinlocks in the driver code. This ensures that only one process can access critical sections of the driver at a time.
KERNEL[54993.850630] add /class/myclass (class)
KERNEL[54993.851804] add /devices/virtual/myclass/mydevice (myclass)
KERNEL[54993.851891] add /module/hello (module)
UDEV [54993.864593] add /class/myclass (class)
UDEV [54993.884469] add /devices/virtual/myclass/mydevice (myclass)
UDEV [54993.914772] add /module/hello (module)