Hasen Linux device Driver Development learning Journey-asynchronous I/O

Source: Internet
Author: User
Tags posix

/** * Author:hasen * Reference: Linux device Driver Development Details * Introduction: Android Small rookie Linux *          device Driver Development Learning Journey * Topic: asynchronous I/O * date:2014-11-11 */

The most commonly used input/output (I/O) model in Linux is synchronous I/O. In this model, when the request is sent, the application blocks, knowing that the request satisfies

So far. However, in some cases, I/O requests may need to overlap with other processes. Portable Operating System Interface (POSIX) asynchronous I/O (AIO)
This functionality is provided by the application Interface (API).
The
basic idea of AIO is to allow a process to initiate many I/O operations without blocking or waiting for any operation to complete. Later or when I am receiving I/O
When the notification is completed, the process retrieves the results of the I/O operation.
In
asynchronous nonblocking I/O, each transport operation has a unique context, with the AIOCB (AIO I/O Control Block) structure
Contains all the information about the transfer, as well as the user buffers that are prepared for the data. When an I/O notification (called completion) is generated, the structure is uniquely labeled
Completion of the I/O operations.

The AIO Series API is included in the GNU C library function, which is required by posix.1b, mainly with the following functions:
 (1) aio_read int Aio_read (struct AIOCB *AIOCBP);/* Asynchronous operation of a valid file descriptor * * The file descriptor can represent a file, a socket, a pipeline, a aio_read () returns immediately after the request is queued, the execution returns 0 successfully, Error returned-1, and set the value of errno. (2) Aio_write int aio_write (struct AIOCB *AIOCBP);/* Used to request an asynchronous write operation */Aio_write () function returns immediately, indicating that the request has been queued, successfully returned 0, failed to return 1, and accordingly Set the errno.        (3) aio_erroraio_error (struct AIOCB *AIOCBP);/* Used to determine the status of the request * * This function can return the following content.        Einprogress: The description request has not been completed.        Ecancelled: Description The request was canceled by the application. -1: Indicates that an error has occurred and the specific error reason is logged by errno. (4) Another difference between aio_return/* asynchronous I/O and standard I/O is that the return state of the function cannot be accessed immediately because asynchronous I/O is not blocked on the read () call. In a standard read () call, the return state is provided when the function returns. However, in asynchronous I/O, use the Aio_return () function */ssize_t aio_return (struct AIOCB *aiocbp), and only after the Aio_error () call determines that the request has been completed (possibly successful, or an error has occurred), To call this function, the return value of the Aio_return () function is equivalent to the return value of Read () and write () in the synchronization condition, the number of bytes returned successfully, and the negative value returned by the error. 

        The following code example gives the user space for an asynchronous read operation, which first , then prepare the AIOCB struct, then call Aio_read (&MY_AIOCB) to make the asynchronous read request , When Aio_error (&MY_AIOCB) ==einprogress, the operation is still in progress, always wait , after the end through Aio_ Return (&MY_AIOCB) get rebate .

#include <aio.h>...int fd, ret, struct AIOCB my_aiocb, fd = open ("file.txt", o_rdonly), if (fd<0) perror ("open"); * Clear 0 AIOCB structure */bzero ((char *) &my_aiocb,sizeof (struct AIOCB));/* Allocates a data buffer for AIOCB struct request */my_aiocb.aio_buf = malloc ( BUFSIZE + 1), if (!my_aiocb.aio_buf) perror ("malloc");/* Initialize AIOCB member */my_aiocb.aio_fildes = FD; my_aiocb.aio_nbytes = BUFSIZE; my_aiocb.aio_offset = 0; ret = aio_read (&MY_AIOCB), if (Ret < 0) perror ("Aio_read"); while (Aio_error (& MY_AIOCB) = = einprogress) continue; if (ret = Aio_return (&MY_AIOCB)) > 0) {/* Gets the return value of asynchronous read */}else{/* read failed, parsing errno*/}

(5) Aio_suspend The user can use the Aio_suspend () function to suspend (or block) the calling process until the asynchronous request completes, generating a signal, or another timeout operation. The caller provides a list of AIOCB references, any one of which will cause aio_suspend () to return. int aio_suspend (const struct AIOCB cblist[], int n,const struct timespec *timeout); Note that the second parameter of Aio_suspend is the number of elements in Cblist, and is not the number of AIOCB references. Any null element in the cblist is ignored by Aio_suspend. If a timeout is provided for Aio_suspend, and the time-out does occur, it returns -1,errno that contains Eagain.
Example: User space asynchronous I/O Aio_suspend () function uses
struct aioct *cblist[max_list]/* 0 aioct struct chain list */bzero ((char *) cblist,sizeof (cblist));/* Place one or more AIOCB into the AIOCT structure list */ Cblist[0] = &MY_AIOCB; ret = Aio_read (&MY_AIOCB); ret = Aio_suspend (cblist,max_list,null);

(6) The Aio_cancelaio_cancel () function allows the user to cancel one or all of the I/O requests performed on a file descriptor as follows: int aio_cancel (int fd, struct AIOCB *aiocbp); To cancel a request, We need to provide file descriptors and AIOCB references. If the request is successfully canceled, the function returns aio_canceled. If the request is complete, the function returns aio_notcanceled. To cancel all requests for a given file descriptor, we need to provide a descriptor for the file and a null reference to the AIOCBP. If all requests are canceled, the function returns aio_canceled, and if at least one request is not canceled, the function returns aio_not_canceled, and if no request can be canceled, the function returns Aio_alldone. We can then use Aio_error to validate each AIO request. If the request has been canceled, then Aio_error will return 1, and the errno will be set to ecanceled. (7) Lio_listio Finally, AIO provides a way to initiate multiple transports simultaneously using the Lio_listio API function. This function is important because it means that we can start a large number of I/O operations in a system call (one kernel context switch). From a performance point of view, this is very important, so it's worth taking a moment to explore.  The Lio_listio API functions are prototyped as follows: int lio_listio (int mode, struct AIOCB *list[], int nent,struct sigevent *sig); The mode parameter can be lio_wait or lio_nowait. LIO_WAIT will block this call until all I/O is complete. After the operation is queued, the lio_nowait will return. List is a AIOCB reference, and the maximum number of elements is defined by nent. Note that the elements of list can be ignored for null,lio_listio. The Sigevent reference defines a method that generates a signal when all I/O operations are complete.
Example: Use of user space asynchronous I/O Lio_listio ()
struct AIOCB aiocb1,aiocb2; struct AIOCB *list[max_list] .../* Prepare first aiocb*/aiocb1.aio_fildes = FD; aiocb1.aio_buf = Mallo C (BUFSIZE + 1); aiocb1.aio_nbytes = BUFSIZE; aiocb1.aio_offset = next_offset;/*lio_read read operation, Lio_write write operation, LIO_NOP empty operation */ Aiocb1.aio_lio_opcode = lio_read;/* Asynchronous read operation */.../* Prepare multiple Aiocb*/bzero (char *) list,sizeof (list);/* Fill in the list AIOCB] = AIOCB1, list[1] = AIOCB2, .... ret = Lio_listio (lio_wait,list,max_list,null);/* Initiate large number of I/O operations */
Using callback functions as a notification for AIO
In addition to the signal, the application can provide a callback (Callback) function to the kernel so that the kernel calls this function after the AIO request is complete.
Example: Using a callback function as an AIO asynchronous I/O notification mechanism
/* Set asynchronous I/O request */void setup_io (...) {int fd; struct AIOCB MY_AIOCB;/* Set AIO request */bzero ((char *) &my_aiocb,sizeof (struct AIOCB); aiocb.aio_fildes = FD; a Iocb.aio_buf = malloc (BUFSIZE + 1); aiocb.aio_nbytes = BUFSIZE; aiocb.aio_offset = next_offset;/* Connect AIO request and thread callback function */MY_AIOC B.aio_sigevent.sigev_notify = sigev_thread; my_aiocb.aio_sigevent.notify_function = Aio_completion_handler;/* Set callback function */my_aiocb.aio_sigevent.notify_attributes = NULL; my_aiocb.aio_sigevent.sigev_value_sival_ptr = &MY_AIOCB; .... ret = Aio_read (&MY_AIOCB);} /* asynchronous I/O completion callback function */void Aio_completion_handler (sigval_t sigval) {struct AIOCB *req; req = (struct AIOCB *) sigval.sival_ptr; *aio Request Complete */if (Aio_error (req) = = 0)/* Request complete, get return value */ret = Aio_return (req);}
after creating the AIOCB request, the above program uses Sigev_thread to request a thread callback function as the notification method. In the callback
function, the corresponding AIOCB pointer can be obtained through the (struct aiocb*) sigval.sival_ptr, using the AIO function to verify that the request has been completed.
The
proc file system contains two virtual files that can be used to optimize the performance of asynchronous I/O.
(1) The/proc/sys/fs/aio-nr file provides the current number of system-wide asynchronous I/O requests.
(2)/proc/sys/fs/aio-max-nr file is the maximum number of concurrent requests allowed, the maximum number is usually 64KB, which for most applications
the program is enough.
AIO and device drivers
in the kernel, each I/O request corresponds to a KIOCB struct, whose KI_FILP member points to the corresponding file pointer, by IS_SYNC_KIOCB ()
You can determine whether a KIOCB is a synchronous I/O request, and if it returns non-true, it is expressed as an asynchronous I/O request.
The
block device and the network device itself are asynchronous, and only the character device must explicitly indicate that the Aio,aio should be supported for most character devices
Not required, only a very small number of devices are needed, for example, for tape drives, because I/O operations are slow, using asynchronous I/O can improve performance.
in a character device driver, File_operations contains 3 AIO-related member functions:
ssize_t (*aio_read) (struct KIOCB *iocb,char *buffer,size_t count,loff_t offset); ssize_t (*aio_write) (struct KIOCB *IOCB , Char *buffer,size_t count,loff_t offset); ssize_t (*aio_fsync) (struct KIOCB *iocb,int datasync);
Aio_read () and Aio_write () are not the same as the offset parameter in read () and write () in Filp_operations, which directly passes the value,
The latter is passed as a pointer, because AIO never needs to change the location of the file.
The
Aio_read () and Aio_write () functions themselves do not necessarily complete the read and write operations, it is just initiating, initializing read and write operations.
Example: I/O functions in a device driver

struct async_work () {struct KIOCB *iocb;/*KIOCB struct pointer */int result;/* Execution result */struct work_struct work;/* Working structure */};.../* asynchronous Read * /static ssize_t xxx_aio_read (struct KIOCB *iocb,char *buf,size_t count,loff_t pos) {return xxx_defer_op (0,iocb,count, POS);} /* Asynchronous Write */static ssize_t xxx_aio_write (struct KIOCB *iocb,const char *buf,size_t count,loff_t POS) {return xxx_defer_op (1, Iocb,count,pos);} /* Initialize async i/o*/static int xxx_defer_op (int write,struct kiocb *iocb,char *buf,size_t count,loff_t pos) {struct async_work * async_wk; int result;/* copy*/if (write) result = Xxx_write (Iocb->ki_filp,buf,count,&pos) When we can access buffer; Elseresult = Xxx_read (Iocb->filp,buf,count,&pos);/* If it is a synchronous IOCB, return to Status */if (IS_SYNC_KIOCB (IOCB)) return result ;/* Otherwise, delay execution of */async_wk = Kmalloc (sizeof (*ASYNC_WK), Gfp_kernel), if (Async_wk = NULL) return result;/* Schedule deferred work */ASYNC_ WK-&GT;IOCB = IOCB; async_wk->result = result; Init_work (&AMP;ASYNC_WK-&GT;WORK,XXX_DO_DEFERRED_OP,ASYNC_WK); Schedule_delay_work (&async_wk->work, HZ/1return-eiocbqueued;/* Control returns User space */}/* delay after execution */static void xxx_do_deferred_op (void *p) {struct Async_work *async_wk = ( struct async_work *) p;./* Perform I/O operations */aio_complete (async_wk->iocb,async_wk->result,0); Kfree (ASYNC_WK);}
The
most core of the above code is the use of the work_struct mechanism through the schedule_delay_work () function to delay the execution of I/O operations, and in the specific
After the I/O operation completes, call Aio_complete () to notify the kernel driver that the I/O operation has completed.
Typically, specific character device drivers are generally not allowed AIO support, but only fs/direct-io.c,driver/usb/gadget/inode.c in the kernel,
AIO is used in a few places such as fs/nfs/direct.c.

Hasen Linux device Driver Development learning Journey-asynchronous I/O

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.