how does open works for normal file and device dri

2019-01-24 16:56发布

Currently, I am learning Linux device drivers. And got stuck over how opening a device file works ?

What I got until now... Consider the a simple code that opens a normal file..

#incldue<stdio.h>
int main() {
   FILE fp;
   char buffer[20];
   fp = fopen(/home/yoggi/foo.txt, "r");
   fread(buffer, 5, 1, fp);
}

In above program, The fopen(), c-library function, is a wrapper function to the system call open(), which intern calls sys_open() or file_open() in VFS layer function. As linux supports a number of file system, virtual file system then transfer the control to actual file system handler to the opening that file.

1) How does virtual file system(VFS) get to know on which file system the 
   underline file resides?
2) How does it then calls the file_open or open function of that particular
   filesystem to open file.

In case of device drivers similar things happens. Suppose a simple device driver.

#include <linux/module.h>
// othher includes... 
static dev_t first; // Global variable for the first device number 
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class
static int my_open(struct inode *i, struct file *f)
{
   printk(KERN_INFO "Driver: open()\n");
   return 0;
} 
static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
   printk(KERN_INFO "Driver: read()\n");
   return 0;
}
struct file_operations pugs_fops =
{
 .owner = THIS_MODULE,
 .open = my_open,
 .read = my_read,
};

static int __init ofcd_init(void) /* Constructor */
{
  printk(KERN_INFO "Namaskar: ofcd registered");
  if (alloc_chrdev_region(&first, 0, 1, "Shweta") < 0)
  {
    return -1;
  }
  if ((cl = class_create(THIS_MODULE, "chardrv")) == NULL)
  {
     unregister_chrdev_region(first, 1);
     return -1;
 }
 if (device_create(cl, NULL, first, NULL, "mynull") == NULL)
 {
    class_destroy(cl);
    unregister_chrdev_region(first, 1);
    return -1;
 }
   cdev_init(&c_dev, &pugs_fops);
 if (cdev_add(&c_dev, first, 1) == -1)
 {
   device_destroy(cl, first);
   class_destroy(cl);
   unregister_chrdev_region(first, 1);
   return -1;
 }
  return 0;
}

static void __exit ofcd_exit(void) /* Destructor */
{
 cdev_del(&c_dev);
 device_destroy(cl, first);
 class_destroy(cl);
 unregister_chrdev_region(first, 1);
 printk(KERN_INFO "Alvida: ofcd unregistered");
} 
module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Our First Character Driver");

Firstly we allocate major minor numbers for the device. Register for the range of device files and Linking the device file operations to the device driver functions.

Some of the term I didn't get are..

1) What does actually cdev_add() do? in terms of registering a device to the 
   kernel.
2) Registering a device to the kernel means?
3) How does a open(/dev/mynull, O_RONLY); called on a device file actually calls 
   the open function of driver which is mapped while initializing the device 
   by calling routine cdev_init(&c_dev, &pugs_fops); ?

1条回答
迷人小祖宗
2楼-- · 2019-01-24 17:18

1) How does virtual file system(VFS) get to know on which file system the underline file resides?

You have to specify the file you're trying to open by its full pathname (or the current working directory).
So by traversing this directory path backwards, the first match (the deepest path) to a mount point will provide the mounted filesystem, type of filesystem and the device.

Each filesystem provides this information when it is mounted and is saved in the mount tables.
You can view this (current state) information using the mount command.

2) How does it then calls the file_open or open function of that particular filesystem to open file.

Once the filesystem is known, the ops structure for that fs is retrieved, and the open() entrypoint can be called.

1) What does actually cdev_add() do? in terms of registering a device to the kernel.

Driver registration (e.g cdev_init() for a char device) installs the driver's ops structure which lists the entrypoints of functions that the driver can perform.
cdev_add() notifies the kernel that the driver can control a specific instance of that char device type. That device instance is assigned a minor number that associates the device name in /dev to state information in the driver.
Note that device types other than char (such a network or platform (bus) devices) belong to a different subsystem, and use different registration procedures.

2) Registering a device to the kernel means?

Access to that device is now enabled.

3) How does a open(/dev/mynull, O_RONLY); called on a device file actually calls the open function of driver which is mapped while initializing the device by calling routine cdev_init(&c_dev, &pugs_fops); ?

The driver's init() routine should only be called once when the driver is loaded. This routine should probe for the existence and operational state of all instances of the device. Resources such as interrupt lines, DMA channels and I/O port and/or memory space should be acquired. The driver registers its ops structure with kernel. The driver registers each instance of the device with kernel.

The open() call in userspace is handled by the C library. The /dev device name is translated to the device major number (which identifies which device subsystem or class, e.g tty or audio, has to process the request) and the minor number (which identifies which device driver to use and which instance of the device is accessed). The processor is switched to supervisor mode so that the kernel driver's open() routine can be called, which is retrieved from the driver's ops structure. For a bit more on the ops structure see this other answer.

查看更多
登录 后发表回答