Title: messages from the FS


In order to communicate with one another, the services (the file system (FS), the memory manager (MM), and the network service), the kernel, and the user processes send messages back and forth. These messages take the following form:


typedef struct { 

int m_source; /* who sent the message */
int m_type; /* what kind of message is it */
union {
mess_1 m_m1;
mess_2 m_m2;
mess_3 m_m3;
mess_4 m_m4;
mess_5 m_m5;
mess_6 m_m6;
} m_u;
} message;
typedef struct {int m1i1, m1i2, m1i3; char *m1p1, *m1p2, *m1p3;} mess_1;
typedef struct {int m2i1, m2i2, m2i3; long m2l1, m2l2; char *m2p1;} mess_2;
typedef struct {int m3i1, m3i2; char *m3p1; char m3ca1[M3_STRING];} mess_3;
typedef struct {long m4l1, m4l2, m4l3, m4l4, m4l5;} mess_4;
typedef struct {char m5c1, m5c2; int m5i1, m5i2; long m5l1, m5l2, m5l3;}mess_5;
typedef struct {int m6i1, m6i2, m6i3; long m6l1; sighandler_t m6f1;} mess_6;

(In the names of the fields above, "i" stands for integer, "l" stands for long, and "p" stands for pointer.)

The message types (i.e., m_type) that the FS sends will be DEV_OPEN, DEV_CLOSE, DEV_READ, DEV_WRITE, DEV_IOCTL3, and DEV_CANCEL. For example, if a user process opens the /dev/udp file, the FS sends a message of type "DEV_OPEN" to the network service. The type of message that the network service always sends the FS is DEVICE_REPLY.

Recall that only a single field within a union is valid. When the file system (FS) receives a request (open, read, write, etc.) for one of the device files associated with the network service (/dev/udp, /dev/tcp, /dev/ip, etc.), the FS sends a message with an m_u of mess_2 to the network service. Repeated from above, the mess_2 typedef is declared as follows:

typedef struct {int m2i1, m2i2, m2i3; long m2l1, m2l2; char *m2p1;} mess_2;


m_source m_type DEVICE (m2_i1) PROC_NR (m2_i2) COUNT or REQUEST (m2_i3) POSITION (m2_l1) TTY_FLAGS (m2_l2) ADDRESS (m2_p1)


Note: The field POSITION is marked gray because it is not used here and TTY_FLAGS is marked gray because it is always false. This denotes that it is in blocking mode (in other words, the process is waiting for a response).

The meaning of each of these fields is as follows:

DEVICE is the minor number of the device.
PROC_NR is the process number of the client.
COUNT is the number of bytes to transfer.
REQUEST is specific to ioctl operations. An example of an ioctl operation is NWIOSUDPOPT (NetWork IO Set UDP OPTions).
POSITION is the position in the device file.
TTY_FLAGS is zero (blocking mode).
ADDRESS is a pointer to a buffer. This buffer will either be read from or written to by the network service.

It is interesting to compare the fields above with the parameters for dev_io (from the file system):

PUBLIC int dev_io(op, nonblock, dev, pos, bytes, proc, buff)
int op;                            /* DEV_READ, DEV_WRITE, DEV_IOCTL, etc. */
int nonblock;                     /* TRUE if nonblocking op */
dev_t dev;                     /* major-minor device number */
off_t pos;                     /* byte position */
int bytes;                     /* how many bytes to transfer */
int proc;                     /* in whose address space is buff? */
char *buff;                     /* virtual address of the buffer */


The format of reply messages is different:


m_source DEVICE_REPLY proc nr status fd operation unused unused



The message queue is a linked list of mq_t struct's:

typedef struct mq

{
       message mq_mess;
       struct mq *mq_next;
       int mq_allocated;
} mq_t;
Immediately after initialization, the message queue is as follows:



In preparation for receiving a message, mq_get() is called to remove one of the mq_t structs in the linked list from the free list of messages. This link will hold the next message received and will be passed to either the code that handles the messages from the file system, the asynchronous alarm task, or the ethernet task.