Starting from:

$25

operatingsystems -  0368-2162 - Message Slot Kernel Module Assignment - Solved

1            Introduction
The goal of this assignment is to gain experience with kernel programming and, particularly, a better understanding on the design and implementation of inter-process communication (IPC), kernel modules, and drivers.

In this assignment, you will implement a kernel module that provides a new IPC mechanism, called a message slot. A message slot is a character device file through which processes communicate. A message slot device has multiple message channels active concurrently, which can be used by multiple processes. After opening a message slot device file, a process uses ioctl() to specify the id of the message channel it wants to use. It subsequently uses read()/write() to receive/send messages on the channel. In contrast to pipes, a message channel preserves a message until it is overwritten, so the same message can be read multiple times.

2            Message slot specification
A message slot appears in the system as a character device file. This device file is managed by the message slot device driver, which you will implement. A message slot is a pseudo device: it doesn’t correspond to a physical hardware device, and therefore all its functionality is provided by the device driver.

Message slot device files.          A device file has a major number and a minor number. The major number tells the kernel which driver is associated with the device file. The minor number is used internally by the driver for its own purposes.

In our case: There can be several message slot files, which correspond to different message slots. All of these files are managed by your driver, i.e., they all have the same major number, which is hard-coded to 240. However, different message slot files will have different minor numbers, allowing your driver to distinguish between them.

Device files are created with the mknod command, which takes as arguments the file’s major and minor numbers. For example:

mknod /dev/slot0 c 240 0

The above command creates a character device file /dev/slot0 with major number 240 (i.e., a message slot) and minor number 0. Then, a subsequent command creates another message slot file:

mknod /dev/slot1 c 240 1

2.1          Semantics of message slot file operations
The message slot driver has special semantics for the ioctl, write, and read file operations, as described below. Note that the description is given in the style of a manual page, and specifies the behavior from the perspective of the processes using the driver. One of your tasks is to figure out how to implement the module so that it provides the specified behavior.

2.1.1           ioctl()
A message slot supports a single ioctl command, named MSG_SLOT_CHANNEL. This command takes a single unsigned int parameter that specifies a non-zero channel id. Invoking the ioctl() sets the file descriptor’s channel id. Subsequent reads/writes on this file descriptor will receive/send messages on the specified channel.

Error cases:

•      If the passed command is not MSG_SLOT_CHANNEL, the ioctl() returns -1 and errno is set to EINVAL.

•      If the passed channel id is 0, the ioctl() returns -1 and errno is set to EINVAL.

2.1.2           write()
Writes a non-empty message of up to 128 bytes from the user’s buffer to the channel. Returns the number of bytes written, unless an error occurs. (Note that the message can contain any sequence of bytes, it is not necessarily a C string.)

Error cases:

•      If no channel has been set on the file descriptor, returns -1 and errno is set to EINVAL.

•      If the passed message length is 0 or more than 128, returns -1 and errno is set to EMSGSIZE.

•      In any other error case (for example, failing to allocate memory), returns -1 and errno is set appropriately (you are free to choose the exact value).

2.1.3           read()
Reads the last message written on the channel into the user’s buffer. Returns the number of bytes read, unless an error occurs:

Error cases:

•      If no channel has been set on the file descriptor, returns -1 and errno is set to EINVAL.

•      If no message exists on the channel, returns -1 and errno is set to EWOULDBLOCK.

•      If the provided buffer length is too small to hold the last message written on the channel, returns -1 and errno is set to ENOSPC.

•      In any other error case (for example, failing to allocate memory), returns -1 and errno is set appropriately (you are free to choose the exact value).

IMPORTANT: Message slot reads/write should be atomic: they should always read/write the entire passed message and not parts of it. So a successful write() always returns the number of bytes in the supplied message and a successful read() returns the number of bytes in the last message written on the channel.

3            Assignment 
Implement the following:

1.    message_slot: A kernel module implementing the message slot IPC mechanism.

2.    message_sender: A user space program to send a message.

3.    message_reader: A user space program to read a message.

3.1          Message slot kernel module (device driver)
Implement the module in files named message_slot.c and mesage_slot.h:

1.        The module should use the hard-coded major number 240. (The proper way to implement a character device driver is to dynamically allocate a major number, as seen in the recitation, but we will use a hard-coded major number for simplicity.)

2.        If module initialization fails, print an error message using printk(KERN_ERR ...).

3.        The module should implement the file operations needed to provide the message slot interface: device_open, device_ioctl, device_read, and device_write. Implement these operations any way you like, as long as the module provides the message slot interface specified above. You might find these suggestions useful:

•      You’ll need a data structure to describe individual message slots (device files with different minor numbers). In device_open(), the module can check if it has already created a data structure for the file being opened, and create one if not. You can get the opened file’s minor number using the iminor() kernel function (applied to the struct inode* argument of device_open()).

•      device_ioctl() needs to associate the passed channel id with the file descriptor it was invoked on. You can use the void* private_data field in the file structure parameter for this purpose. For example: file->private_data = (void*) 3. Check <linux/fs.h> for the details on struct file.

4.        Bounds on number of messages channels and message slots:

•      For each message slot file, assume that no more than 220 message channels will be used. This does not mean that the channel ids will be smaller than 220, just that you need to support at most 220 different ids.

•      If you use register_chrdev() to register your device, you can assume that minor numbers do not exceed 256 (i.e., there can be at most 256 different message slots device files. (This occurs because register_chrdev() limits the registered device to 256 minor number.) Otherwise, you can assume that minor numbers do not exceed 220, because Linux uses 20 bits to represent minor numbers.

5.        In the module’s struct file_operations, include the initialization

.owner = THIS_MODULE,

This will prevent the module from being unloaded while it is being used.

6.        You are responsible for defining the driver’s ioctl command, as shown in the recitation.

7.        Allocate memory using kmalloc() with GFP_KERNEL flag. (It is declared in <linux/slab.h>.)

8.        When unloaded, the module should free all memory that it allocated.

9.        Remember that processes aren’t trusted. Verify arguments to file operations and return -1 with errno set to EINVAL if the arguments are invalid. In particular, check the validity of user space buffers.

10.    You can assume that any invocation of the module’s operations (including loading/unloading) will run alone; i.e., there will not be concurrent system call invocations. This does not mean that there can’t be several processes that have the same message slot open or using the same channel; it just means that they won’t access the device concurrently.

3.2          Message sender
Implement the program in a file named message_sender.c.

Command line arguments:

•      argv[1]: message slot file path.

•      argv[2]: the target message channel id. Assume a non-negative integer.

•      argv[3]: the message to pass.

You should validate that the correct number of command line arguments is passed.

The flow:

1.    Open the specified message slot device file.

2.    Set the channel id to the id specified on the command line.

3.    Write the specified message to the message slot file. Don’t include the terminating null character of the C string as part of the message.

4.    Close the device.

5.    Exit the program with exit value 0.

If an error occurs in any of the above steps, print an appropriate error message (using strerror() or perror()) and exit the program with exit value 1.

3.3          Message reader
Implement the program in a file named message_reader.c.

Command line arguments:

•      argv[1]: message slot file path.

•      argv[2]: the target message channel id. Assume a non-negative integer.

You should validate that the correct number of command line arguments is passed.

The flow:

1.    Open the specified message slot device file.

2.    Set the channel id to the id specified on the command line.

3.    Read a message from the message slot file to a buffer.

4.    Close the device.

5.    Print the message to standard output (using the write() system call). Print only the message, without any additional text.

6.    Exit the program with exit value 0.

If an error occurs in any of the above steps, print an appropriate error message (using strerror() or perror()) and exit the program with exit value 1.

3.4          Example session
1.    As root (e.g., with sudo): Load (insmod) the message_slot.ko module.

2.    As root: Create a message slot file using mknod.

3.    As root: Change the message slot file’s permissions to make it readable and writable by your user.

4.    Invoke message_sender to send a message on some channel.

5.    Invoke message_reader to read the message on the same channel.

6.    Execute steps #4 and #5 several times, for different channels, in different sequences.

4            General guidelines
1.    Because your message sender takes the messages from the command line, it will only be able to send a C string as a message. This does not mean that the kernel module or message reader should only work with C string messages. The kernel module provides the general interface specified in Section 2, and any program—not necessarily yours—can use it.

2.    There’s no requirement for the message sender/reader to exit “cleanly” on error. These programs may terminate without freeing memory and closing file descriptors.


More products