This implies that you will not be able to get away with a small pool of message structures. You will need lots of message structures and some way to allocate them dynamically. Finally, you need to make sure that if a process can't find a free buffer, the system makes it wait.
You can satisfy all these requirements by using the message passing primitives themselves to allocate messages. To create a pool of dynamically allocated messages, declare an array of messages to be allocated and a mailbox for allocating them. For now, let us call the mailbox "alloc". As part of your system initialization, execute a loop that sends all the messages in your array to "alloc". Don't worry about the values sent. All you are really doing is putting the messages in a queue. Then, when a process needs a free message, have it "receive" one from "alloc". Similarly, when it is finished with a message, it can make it available again by "send"ing the message to "alloc". Note that if no messages are free, processes will automatically be suspended waiting for the empty but essential messages they receive from "alloc".
One pitfall to avoid when using this scheme is deadlock. If all the processes share one pool of buffers it will most likely be possible to end up in a state where no free messages can be obtained until some process gets one so that it can complete a task that will allow it to free several. This can be avoided by carefully using separate pools of buffers for separate tasks.
Another problem with this scheme is that if you handle a lot of messages this way, the time required to initialize your system may become large. If it becomes too large, characters will start arriving from your terminals before you are ready to handle them. To cover this possibility, you should consider a) setting up a separate process to send all the messages to the "free message" mailboxes and b) filling your "free message" mailboxes in parallel rather than in series.