Yan Gang's fread and read, fwrite and write

Keywords: Unix Linux less

Articles Catalogue

Resources

nuttx_fread_fwrite.md

brief introduction

The Realization Principle of fwrite, write and printf

#include 	<stdio.h>
#include	<unistd.h>
#include	<string.h>
#include	<errno.h>

int main(int argc,char**argv){
    char buf[]="hello,world\n";
    
    //Print with'printf'
    printf("%s",buf);
     
    //Print with'fwrite'
    fwrite(buf,strlen(buf), 1,stdout);
    
    //use 'write' function to print char 
    write(STDOUT_FILENO,&buf,strlen(buf));
    
    return(0);
}

results of enforcement

hello,world
hello,world
hello,world 

fread, fwrite Basic Knowledge

The operation of files under UNIX is fopen, fread, fwrite, and open, read, write. The difference between them is that fopen series is the standard C library function; open series is defined by POSIX, is the system call file handles in UNIX system, also known as file structure pointer, file descriptor. Descriptors is an integer variable. As we often know, stdout (standard output), stdin (standard input), stderr (standard error), are all file handles. Each file handle corresponds to a file descriptor, stdout, stdin, stderr, which corresponds to the file description of 0, 1, 2 FREAD and fwrite interfaces NAME fread. Fwrite - binary stream input / output
SYNOPSIS

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
              FILE *stream);

3.1 Nutx configuration options

# CONFIG_STDIO_DISABLE_BUFFERING is not set
CONFIG_STDIO_BUFFER_SIZE=32   #Configuration file buffer size
CONFIG_STDIO_LINEBUFFER=y     #Whether to Open File Buffer

3.2 Definition of Nutx File Handles and File Descriptors

3.2.1 file_struct file structure

File structure file location NuttX/nuttx/include/nuttx/fs/fs.h, file structure, including the description file buffer, this is a great feature of the file structure.

#if CONFIG_NFILE_STREAMS > 0
struct file_struct
{
  int                fs_fd;        //Binding file descriptors
#ifndef CONFIG_STDIO_DISABLE_BUFFERING
  sem_t              fs_sem;       /* For thread safety */
  pid_t              fs_holder;    /* Holder of sem */
  int                fs_counts;    /* Number of times sem is held */
  FAR unsigned char *fs_bufstart;  //The head of the execution buffer, which is dynamically allocated by f_open
  FAR unsigned char *fs_bufend;    /* Pointer to 1 past end of buffer */
  FAR unsigned char *fs_bufpos;    /* Current position in buffer */
  FAR unsigned char *fs_bufread;   /* Pointer to 1 past last buffered read char. */
#endif
  uint16_t           fs_oflags;    /* Open mode flags */
  uint8_t            fs_flags;     /* Stream flags */
#if CONFIG_NUNGET_CHARS > 0
  uint8_t            fs_nungotten; /* The number of characters buffered for ungetc */
  unsigned char      fs_ungotten[CONFIG_NUNGET_CHARS];
#endif
};

3.2.2 file_opcreation framework

Definition of file descriptor Each file descriptor of linux binds a static file creation file interface, binds f_priv-driven objects, and operates on the underlying registers.

struct file
{
  int               f_oflags;   /* Open mode flags */
  off_t             f_pos;      /* File position */
  FAR struct inode *f_inode;    /* Driver or file system interface */
  void             *f_priv;     /* Per file driver private data */
};

/* This defines a list of files indexed by the file descriptor */

#if CONFIG_NFILE_DESCRIPTORS > 0
struct filelist
{
  sem_t   fl_sem;               /* Manage access to the file list */
  struct file fl_files[CONFIG_NFILE_DESCRIPTORS];
};

3.2.3 Standard Input, Standard Output, Standard Error

The stream of standard input and output is a file handle created automatically by each thread when it is created and bound automatically to file descriptors 0, 1, 2.

#define stdin      (&sched_getstreams()->sl_streams[0])
#define stdout     (&sched_getstreams()->sl_streams[1])
#define stderr     (&sched_getstreams()->sl_streams[2])

Standard interface definition for 3.2.4 posix

struct file_operations
{
  /* The device driver open method differs from the mountpoint open method */

  int     (*open)(FAR struct file *filep);

  /* The following methods must be identical in signature and position because
   * the struct file_operations and struct mountp_operations are treated like
   * unions.
   */

  int     (*close)(FAR struct file *filep);
  ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
  ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
  off_t   (*seek)(FAR struct file *filep, off_t offset, int whence);
  int     (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);

  /* The two structures need not be common after this point */

#ifndef CONFIG_DISABLE_POLL
  int     (*poll)(FAR struct file *filep, struct pollfd *fds, bool setup);
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  int     (*unlink)(FAR struct inode *inode);
#endif

3.3 Implementation of fopen interface

FAR FILE *fopen(FAR const char *path, FAR const char *mode)
    //open file descriptors
    fd = open(path, oflags, 0666);
        
        //Bind file handle to os
        -> ret = fs_fdopen(fd, oflags, NULL); 
        
        //Dynamic file buffer to locate start and end. 
        stream->fs_bufstart =	group_malloc(tcb->group, CONFIG_STDIO_BUFFER_SIZE);
        stream->fs_bufend  = &stream->fs_bufstart[CONFIG_STDIO_BUFFER_SIZE];
        stream->fs_bufpos  = stream->fs_bufstart;
        stream->fs_bufread = stream->fs_bufstart;
        
        //Binding files are described in the os stream
        stream->fs_fd      = fd;  
};

Implementation of 3.4 fwrite interface

size_t fwrite(FAR const void *ptr, size_t size, size_t n_items, FAR FILE *stream)
    ->  size_t  full_size = n_items * (size_t)size;
	  ->  bytes_written = lib_fwrite(ptr, full_size, stream); //Write all bytes

      //Write the data to the file buffer first
      for (dest = stream->fs_bufpos; gulp_size > 0; gulp_size--)
        *dest++ = *src++;
      
      //Buffers are full before they are written to the real device
      if (dest >= stream->fs_bufend)
          int bytes_buffered = lib_fflush(stream, false);
            ->   bytes_written = write(stream->fs_fd, src, nbuffer);3.3.3 fread Implementation process  size_t fread(FAR void *ptr, size_t size, size_t n_items, FAR FILE *stream)
         bytes_read = lib_fread(ptr, full_size, stream);
        
        //If the buffer has data, get the data directly from the buffer first.
        while ((count > 0) && (stream->fs_bufpos < stream->fs_bufread))
            *dest++ = *stream->fs_bufpos++;
            count--;
          }
        
          buffer_available = stream->fs_bufend - stream->fs_bufread;

        //If the data is insufficient, read directly from the file
        -> if (count > buffer_available)
            bytes_read = read(stream->fs_fd, dest, count);

        //If the required data is smaller than the buffer size, read the entire buffer size
        ->   if (count < buffer_available)
          bytes_read = read(stream->fs_fd, dest, buffer_available);3.4 printf Interface Implementation printf The lowest level function isup_putc() This function is smt32 Blocking waits for the sending to complete, and this up_putc It can be invoked anywhere.int printf(FAR const IPTR char *fmt, ...)
          int vfprintf(FAR FILE *stream, FAR const IPTR char *fmt, va_list ap)
                ->lib_stdoutstream(&stdoutstream, stream);
                  ->  outstream->public.put = stdoutstream_putc;
                    ->  result = fputc(ch, sthis->stream);
                        -> ret = lib_fwrite(&buf, 1, stream); // Here's the same implementation as fwrite
                          ->  if (dev->isconsole)
                          -> ret = uart_irqwrite(dev, buffer, buflen);
                            ->uart_putc('\r'); //  #define uart_putc(ch) up_putc(ch)     
                                  -> void up_lowputc(char ch)
                                      -> while ((getreg32(CONSOLE_BASE+A1X_UART_LSR_OFFSET) & UART_LSR_THRE) == 0);
                                      putreg32((uint32_t)ch, CONSOLE_BASE+A1X_UART_THR_OFFSET);
                -> n = lib_vsprintf(&stdoutstream.public, fmt, ap);
                      ->   obj->put(obj, FMT_CHAR);
                  if (FMT_CHAR == '\n')
                      (void)obj->flush(obj); 

3.5 File System Principles

File system mounting filesystem is a problem I have been puzzled about, which finally understands the problem of file system mounting. The difference between block device and character device is that the character device drives the registered device node, while open and write can operate character device directly, while the registration of block device is not given to wr directly. The file system is registered at the mount point to operate the file. Block devices, unlike character device drivers, can be processed in a single byte. flash features sector processing. Generally, SD card sectors are 512 bytes.

union inode_ops_u
{
  FAR const struct file_operations     *i_ops;    /* Driver operations for inode *//
  FAR const struct mountpt_operations  *i_mops;   /* Operations on a mountpoint */
};

//Mount file system
if mount -t vfat /dev/mmcsd0 /fs/microsd

//mount.c 
mountpt_inode->u.i_mops  = mops; //File system and device node mounted well

3.6 sd card file write operation process of this sd card file

The write of posix calls the file system fat_write, and then the drive mmcsd_writessize_t write (int fd, FAR const void*buf, size_t nbytes)

//write of fat file system interface
    ret = fat_hwwrite(fs, userbuffer, ff->ff_currentsector, nsectors);
      //Driver interface for calling mmcsd card
      ->  ssize_t nSectorsWritten =
            inode->u.i_bops->write(inode, buffer, sector, nsectors);
            ->mmcsd_write,/* mmcsd write*/

summary

userbuffer, ff->ff_currentsector, nsectors);
// driver interface for calling mmcsd card
-> ssize_t nSectorsWritten =
inode->u.i_bops->write(inode, buffer, sector, nsectors);
-> mmcsd_write, /* mmcsd write*/

# Summary
 Through the above analysis, our FREAD and fwrite, and printf are buffered. The condition that the contents written by fwrite are refreshed to the SD card is that the buffer is full before it can be written to the physical sd. Fread reads the data of the buffer from the physical sector every time. For example, the buffer is 32 bytes, FREAD takes 10 bytes, and FREAD reads 3 bytes directly. 2 bytes, buffered to the file buffer, the remaining 22 bytes, the next time the user calls fread, read 10 bytes, 10 bytes less than the remaining 22 bytes of the buffer, you can read directly from the buffer, which can improve the size of the buffer. Printf has byte-independent characteristics, because printf processes all characters, then the efficient method is to determine whether to receive the return symbol, you can refresh the buffer, and fgetc is to wait for the received data to have the return symbol, before processing. Block device [fwrite [c library] - > write [system call] - > fat_write [file system] - > mmcsd_write [SD driver] [character device [fwrite [c library] - > write [system call] - > serial_write [serial driver]

Posted by Stickdragon on Sat, 17 Aug 2019 05:57:20 -0700