Miosix Processes Quick Start: Difference between revisions
No edit summary |
No edit summary |
||
| Line 2: | Line 2: | ||
In the quick start you just followed, you used Miosix as a unikernel, thus writing your application in kernelspace. To use Miosix as a fluid kernel, we'll see how we can move part of our hello world example application in a userspace process. | In the quick start you just followed, you used Miosix as a unikernel, thus writing your application in kernelspace. To use Miosix as a fluid kernel, we'll see how we can move part of our hello world example application in a userspace process. | ||
In this tutorial we'll break the previous example program that blinked an LED and printed ''Hello world'' in two parts: we'll blink the LED in kernelspace form a kernel thread, while we'll print ''Hello world'' in userspace form a proccess. | |||
= Starting from a template = | = Starting from a template = | ||
Revision as of 21:48, 17 May 2026
This tutorial assumes you just completed the quick start guide either for Linux, Windows or MacOS. The screenshots in this tutorial are taken from a Linux computer, but the procedure is the same regardless of your host OS.
In the quick start you just followed, you used Miosix as a unikernel, thus writing your application in kernelspace. To use Miosix as a fluid kernel, we'll see how we can move part of our hello world example application in a userspace process.
In this tutorial we'll break the previous example program that blinked an LED and printed Hello world in two parts: we'll blink the LED in kernelspace form a kernel thread, while we'll print Hello world in userspace form a proccess.
Starting from a template
To keep this tutorial simple, also in this case we'll start from a template project. More details on the theory of what we're doing can be found in the fluid kernel page, while a more detailed description of the content of the template files can be found in the Miosix processes page.
In a directory of your choice, create a hello_processes directory and download a copy of the Miosix kernel as a subdirectory using git. As a refresher, on Linux that would be:
mkdir hello_processes
cd hello_processes
git clone https://github.com/fedetft/miosix-kernel.git
This time, however, we won't use the perl script init_project_out_of_git_tree.pl and instead manually copy the content of the miosix-kernel/templates/processes in our project directory. Then, we'll copy the directory (this time not the content of, but the entire directory) miosix-kernel/miosix/config again in our project directory.
The result should look like this:
Since this time we have not used the perl script, we'll have to fix the Makefile paths manually, so it can find the kernel and configuration properly. Open the Makefile and replace the lines:
KPATH := ../../miosix
CONFPATH := $(KPATH)
to the following value:
KPATH := miosix-kernel/miosix
CONFPATH := .
We should also edit the CMakeLists.txt, but since in this tutorial we'll use the Makefile build system, we won't bother. Information on how to do so can be found in the Miosix and git workflow and CMake pages.
Configuring the kernel
Just like in the previous tutorial we'll have to select a board in config/Makefile.inc:
...
#OPT_BOARD := stm32f469ni_stm32f469i-disco
OPT_BOARD := stm32f746zg_nucleo
#OPT_BOARD := stm32f765ii_marco_ram_board
...
But this time, there are two more configuration steps to perform. First, we'll need to compile the kernel with the filesystem and processes support enabled.
Open the config/miosix_settings.h and uncomment the following lines:
#define WITH_FILESYSTEM
#define WITH_PROCESSES
Note that the process loader depends on filesystem support, so the filesystem is a dependency of process support.
There's one more step: select an appropriate linker script. This is done in the board-specific Makefile.inc file. This can be found in config/board/<your board name>/Makefile.inc. Since this tutorial is based on the stm32f746zg_nucleo board, that would be config/board/stm32f746zg_nucleo/Makefile.inc. In this file, you should uncomment whatver linker script is selected, and uncomment the linker script whose name ends with processes:
## Select linker script
#LINKER_SCRIPT := unikernel.ld
LINKER_SCRIPT := processes.ld
Note that some boards (those with an external off-chip RAM), may have multiple linker scripts that end with processes. For this tutorial, any of them will work. Some boards may not have any linker script option with processes. This usually happens because the board has too little FLASH or RAM to support processes. The minimum required memory to use processes is 128KB of FLASH and 32KB of RAM, even though at least twice that amount is recommended.
Modifying the kernelspace main()
The main.cpp that we copied from the template application should look like this:
#include <cstdio>
#include <sys/wait.h>
#include <signal.h>
#include <spawn.h>
using namespace std;
int main()
{
pid_t pid;
const char *arg[] = { "/bin/hello", nullptr };
const char *env[] = { nullptr };
int ec=posix_spawn(&pid,arg[0],NULL,NULL,(char* const*)arg,(char* const*)env);
if(ec!=0)
{
iprintf("spawn returned %d\n",ec);
return 1;
}
pid=wait(&ec);
iprintf("pid %d exited\n",pid);
if(WIFEXITED(ec))
{
iprintf("Exit code is %d\n",WEXITSTATUS(ec));
} else if(WIFSIGNALED(ec)) {
if(WTERMSIG(ec)==SIGSEGV) iprintf("Process segfaulted\n");
else iprintf("Process terminated due to an error\n");
}
return 0;
}
This is a generic code to spawn a process that should work on any POSIX-compliant OS, including Linux and MacOS, except for the use of iprintf instead of printf to save code size.
However, in this tutorial, we also want to run a kernelspace application that blinks an LED. To do so while main() spawns and waits for the process, we'll use a thread.
Modify the main() as follows:
#include <cstdio>
#include <sys/wait.h>
#include <signal.h>
#include <spawn.h>
#include <thread>
#include <interfaces/bsp.h>
using namespace std;
void blinker()
{
for(;;)
{
this_thread::sleep_for(500ms);
miosix::ledOn();
this_thread::sleep_for(500ms);
miosix::ledOff();
}
}
int main()
{
thread t(blinker);
t.detach();
pid_t pid;
const char *arg[] = { "/bin/hello", nullptr };
const char *env[] = { nullptr };
int ec=posix_spawn(&pid,arg[0],NULL,NULL,(char* const*)arg,(char* const*)env);
if(ec!=0)
{
iprintf("spawn returned %d\n",ec);
return 1;
}
pid=wait(&ec);
iprintf("pid %d exited\n",pid);
if(WIFEXITED(ec))
{
iprintf("Exit code is %d\n",WEXITSTATUS(ec));
} else if(WIFSIGNALED(ec)) {
if(WTERMSIG(ec)==SIGSEGV) iprintf("Process segfaulted\n");
else iprintf("Process terminated due to an error\n");
}
return 0;
}
Modifying the userspace main()
The userspace main() is the process_template/main.cpp file. Open it and replace its content with:
#include <cstdio>
#include <thread>
using namespace std;
int main()
{
for(;;)
{
this_thread::sleep_for(2s);
iprintf("Hello world\n");
}
}
Compiling and flashing
Just like in the previous tutorial, compile with make and flash using make program on Linux or with the chip vendor's flashing tool on Windows. Note that this time you'll need to flash image.bin and not main.bin.
You should see the LED blink and the Hello world being printed on the serial port.
