The mysteries of write

Source: Internet
Author: User

By Coly (LI Yong)

Source: http://www.linuxforum.net/doc/write-coly.html

Abstract: A simple character device driver is introduced, and the working principle of the write function is deeply analyzed.

In Linux, we use the write function when using a device. Through this function, we can transmit data to the device as if using a file. But why can I write data to a device using the write function? How can this process be implemented? This mystery lies in the write Implementation of the device driver. Here I use some source code to explain how to make a simple write function complete the complex process of writing data to the device. The source code here mainly comes from two places. The first is the Linux Device Driver instance published by oreilly, and the second is the core source code of Linux Kernel 2.2.14. I only list the relevant content. If you are interested, you can also check other source code. However, I am not explaining how to write a device driver, so I will not explain every detail. Besides, I feel that I have not thoroughly understood it. Since the examples in Linux device driver are still complicated for us, I simplified one of them. The driver supports an array of 10 kbuf [10] in the core space. We can open it through the user program, read it, write it, close it. The name of this device is called short_t. Now let's get down to the truth. For a device, it can have a corresponding logical device node under/dev. This node exists as a file, but it is not a file in the general sense. It is a device file, more specifically, it is a device node. This node is created using the mknod command, which specifies the primary device number and secondary device number. The primary device number indicates a certain type of backup, which generally corresponds to a specific driver. The secondary device number is generally distinguished by indicating different properties, such as different usage methods and locations, different operations. This device number is obtained from the/proc/devices file. Therefore, the device node is in the directory only when the driver is in the kernel. The main function of this device number (especially the main device number) is to declare the driver used by the device. Drivers correspond to device numbers one by one. When you open a device file, the operating system knows which driver is corresponding to the device. This "know" process will be explained later. Let's talk about the basic structure of the driver. Here I will only introduce the dynamic module Driver (that is, the one we use to load to the core using insmod and uninstall using rmmod), because I am only familiar with this structure. The modular driver is fixed by two functions: int init_module (void) and void cleanup_module (void ). The former is executed during insmod and the latter is executed during rmmod. During execution, init_nodule initializes some drivers. The most important tasks include registering a device, applying for an I/O port address range, and applying for an IRQ interrupt. Here, only the registered devices are related to what we want to know. The following is a typical init_module function: int init_module (void) {int result = check_region (short_base, 1);/* view the port address */...... Request_region (short_base, 1, "short");/* apply for a port address */...... Result = register_chrdev (short_major, "short", & short_fops);/* register the device */...... Result = request_irq (short_irq, short_interrupt, sa_interrupt, "short", null);/* apply for IRQ */...... Return 0;}/* init_module */I only keep the most important part of the above function. The most important function is result = register_chrdev (short_major, "short ", & short_fops); this is the essence of a driver !! When you execute the indmod command, this function can complete three major events: first, apply for the master device number (short_major), or specify, or dynamically allocate; second, register the backup name ("short") in the kernel. Third, specify the FoPs method (& short_fops ). The specified fops method is the method for operating the device (for example, read, write, seek, Dir, open, and release). How can we implement these methods, is the workload of writing a device driver. Now we need to get into the key part-how to implement the FoPs method. We all know that each file has a file structure, in which there is a file_operations struct that specifies operations that can be performed on the file. The following is a typical file_operations structure: struct file_operations {loff_t (* llseek) (struct file *, loff_t, INT); ssize_t (* read) (struct file *, char *, size_t, loff_t *); ssize_t (* write) (struct file *, const char *, size_t, loff_t *); int (* readdir) (struct file *, void *, filldir_t); unsigned int (* poll) (struct file *, struct poll_table_struct *); int (* IOCTL) (struct inode *, struct file *, unsigned int, Unsigned long); int (* MMAP) (struct file *, struct vm_area_struct *); int (* open) (struct inode *, struct file *); int (* flush) (struct file *); int (* release) (struct inode *, struct file *); int (* fsync) (struct file *, struct dentry *); INT (* fasync) (INT, struct file *, INT); int (* check_media_change) (kdev_t Dev); int (* revalidate) (kdev_t Dev); int (* Lock) (struct file *, Int, struct file_lo CK *) ;}; we can see that it is actually a function pointer for many file operations, where there is write, and we will not care about it. This write pointer will appear in the actual driver with the name of the function implemented by the programmer. It refers to the device write operation function implemented by the programmer. The following is an actual example. This write function can input a string into an array of core memory. Int short_write (struct inode * inode, struct file * filp, const char * Buf, int count) {int retval = count; extern unsigned char kbuf [10]; if (count> 10) Count = 10; copy_from_user (kbuf, Buf, count); Return retval;}/* short_write */The FoPs method corresponding to the device short_t is declared as follows: struct file_operations short_fops = {null,/* short_lseek */short_read, short_write, null,/* short_readdir */null,/* short_poll */null,/* s Hort_ioctl */null,/* short_mmap */short_open, short_release, null,/* short_fsync */null,/* short_fasync * // * nothing more, fill with nulls */}; the null project does not provide this function. Therefore, we can see that the short_t device only provides the read, write, open, and release functions. The Write function has been implemented above. The specific implementation function is named short_write. These functions are the functions that really operate on the device. This is a major benefit of the driver: no matter how complicated the implementation is, but for users, is the common file operation functions. However, we can see that the write function in the driver has four parameters. The function format is as follows: short_write (struct inode * inode, struct file * filp, const char * Buf, int count) the Write function in the user program has only three parameters. The function format is as follows: Write (inf fd, char * Buf, int count). How are the two of them associated? This depends on the function sys_write in the core of the operating system. The source code in sys_write in Linux Kernel 2.2.14 is asmlinkage ssize_t sys_write (unsigned int FD, const char * Buf, size_t count) {ssize_t ret; struct file * file; struct inode * inode; ssize_t (* write) (struct file *, const char *, size_t, loff_t *); /* pointer to the wirte function in the driver */lock_kernel (); ret =-ebadf; file = fget (FD ); /* get the file pointer through the file descriptor */If (! File) goto bad_file; If (! (File-> f_mode & fmode_write) goto out; inode = file-> f_dentry-> d_inode;/* Get inode information */ret = locks_verify_area (flock_verify_write, inode, file, file-> f_pos, count); If (RET) goto out; ret =-einval; If (! File-> f_op |! (Write = file-> f_op-> write)/* points the write function pointer declared at the beginning of the function to the corresponding write function in the FoPs Method */goto out; down (& inode-> I _sem); ret = write (file, Buf, Count, & file-> f_pos);/* use the write function in the driver to input data to the device, note: Here are four parameters */up (& inode-> I _sem); Out: fput (File); bad_file: unlock_kernel (); return ret ;} I wrote a simple program to test this driver. The source code of this program is excerpted as follows (I have saved it in the province): Main () {int FD, Count = 0; unsigned char Buf [10]; FD = open ("/dev/short_t", o_rdwr); printf ("in Put string: "); scanf (" % s ", Buf); Count = strlen (BUF); If (count> 10) Count = 10; Count = write (FD, buf, count); close (FD); return 1;} Now we will demonstrate how the user writes data to the device using the write function: 1, insmod driver. The driver requests the device name and master device number, which can be obtained in/proc/devieces. 2. Obtain the master device number from/proc/devices and run the mknod command to create a device node file. This is to associate the device node file with the device driver through the master device number. The file attribute in the device node file specifies the function pointer implemented by the FoPs method in the driver. 3. the user program uses open to open the device node file. When the operating system kernel knows that the driver is working, it calls the OPEN function in the FoPs Method for relevant work. Generally, the open method returns a file identifier, which does not directly operate on it. Instead, the system calls of the operating system work behind the scenes. 4. When you use the write function to operate a device file, the operating system calls the sys_write function. This function first obtains the inode pointer and flip pointer corresponding to the device node file through the file identifier. The inode pointer contains device information, which tells the operating system which device driver should be used, and the flip pointer contains fops information, which can tell the operating system that the corresponding fops method functions can be found there. 5. Then, sys_write will call the write method in the driver to write the device. Among them, 1-3 is performed in the user space, and 4-5 is performed in the core space. The user's write function is associated with the OS's write function by calling sys_write. Note: For Block devices, the writing mode still exists, which should be solved by the gnu c library. We will not discuss it here, because I have not read the source code of the GNU C library. In addition, this is a beta version article. Please give your comments and suggestions. Thank you very much!
     
 
 
All rights reserved for the China Linux Forum

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.