📄 fileio.sgml
字号:
<para>
This table is analogous to a combination of the filesystem and mount
tables.
</para>
<para>
The <structfield>valid</structfield> field is set
<literal>true</literal> if the stack's <function>init()</function>
function returned successfully and the
<structfield>syncmode</structfield> field contains the
<literal>CYG_SYNCMODE_SOCK_*</literal> bits described above.
</para>
<!--
-->
<para>
The <structfield>name</structfield> field contains the name of the
protocol stack.
</para>
<!--
-->
<para>
The <structfield>devname</structfield> field names the device that the stack is using. This may
reference a device under "/dev", or may be a name that is only
meaningful to the stack itself.
</para>
<!--
-->
<para>
The <function>init()</function> function pointer is called during
system initialization to start the protocol stack running. If it
returns non-zero the <structfield>valid</structfield> field is set
false and the stack will be ignored subsequently.
</para>
<para>
The <function>socket()</function> function is called to attempt to create a socket in the
stack. When the <function>socket()</function> API function is called the netstack table is
scanned and for each valid entry the <function>socket()</function>
function pointer is called. If
this returns non-zero then the scan continues to the next valid stack,
or terminates with an error if the end of the table is reached.
</para>
<para>
The result of a successful socket call is an initialized file object
with the <structfield>f_xops</structfield> field pointing to the
following structure:
</para>
<programlisting width=72>
struct cyg_sock_ops
{
int (*bind) ( cyg_file *fp, const sockaddr *sa, socklen_t len );
int (*connect) ( cyg_file *fp, const sockaddr *sa, socklen_t len );
int (*accept) ( cyg_file *fp, cyg_file *new_fp,
struct sockaddr *name, socklen_t *anamelen );
int (*listen) ( cyg_file *fp, int len );
int (*getname) ( cyg_file *fp, sockaddr *sa, socklen_t *len, int peer );
int (*shutdown) ( cyg_file *fp, int flags );
int (*getsockopt)( cyg_file *fp, int level, int optname,
void *optval, socklen_t *optlen);
int (*setsockopt)( cyg_file *fp, int level, int optname,
const void *optval, socklen_t optlen);
int (*sendmsg) ( cyg_file *fp, const struct msghdr *m,
int flags, ssize_t *retsize );
int (*recvmsg) ( cyg_file *fp, struct msghdr *m,
socklen_t *namelen, ssize_t *retsize );
};
</programlisting>
<para>
It should be obvious from the names of these functions which API calls
they provide support for. The <function>getname()</function> function
pointer provides support for both <function>getsockname()</function>
and <function>getpeername()</function> while the
<function>sendmsg()</function> and <function>recvmsg()</function>
function pointers provide support for <function>send()</function>,
<function>sendto()</function>, <function>sendmsg()</function>,
<function>recv()</function>, <function>recvfrom()</function> and
<function>recvmsg()</function> as appropriate.
</para>
</chapter>
<!-- }}} -->
<!-- {{{ Select -->
<chapter id="fileio-select">
<title>Select</title>
<para>
The infrastructure provides support for implementing a select
mechanism. This is modeled on the mechanism in the BSD kernel, but has
been modified to make it implementation independent.
</para>
<para>
The main part of the mechanism is the <function>select()</function>
API call. This processes its arguments and calls the
<function>fo_select()</function> function pointer on all file objects
referenced by the file descriptor sets passed to it. If the same
descriptor appears in more than one descriptor set, the
<function>fo_select()</function> function will be called separately
for each appearance.
</para>
<para>
The <parameter>which</parameter> argument of the
<function>fo_select()</function> function will either be
<literal>CYG_FREAD</literal> to test for read conditions,
<literal>CYG_FWRITE</literal> to test for write conditions or zero to
test for exceptions. For each of these options the function should
test whether the condition is satisfied and if so return true. If it
is not satisfied then it should call
<function>cyg_selrecord()</function> with the
<parameter>info</parameter> argument that was passed to the function
and a pointer to a <structname>cyg_selinfo</structname> structure.
</para>
<para>
The <structname>cyg_selinfo</structname> structure is used to record information about current
select operations. Any object that needs to support select must
contain an instance of this structure. Separate <structname>cyg_selinfo</structname>
structures should be kept for each of the options that the object can
select on - read, write or exception.
</para>
<para>
If none of the file objects report that the select condition is
satisfied, then the <function>select()</function> API function puts
the calling thread to sleep waiting either for a condition to become
satisfied, or for the optional timeout to expire.
</para>
<para>
A selectable object must have some asynchronous activity that may
cause a select condition to become true - either via interrupts or the
activities of other threads. Whenever a selectable condition is
satisfied, the object should call <function>cyg_selwakeup()</function> with a pointer to
the appropriate <structname>cyg_selinfo</structname> structure. If the thread is still waiting,
this will cause it to wake up and repeat its poll of the file
descriptors. This time around, the object that caused the wakeup
should indicate that the select condition is satisfied, and the
<function>select()</function> API call will return.
</para>
<para>
Note that <function>select()</function> does not exhibit real time
behaviour: the iterative poll of the descriptors, and the wakeup
mechanism mitigate against this. If real time response to device or
socket I/O is required then separate threads should be devoted to each
device of interest and should use blocking calls to wait for a
condition to become ready.
</para>
</chapter>
<!-- }}} -->
<!-- {{{ Devices -->
<chapter id="fileio-devices">
<title>Devices</title>
<para>
Devices are accessed by means of a pseudo-filesystem, "devfs", that is
mounted on "/dev". Open operations are translated into calls to
<function>cyg_io_lookup()</function> and if successful result in a file object whose
<structfield>f_ops</structfield> functions translate filesystem API functions into calls into
the device API.
</para>
</chapter>
<!-- }}} -->
<!-- {{{ Writing a New Filesystem -->
<chapter id="fileio-writing">
<title>Writing a New Filesystem</title>
<para>
To create a new filesystem it is necessary to define the fstab entry
and the file IO operations. The easiest way to do this is to copy an
existing filesystem: either the test filesystem in the FILEIO package,
or the RAM or ROM filesystem packages.
</para>
<para>
To make this clearer, the following is a brief tour of the FILEIO
relevant parts of the RAM filesystem.
</para>
<para>
First, it is necessary to provide forward definitions of the functions
that constitute the filesystem interface:
</para>
<programlisting width=72>
//==========================================================================
// Forward definitions
// Filesystem operations
static int ramfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte );
static int ramfs_umount ( cyg_mtab_entry *mte );
static int ramfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
int mode, cyg_file *fte );
static int ramfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
static int ramfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
static int ramfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
static int ramfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
cyg_dir dir2, const char *name2 );
static int ramfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
cyg_dir dir2, const char *name2, int type );
static int ramfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
cyg_file *fte );
static int ramfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
cyg_dir *dir_out );
static int ramfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
struct stat *buf);
static int ramfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
int key, void *buf, int len );
static int ramfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
int key, void *buf, int len );
// File operations
static int ramfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int ramfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int ramfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
static int ramfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
CYG_ADDRWORD data);
static int ramfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode );
static int ramfs_fo_close (struct CYG_FILE_TAG *fp);
static int ramfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf );
static int ramfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
static int ramfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
// Directory operations
static int ramfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int ramfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
</programlisting>
<para>
We define all of the fstab entries and all of the file IO
operations. We also define alternatives for the
<structfield>fo_read</structfield> and
<structfield>fo_lseek</structfield> file IO operations.
</para>
<para>
We can now define the filesystem table entry. There is a macro,
<literal>FSTAB_ENTRY</literal> to do this:
</para>
<programlisting width=72>
//==========================================================================
// Filesystem table entries
// -------------------------------------------------------------------------
// Fstab entry.
// This defines the entry in the filesystem table.
// For simplicity we use _FILESYSTEM synchronization for all accesses since
// we should never block in any filesystem operations.
FSTAB_ENTRY( ramfs_fste, "ramfs", 0,
CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM,
ramfs_mount,
ramfs_umount,
ramfs_open,
ramfs_unlink,
ramfs_mkdir,
ramfs_rmdir,
ramfs_rename,
ramfs_link,
ramfs_opendir,
ramfs_chdir,
ramfs_stat,
ramfs_getinfo,
ramfs_setinfo);
</programlisting>
<para>
The first argument to this macro gives the fstab entry a name, the
remainder are initializers for the field of the structure.
</para>
<para>
We must also define the file operations table that is installed in all
open file table entries:
</para>
<programlisting width=72>
// -------------------------------------------------------------------------
// File operations.
// This set of file operations are used for normal open files.
static cyg_fileops ramfs_fileops =
{
ramfs_fo_read,
ramfs_fo_write,
ramfs_fo_lseek,
ramfs_fo_ioctl,
cyg_fileio_seltrue,
ramfs_fo_fsync,
ramfs_fo_close,
ramfs_fo_fstat,
ramfs_fo_getinfo,
ramfs_fo_setinfo
};
</programlisting>
<para>
These all point to functions supplied by the filesystem except the
<structfield>fo_select</structfield> field which is filled with a
pointer to <function>cyg_fileio_seltrue()</function>. This is provided
by the FILEIO package and is a select function that always returns
true to all operations.
</para>
<para>
Finally, we need to define a set of file operations for use when
reading directories. This table only defines the
<structfield>fo_read</structfield> and
<structfield>fo_lseek</structfield> operations. The rest are filled
with stub functions supplied by the FILEIO package that just return an
error code.
</para>
<programlisting width=72>
// -------------------------------------------------------------------------
// Directory file operations.
// This set of operations are used for open directories. Most entries
// point to error-returning stub functions. Only the read, lseek and
// close entries are functional.
static cyg_fileops ramfs_dirops =
{
ramfs_fo_dirread,
(cyg_fileop_write *)cyg_fileio_enosys,
ramfs_fo_dirlseek,
(cyg_fileop_ioctl *)cyg_fileio_enosys,
cyg_fileio_seltrue,
(cyg_fileop_fsync *)cyg_fileio_enosys,
ramfs_fo_close,
(cyg_fileop_fstat *)cyg_fileio_enosys,
(cyg_fileop_getinfo *)cyg_fileio_enosys,
(cyg_fileop_setinfo *)cyg_fileio_enosys
};
</programlisting>
<para>
If the filesystem wants to have an instance automatically mounted on
system startup, it must also define a mount table entry. This is done
with the <literal>MTAB_ENTRY</literal> macro. This is an example from
the test filesystem of how this is used:
</para>
<programlisting width=72>
MTAB_ENTRY( testfs_mte1,
"/",
"testfs",
"",
0);
</programlisting>
<para>
The first argument provides a name for the table entry. The following
arguments provide initialization for the
<structfield>name</structfield>, <structfield>fsname</structfield>,
<structfield>devname</structfield> and <structfield>data</structfield>
fields respectively.
</para>
<para>
These definitions are adequate to let the new filesystem interact
with the FILEIO package. The new filesystem now needs to be fleshed
out with implementations of the functions defined above. Obviously,
the exact form this takes will depend on what the filesystem is
intended to do. Take a look at the RAM and ROM filesystems for
examples of how this has been done.
</para>
</chapter>
<!-- }}} -->
</part>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -