Table of Contents

<< Back to Writing Codezero Applications

Standalone L4 Applications

1.) Building a Codezero Service Using IPC

In this section you may find examples on how to create services that serves requests from potential applications using IPC.

In distributed computing, the core methodology involves creation of clients and servers using autogenerated source code stubs. Often, the methodology is complemented by object oriented programming mechanisms such as interfaces and implementations of objects.

In Codezero, client server communication is kept simple and lightweight. There is no mechanism to create autogenerated client and server stubs, as this methodology is known to create notoriously complicated and heavyweight implementations. Instead, any client server communication is created manually.

It is still possible to create stubs and leverage object-oriented programming, but this is not enforced as a methodology.

Handling Requests

On a typical Codezero service, request handling pattern involves the code snippet as below.

 
void handle_requests(void)
{
	/* Generic ipc data */
	u32 mr[MR_UNUSED_TOTAL];
	l4id_t senderid;
	struct tcb *sender;
	u32 tag;
	int ret;
 
        /* Receive request from any thread */
	if ((ret = l4_receive(L4_ANYTHREAD)) < 0)
               goto out_err;
 
	/* Read the tag that identifies request */
	tag = l4_get_tag();
 
        /* Read the sender id, set by the microkernel */
	senderid = l4_get_sender();
 
        /* Retrieve the information stored on the service about the sender */
	if (!(sender = find_task(senderid))) {
		l4_ipc_return(-ESRCH);
		return;
	}
 
	/* Read message megisters */
	for (int i = 0; i < MR_UNUSED_TOTAL; i++)
		mr[i] = read_mr(MR_UNUSED_START + i);
 
        /* Handle request according to the given tag */
        switch(tag) {
	case L4_IPC_REQUEST_NO_RETURN: {
		ret = handle_no_return_request(sender, (char *)mr[0], mr[1], mr[2]);
		if (ret < 0)
			break;	/* We only return for errors */
		else
			return; /* Otherwise we don't return, one way request. */
	}
	case L4_IPC_REQUEST_WITH_RETURN:
		ret = handle_returning_request(sender, (void *)mr[0]);
		break;
	default:
	}
 
	/* Send return message back to the client */
	if ((ret = l4_ipc_return(ret)) < 0) {
		printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, ret);
		BUG();
	}
 
out_err:
        printf("IPC Error occured: %d\n", err);
}
 
void main(void)
{
	/* Initialise service */
	initialise();
 
	while (1) {
		handle_requests();
	}
}

Operational Model

  1. After initialization, the server continuously asks for requests.
  2. A general server typically accepts requests from any thread on the system, using the L4_ANYTHREAD special value.
  3. Each IPC contains crucial information about the request such as the request tag and the sender id.
  4. The sender id is set by the microkernel in case the receiver service does not know where the message is coming from.
  5. The tag in the message identifies the type of request, and it is only relevant to userspace threads. It bears no significance for the microkernel itself.
  6. Both the tag and sender id are received on pre-allocated message registers, defined by the protocol between the service and the client. Typically these registers are defined by libl4.
  7. The rest of the message registers contain actual arguments about the request.
  8. A typical request involves a send and a receive phase. For instance, a client makes a system call by the send operation, and receives the return value of the system call through a receive. The return value is also returned in a pre-allocated message register, typically defined by libl4.
  9. On requests that do not require a return value (e.g. exit and execve system calls do not return) the service moves onto the next request without a send phase, after the first receive.

A note worth mentioning here is that the communication in this example is synchronous. In other words, both the client and the server tasks block during IPC. This may create complications in the cases where one of the parties involved in the IPC are buggy. For example, a service in its return phase may block indefinitely if the client does not adhere to the protocol and issue a receive.

Currently this is the model of communication, but such problems will be solved in the future releases with remedies such as multithreaded servers, for example.

<< Back to Writing Codezero Applications