Mumufs: Virtual File System to Support IPC of Type Many-to-many

Author / Автор: Sergey Satskiy
Publication date / Опубликовано: 15.07.2007
Version / Версия текста: 1.0

Main Characteristics and Usage Scenario Example

Mumufs main characteristics are listed below:

  • Mumufs is a file system which resides in RAM.
  • Mumufs supports regular files, directories, symbolic and hard links.
  • Each file on mumufs is associated with a data block of an arbitrary size. Read/write operations for a file correspond to read/write operations from/to the corresponding data block.
  • Each successful write to a file operation replaces the previously stored data block.
  • Each new read/write operation must start from the shift 0 from the beginning of a file. An operation is considered as successful only if the whole data block is read or written.
  • Each file can be opened for reading and writing simultaneously many times.
  • Both blocking and non-blocking IO is supported.
  • Mumufs supports select и poll operations.

Let's consider one of the possible scenarios. Suppose that there are two processes (#1 and #2) which read from the /mnt/mumu/file1 file and two processes (#3 and #4) which write into the same file.

A mumufs file system is mounted on /mnt/mumu. The process #3 opens the /mnt/mumu/file1 file for writing. The file does not exist yet so mumufs creates all the necessary data structures in memory. Then the process #3 writes into the file a block of 2048 bytes. Mumufs allocates in memory a block of a suitable size and copies the process data block into the just allocated memory. Then the process #1 opens the /mnt/mumu/file1 file for reading and reads 2048 bytes. If the process #1 reading buffer is less than 2048 bytes the process will receive an error. If the buffer is large enough then the data block will be copied to the process and mumufs memorises that the current block has been read by the process #1 (more precise, the file descriptor of the process #1 is memorised). Then the process #1 repeats the read operation. Mumufs understands that the process #1 has read the currently available data block and blocks the process reading. Then the process #4 opens the /mnt/mumu/file1 file for writing and writes 256 bytes. Mumufs replaces the first data block with a new one and unblocks all the blocked processes (process #1 in the case). Then the process #1 reads 256 bytes of the second data block and will be blocked at the followed read operation. Then the process #2 opens the /mnt/mumu/file1 file for reading and reads from the file. Mumufs copies 256 bytes of the current data block to the process #2. At the followed read operation the process #2 will be blocked. Then the process #1 writes 512 bytes. The new block replaces the old one and mumufs unblocks the processes #1 and #2 to make them able to read the new data block.

A file deletion leads to deallocation of a memory block which is associated with the file. Closing of the file by all the clients do not lead to deallocation of the memory occupied by the last written data block. Unmounting of a file system leads to deallocation of all the allocated memory blocks.

So, a transaction unit for mumufs is a data block. A file name on mumufs is used as a unique identifier of an arbitrary data block. A life time of a data block is the same as the life time of a file on mumufs i.e. till the file removal or file system unmounting.

Mumufs could be mounted arbitrary number of times.

A good example of the mumufs usage could be a system with a graphics user interface. For example, a battery status in a mobile phone could be interested for many processes. That could be a process which displays an icon in a kind of status bar, a process which displays a detailed battery status in a separate window and a process which initiates a power saving mode etc. All those processes are interested in the most up to date value while the history of the status changes in not important. If mumufs is used the process which populates the battery status does not need to know how many consuming processes exist and how many will appear in the future. The data producer opens a file and updates the value when required. The implementation of the data exchange for consumers is also very simple. Consumers need to open a file and read it. The only required agreement is the file name and the file format.

Blocking

Mumufs supports both blocking and non-blocking IO. The IO mode affects the read operations only.

In case of blocking read a process is blocked when there is no data block which has not been read yet. The process unlocking is performed when a new data block becomes available or when a process receives an asynchronous signal.

Implementation Specific

Mumufs uses read-write locks to implement secure data access. This leads to the higher priority of the write operations than read operations.

Mumufs supports extended file attributes. This allows the data producers to comment the purpose of the data blocks in a way they want, for example in a form of a descriptive text.

As soon mumufs supports a standard file system interface therefore all the standard UNIX file utilities could be used. Nevertheless it worth mentioning that mumufs has some implementation specific. If the command 'cat /mnt/mumu/batteryStatus' is issued, for example, then the current data block will be printed on the standard output and then the cat command will be blocked because cat will call read again internally. As soon as the data producer updates the data block the cat command will print a new block and be blocked again. It is always possible, however, to interrupt the cat command by pressing Ctrl+C. This kind of limitations makes the usage of utilities difficult in a batch mode, however, visibility and easiness of mumufs use is still much better than in case of traditional IPCs.

Access Rights

Mumufs processes file access attributes in a traditional for many UNIX disk file systems way.

Usage Specific

As soon as mumufs uses the whole data block as a single transaction, the read and write operations must always start with shift 0 from the beginning of a file and the seek operation is not supported at all (as well as file locks). This lead to some specific which is described below.

The libc library provides a set of functions with the f prefix: fopen(:), fclose(:), fread(:), fwrite(:) etc. It would think that those functions could be used with files located on mumufs but it is not so. The libc version of fwrite(:) function performs a merge of small data blocks into a single block to optimize IO operations. If for example there are two write by 2 bytes each then a file system might be called just one with a block of 4 bytes (with the default buffering settings). This makes the usage of those functions inconvenient with mumufs.

It is also possible to use another group of functions: open(:), close(:), read(:), write(:) etc. These functions do not make any additional actions with data blocks and suit well for working with files on mumufs.

In order to simplify programmimg with mumufs a C++ templates library libmumu++ is developed. The library described in the section below.

Libmumu++

The libmumu++ templates library provides just a single template class Entry. The class is a C++ wrapper to work with files on mumufs. The given to the template type is used as a data exchange block. It is supposed that the type data members occupy a consequent memory block.

An example below demonstrates usage of the write operation via libmumu++.

#include "mumulib.h"
using namespace mumu;

int  main( int  argc, char **  argv )
{
    try
    {
        Entry< int >      Value( "/mnt/mumu/one" );
        Entry.Write( 154 );
    }
    catch ( std::exception &  E )
    {
        return 1;    // Writing error
    }

    return 0;
}

The Entry template constructor opens the given file in the blocking mode by default. The second constructor parameter controls the mode so the file could also be opened in the non-blocking mode.

The Entry template supports two overloaded versions of read functions which are purposed for blocking and non-blocking IO. A select operation is also supported with ability to provide a timeout.

An example below demonstrates usage of read and select operations via libmumu++.

#include "mumulib.h"
using namespace mumu;

int  main( int  argc, char **  argv )
{
    try
    {
        int               Value;
        Entry< int >      Blocking( "/mnt/mumu/one" );
        Entry< int >      NonBlocking("/mnt/mumu/one", false );

          // Blocking IO
          // If the file does not exist then we are blocked here
          // Use the first program to put some value in the file
        Value = Blocking.Read();

          // Non-blocking IO
        if ( NonBlocking.Read( &Value ) == 0 )
        {
          // No data available
        }
        else
        {
          // A value is read
        }

          // Select example
          // Use the first program to put update the file content

        struct timeval    Timeout;
        Timeout.tv_sec  = 15;
        Timeout.tv_usec = 0;
        if ( Blocking.Select( &Timeout ) )
        {
          // New data arrived
          Value = Blocking.Read();
        }
        else
        {
          // No new data and the timeout exceeded
        }

          // 0 - waits forever. Use the first program
        if ( Blocking.Select( 0 ) )
        {
          // New data arrived
          Value = Blocking.Read();
        }
        else
        {
          // No new data and the timeout exceeded
        }
    }
    catch ( std::exception &  E )
    {
        return 1;    // Writing error
    }

    return 0;
}

The Entry template supports a specialization of all the member functions for the std::string type.

A more detailed documentation for libmumu++ could be generated with a help of doxygen. The library has all the required comments.


Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Разрешается копирование и распространение этой статьи любым способом без внесения изменений, при условии, что это разрешение сохраняется.
Last Updated: December 15, 2007