1. Processes

A process contains one or more tasks. It holds information that is common for all of its tasks. Each process has a main task and can have multiple child tasks.

A process itself has no id, it is identified by the id of its main task.

2. Tasks

A task is a single stream of execution. g_task is the data structure that contains information for a task.

Tasks running in kernel-level only have one stack, user-space tasks have an additional stack that is used during interrupt handling. The stack section describes how these stacks are handled.

2.1. Types

The following constants of type g_task_type denote types of tasks.

Identifier Description

G_TASK_TYPE_DEFAULT

Normal task

G_TASK_TYPE_VM86

Virtual 8086 task

2.2. Kernel-level

2.2.1. Creating a task

In kernel space, a task can be created by using taskingCreateTask and assigning it to scheduling with taskingAssign.

2.2.2. Considerations

There are some things to consider when implementing a task in kernel-level. When a kernel-lock is taken by a task, scheduling is disabled until the task releases the lock. This is inevitable as it could cause a deadlock if the task is interrupted while holding a lock.

To minimize any blocking, locks should therefore be used around critical parts but acquired as late and released as soon as possible.

2.2.3. Exiting a task

A kernel task must be exited using taskingExit, otherwise the task will run into nirvana and cause a failure.

2.3. User-level

2.3.1. Creating a task

To create a task from userspace, the g_create_task API function is used. This function must be supplied with an address pointing to code that the task shall execute. Additionally to the entry point, a pointer to user data can be passed.

A simple example could look like this:

void myTaskCounter(int* counter) {
	for(;;) {
		(*counter)++;
	}
}

int main(int argc, char** argv) {
	int counter = 0;
	g_create_task_d(myTaskCounter, &counter);

	for(;;) {
		g_sleep(1000);
		printf("The counter is: %i\n", counter);
	}
}

Each task has a unique id of type g_tid. This id can be obtained from within the task using g_get_tid.

2.3.2. Internal initialization

The task initialization sequence uses a wrapper function for starting the task. First, the G_SYSCALL_CREATE_TASK system call is called and supplied with a "task setup routine" and the "user entry" (the function given to g_create_task).

This setup routine is the first piece of code that is executed from within the newly created tasnk, but does initially not know where to start execution. To get this information, it uses G_SYSCALL_GET_TASK_ENTRY which returns the user entry to start executing at.

This entire procedure is done to avoid that the kernel must modify the user stack to put the necessary values on it, but rather do it from within code which is inherently ABI compatible.

2.3.3. Exiting a task

A task is automatically destroyed once the entry function finishes execution. To stop execution from an arbitrary point, the g_exit API function can be used.

A single task can not be killed from a different process; using the g_kill always causes the entire process to exit.

When the main task of a process dies, all tasks of the process are killed.

2.4. Security Levels

When creating a process, a security level is used to determine what permissions the tasks of the process have. This security level is stored on the process and applied on task creation.

2.4.1. Existing security levels

The following constants of type g_security_level denote the different security levels.

Identifier Description

G_SECURITY_LEVEL_APPLICATION

Has no specific permissions.

G_SECURITY_LEVEL_DRIVER

Has I/O permission level 3 (may use out and in commands to access the CPU I/O ports) and is permitted to use a variety of driver-related system calls.

G_SECURITY_LEVEL_KERNEL

Runs in kernel space