Add the spinlock feature!

This commit is contained in:
ianchen0119
2021-06-17 23:00:55 +08:00
parent 6c78388205
commit ea5b0a654f
19 changed files with 935 additions and 0 deletions

39
06-Spinlock/Makefile Normal file
View File

@@ -0,0 +1,39 @@
CC = riscv64-unknown-elf-gcc
CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32
GDB = riscv64-unknown-elf-gdb
OBJ = \
start.s \
sys.s \
lib.c \
timer.c \
task.c \
os.c \
user.c \
trap.c \
lock.c
QEMU = qemu-system-riscv32
QFLAGS = -nographic -smp 4 -machine virt -bios none
OBJDUMP = riscv64-unknown-elf-objdump
all: os.elf
os.elf: $(OBJ)
$(CC) $(CFLAGS) -g -Wall -T os.ld -o os.elf $^
qemu: $(TARGET)
@qemu-system-riscv32 -M ? | grep virt >/dev/null || exit
@echo "Press Ctrl-A and then X to exit QEMU"
$(QEMU) $(QFLAGS) -kernel os.elf
clean:
rm -f *.elf
.PHONY : debug
debug: all
@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
@echo "-------------------------------------------------------"
@${QEMU} ${QFLAGS} -kernel os.elf -s -S &
@${GDB} os.elf -q -x ./gdbinit

133
06-Spinlock/README.md Normal file
View File

@@ -0,0 +1,133 @@
# 06-Spinlock
## Build & Run
```sh
IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/06-Spinlock (feat/spinlock)
$ make
riscv64-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c
IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/06-Spinlock (feat/spinlock)
$ make qemu
Press Ctrl-A and then X to exit QEMU
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf
OS start
OS: Activate next task
Task0: Created!
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
timer interruption!
timer_handler: 1
OS: Back to OS
OS: Activate next task
Task1: Created!
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
Task1: Running...
timer interruption!
timer_handler: 2
OS: Back to OS
OS: Activate next task
Task2: Created!
The value of shared_var is: 550
The value of shared_var is: 600
The value of shared_var is: 650
The value of shared_var is: 700
The value of shared_var is: 750
The value of shared_var is: 800
The value of shared_var is: 850
The value of shared_var is: 900
The value of shared_var is: 950
The value of shared_var is: 1000
The value of shared_var is: 1050
The value of shared_var is: 1100
The value of shared_var is: 1150
The value of shared_var is: 1200
The value of shared_var is: 1250
The value of shared_var is: 1300
The value of shared_var is: 1350
The value of shared_var is: 1400
The value of shared_var is: 1450
The value of shared_var is: 1500
The value of shared_var is: 1550
The value of shared_var is: 1600
timer interruption!
timer_handler: 3
OS: Back to OS
OS: Activate next task
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
Task0: Running...
timer interruption!
timer_handler: 4
OS: Back to OS
QEMU: Terminated
```
## Debug mode
```sh
make debug
riscv64-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c
Press Ctrl-C and then input 'quit' to exit GDB and QEMU
-------------------------------------------------------
Reading symbols from os.elf...
Breakpoint 1 at 0x80000000: file start.s, line 7.
0x00001000 in ?? ()
=> 0x00001000: 97 02 00 00 auipc t0,0x0
Thread 1 hit Breakpoint 1, _start () at start.s:7
7 csrr t0, mhartid # read current hart id
=> 0x80000000 <_start+0>: f3 22 40 f1 csrr t0,mhartid
(gdb)
```
### set the breakpoint
You can set the breakpoint in any c file:
```sh
(gdb) b trap.c:27
Breakpoint 2 at 0x80008f78: file trap.c, line 27.
(gdb)
```
As the example above, when process running on trap.c, line 27 (Timer Interrupt).
The process will be suspended automatically until you press the key `c` (continue) or `s` (step).

4
06-Spinlock/gdbinit Normal file
View File

@@ -0,0 +1,4 @@
set disassemble-next-line on
b _start
target remote : 1234
c

146
06-Spinlock/lib.c Normal file
View File

@@ -0,0 +1,146 @@
#include "lib.h"
void lib_delay(volatile int count)
{
count *= 50000;
while (count--);
}
int lib_putc(char ch) {
while ((*UART_LSR & UART_LSR_EMPTY_MASK) == 0);
return *UART_THR = ch;
}
void lib_puts(char *s) {
while (*s) lib_putc(*s++);
}
int lib_vsnprintf(char * out, size_t n, const char* s, va_list vl)
{
int format = 0;
int longarg = 0;
size_t pos = 0;
for( ; *s; s++) {
if (format) {
switch(*s) {
case 'l': {
longarg = 1;
break;
}
case 'p': {
longarg = 1;
if (out && pos < n) {
out[pos] = '0';
}
pos++;
if (out && pos < n) {
out[pos] = 'x';
}
pos++;
}
case 'x': {
long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
int hexdigits = 2*(longarg ? sizeof(long) : sizeof(int))-1;
for(int i = hexdigits; i >= 0; i--) {
int d = (num >> (4*i)) & 0xF;
if (out && pos < n) {
out[pos] = (d < 10 ? '0'+d : 'a'+d-10);
}
pos++;
}
longarg = 0;
format = 0;
break;
}
case 'd': {
long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
if (num < 0) {
num = -num;
if (out && pos < n) {
out[pos] = '-';
}
pos++;
}
long digits = 1;
for (long nn = num; nn /= 10; digits++)
;
for (int i = digits-1; i >= 0; i--) {
if (out && pos + i < n) {
out[pos + i] = '0' + (num % 10);
}
num /= 10;
}
pos += digits;
longarg = 0;
format = 0;
break;
}
case 's': {
const char* s2 = va_arg(vl, const char*);
while (*s2) {
if (out && pos < n) {
out[pos] = *s2;
}
pos++;
s2++;
}
longarg = 0;
format = 0;
break;
}
case 'c': {
if (out && pos < n) {
out[pos] = (char)va_arg(vl,int);
}
pos++;
longarg = 0;
format = 0;
break;
}
default:
break;
}
}
else if(*s == '%') {
format = 1;
}
else {
if (out && pos < n) {
out[pos] = *s;
}
pos++;
}
}
if (out && pos < n) {
out[pos] = 0;
}
else if (out && n) {
out[n-1] = 0;
}
return pos;
}
static char out_buf[1000]; // buffer for lib_vprintf()
int lib_vprintf(const char* s, va_list vl)
{
int res = lib_vsnprintf(NULL, -1, s, vl);
if (res+1 >= sizeof(out_buf)) {
lib_puts("error: lib_vprintf() output string size overflow\n");
while(1) {}
}
lib_vsnprintf(out_buf, res + 1, s, vl);
lib_puts(out_buf);
return res;
}
int lib_printf(const char* s, ...)
{
int res = 0;
va_list vl;
va_start(vl, s);
res = lib_vprintf(s, vl);
va_end(vl);
return res;
}

17
06-Spinlock/lib.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef __LIB_H__
#define __LIB_H__
#include "riscv.h"
#include <stddef.h>
#include <stdarg.h>
#define lib_error(...) { lib_printf(__VA_ARGS__); while(1) {} } }
extern void lib_delay(volatile int count);
extern int lib_putc(char ch);
extern void lib_puts(char *s);
extern int lib_printf(const char* s, ...);
extern int lib_vprintf(const char* s, va_list vl);
extern int lib_vsnprintf(char * out, size_t n, const char* s, va_list vl);
#endif

11
06-Spinlock/lock.c Normal file
View File

@@ -0,0 +1,11 @@
#include "os.h"
void spinlock_lock()
{
w_mstatus(r_mstatus() & ~MSTATUS_MIE);
}
void spinlock_unlock()
{
w_mstatus(r_mstatus() | MSTATUS_MIE);
}

32
06-Spinlock/os.c Normal file
View File

@@ -0,0 +1,32 @@
#include "os.h"
extern void trap_init(void);
void os_kernel()
{
task_os();
}
void os_start()
{
lib_puts("OS start\n");
user_init();
trap_init();
timer_init(); // start timer interrupt ...
}
int os_main(void)
{
os_start();
int current_task = 0;
while (1)
{
lib_puts("OS: Activate next task\n");
task_go(current_task);
lib_puts("OS: Back to OS\n");
current_task = (current_task + 1) % taskTop; // Round Robin Scheduling
lib_puts("\n");
}
return 0;
}

15
06-Spinlock/os.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef __OS_H__
#define __OS_H__
#include "riscv.h"
#include "lib.h"
#include "task.h"
#include "timer.h"
extern void user_init();
extern void os_kernel();
extern int os_main(void);
extern void spinlock_lock();
extern void spinlock_unlock();
#endif

46
06-Spinlock/os.ld Normal file
View File

@@ -0,0 +1,46 @@
OUTPUT_ARCH( "riscv" )
ENTRY( _start )
MEMORY
{
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
}
PHDRS
{
text PT_LOAD;
data PT_LOAD;
bss PT_LOAD;
}
SECTIONS
{
.text : {
PROVIDE(_text_start = .);
*(.text.init) *(.text .text.*)
PROVIDE(_text_end = .);
} >ram AT>ram :text
.rodata : {
PROVIDE(_rodata_start = .);
*(.rodata .rodata.*)
PROVIDE(_rodata_end = .);
} >ram AT>ram :text
.data : {
. = ALIGN(4096);
PROVIDE(_data_start = .);
*(.sdata .sdata.*) *(.data .data.*)
PROVIDE(_data_end = .);
} >ram AT>ram :data
.bss :{
PROVIDE(_bss_start = .);
*(.sbss .sbss.*) *(.bss .bss.*)
PROVIDE(_bss_end = .);
} >ram AT>ram :bss
PROVIDE(_memory_start = ORIGIN(ram));
PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
}

116
06-Spinlock/riscv.h Normal file
View File

@@ -0,0 +1,116 @@
#ifndef __RISCV_H__
#define __RISCV_H__
#include <stdint.h>
#define reg_t uint32_t // RISCV32: register is 32bits
// define reg_t as uint64_t // RISCV64: register is 64bits
// ref: https://www.activexperts.com/serial-port-component/tutorials/uart/
#define UART 0x10000000
#define UART_THR (uint8_t*)(UART+0x00) // THR:transmitter holding register
#define UART_LSR (uint8_t*)(UART+0x05) // LSR:line status register
#define UART_LSR_EMPTY_MASK 0x40 // LSR Bit 6: Transmitter empty; both the THR and LSR are empty
// Saved registers for kernel context switches.
struct context {
reg_t ra;
reg_t sp;
// callee-saved
reg_t s0;
reg_t s1;
reg_t s2;
reg_t s3;
reg_t s4;
reg_t s5;
reg_t s6;
reg_t s7;
reg_t s8;
reg_t s9;
reg_t s10;
reg_t s11;
};
// ref: https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/riscv.h
//
// local interrupt controller, which contains the timer.
// ================== Timer Interrput ====================
#define NCPU 8 // maximum number of CPUs
#define CLINT 0x2000000
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 4*(hartid))
#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot.
// which hart (core) is this?
static inline reg_t r_mhartid()
{
reg_t x;
asm volatile("csrr %0, mhartid" : "=r" (x) );
return x;
}
// Machine Status Register, mstatus
#define MSTATUS_MPP_MASK (3 << 11) // previous mode.
#define MSTATUS_MPP_M (3 << 11)
#define MSTATUS_MPP_S (1 << 11)
#define MSTATUS_MPP_U (0 << 11)
#define MSTATUS_MIE (1 << 3) // machine-mode interrupt enable.
static inline reg_t r_mstatus()
{
reg_t x;
asm volatile("csrr %0, mstatus" : "=r" (x) );
return x;
}
static inline void w_mstatus(reg_t x)
{
asm volatile("csrw mstatus, %0" : : "r" (x));
}
// machine exception program counter, holds the
// instruction address to which a return from
// exception will go.
static inline void w_mepc(reg_t x)
{
asm volatile("csrw mepc, %0" : : "r" (x));
}
static inline reg_t r_mepc()
{
reg_t x;
asm volatile("csrr %0, mepc" : "=r" (x));
return x;
}
// Machine Scratch register, for early trap handler
static inline void w_mscratch(reg_t x)
{
asm volatile("csrw mscratch, %0" : : "r" (x));
}
// Machine-mode interrupt vector
static inline void w_mtvec(reg_t x)
{
asm volatile("csrw mtvec, %0" : : "r" (x));
}
// Machine-mode Interrupt Enable
#define MIE_MEIE (1 << 11) // external
#define MIE_MTIE (1 << 7) // timer
#define MIE_MSIE (1 << 3) // software
static inline reg_t r_mie()
{
reg_t x;
asm volatile("csrr %0, mie" : "=r" (x) );
return x;
}
static inline void w_mie(reg_t x)
{
asm volatile("csrw mie, %0" : : "r" (x));
}
#endif

26
06-Spinlock/start.s Normal file
View File

@@ -0,0 +1,26 @@
.equ STACK_SIZE, 8192
.global _start
_start:
# setup stacks per hart
csrr t0, mhartid # read current hart id
slli t0, t0, 10 # shift left the hart id by 1024
la sp, stacks + STACK_SIZE # set the initial stack pointer
# to the end of the stack space
add sp, sp, t0 # move the current hart stack pointer
# to its place in the stack space
# park harts with id != 0
csrr a0, mhartid # read current hart id
bnez a0, park # if we're not on the hart 0
# we park the hart
j os_main # hart 0 jump to c
park:
wfi
j park
stacks:
.skip STACK_SIZE * 4 # allocate space for the harts stacks

8
06-Spinlock/sys.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef __SYS_H__
#define __SYS_H__
#include "riscv.h"
extern void sys_timer();
extern void sys_switch(struct context *ctx_old, struct context *ctx_new);
#endif

145
06-Spinlock/sys.s Normal file
View File

@@ -0,0 +1,145 @@
# This Code derived from xv6-riscv (64bit)
# -- https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/swtch.S
# ============ MACRO ==================
.macro ctx_save base
sw ra, 0(\base)
sw sp, 4(\base)
sw s0, 8(\base)
sw s1, 12(\base)
sw s2, 16(\base)
sw s3, 20(\base)
sw s4, 24(\base)
sw s5, 28(\base)
sw s6, 32(\base)
sw s7, 36(\base)
sw s8, 40(\base)
sw s9, 44(\base)
sw s10, 48(\base)
sw s11, 52(\base)
.endm
.macro ctx_load base
lw ra, 0(\base)
lw sp, 4(\base)
lw s0, 8(\base)
lw s1, 12(\base)
lw s2, 16(\base)
lw s3, 20(\base)
lw s4, 24(\base)
lw s5, 28(\base)
lw s6, 32(\base)
lw s7, 36(\base)
lw s8, 40(\base)
lw s9, 44(\base)
lw s10, 48(\base)
lw s11, 52(\base)
.endm
.macro reg_save base
# save the registers.
sw ra, 0(\base)
sw sp, 4(\base)
sw gp, 8(\base)
sw tp, 12(\base)
sw t0, 16(\base)
sw t1, 20(\base)
sw t2, 24(\base)
sw s0, 28(\base)
sw s1, 32(\base)
sw a0, 36(\base)
sw a1, 40(\base)
sw a2, 44(\base)
sw a3, 48(\base)
sw a4, 52(\base)
sw a5, 56(\base)
sw a6, 60(\base)
sw a7, 64(\base)
sw s2, 68(\base)
sw s3, 72(\base)
sw s4, 76(\base)
sw s5, 80(\base)
sw s6, 84(\base)
sw s7, 88(\base)
sw s8, 92(\base)
sw s9, 96(\base)
sw s10, 100(\base)
sw s11, 104(\base)
sw t3, 108(\base)
sw t4, 112(\base)
sw t5, 116(\base)
sw t6, 120(\base)
.endm
.macro reg_load base
# restore registers.
lw ra, 0(\base)
lw sp, 4(\base)
lw gp, 8(\base)
# not this, in case we moved CPUs: lw tp, 12(\base)
lw t0, 16(\base)
lw t1, 20(\base)
lw t2, 24(\base)
lw s0, 28(\base)
lw s1, 32(\base)
lw a0, 36(\base)
lw a1, 40(\base)
lw a2, 44(\base)
lw a3, 48(\base)
lw a4, 52(\base)
lw a5, 56(\base)
lw a6, 60(\base)
lw a7, 64(\base)
lw s2, 68(\base)
lw s3, 72(\base)
lw s4, 76(\base)
lw s5, 80(\base)
lw s6, 84(\base)
lw s7, 88(\base)
lw s8, 92(\base)
lw s9, 96(\base)
lw s10, 100(\base)
lw s11, 104(\base)
lw t3, 108(\base)
lw t4, 112(\base)
lw t5, 116(\base)
lw t6, 120(\base)
.endm
# ============ Macro END ==================
# Context switch
#
# void sys_switch(struct context *old, struct context *new);
#
# Save current registers in old. Load from new.
.globl sys_switch
.align 4
sys_switch:
ctx_save a0 # a0 => struct context *old
ctx_load a1 # a1 => struct context *new
ret # pc=ra; swtch to new task (new->ra)
.globl trap_vector
# the trap vector base address must always be aligned on a 4-byte boundary
.align 4
trap_vector:
# save context(registers).
csrrw t6, mscratch, t6 # swap t6 and mscratch
reg_save t6
csrw mscratch, t6
# call the C trap handler in trap.c
csrr a0, mepc
csrr a1, mcause
call trap_handler
# trap_handler will return the return address via a0.
csrw mepc, a0
# load context(registers).
csrr t6, mscratch
reg_load t6
mret

32
06-Spinlock/task.c Normal file
View File

@@ -0,0 +1,32 @@
#include "task.h"
#include "lib.h"
uint8_t task_stack[MAX_TASK][STACK_SIZE];
struct context ctx_os;
struct context ctx_tasks[MAX_TASK];
struct context *ctx_now;
int taskTop = 0; // total number of task
// create a new task
int task_create(void (*task)(void))
{
int i = taskTop++;
ctx_tasks[i].ra = (reg_t)task;
ctx_tasks[i].sp = (reg_t)&task_stack[i][STACK_SIZE - 1];
return i;
}
// switch to task[i]
void task_go(int i)
{
ctx_now = &ctx_tasks[i];
sys_switch(&ctx_os, &ctx_tasks[i]);
}
// switch back to os
void task_os()
{
struct context *ctx = ctx_now;
ctx_now = &ctx_os;
sys_switch(ctx, &ctx_os);
}

16
06-Spinlock/task.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef __TASK_H__
#define __TASK_H__
#include "riscv.h"
#include "sys.h"
#define MAX_TASK 10
#define STACK_SIZE 1024
extern int taskTop;
extern int task_create(void (*task)(void));
extern void task_go(int i);
extern void task_os();
#endif

38
06-Spinlock/timer.c Normal file
View File

@@ -0,0 +1,38 @@
#include "timer.h"
// a scratch area per CPU for machine-mode timer interrupts.
reg_t timer_scratch[NCPU][5];
#define interval 20000000 // cycles; about 2 second in qemu.
void timer_init()
{
// each CPU has a separate source of timer interrupts.
int id = r_mhartid();
// ask the CLINT for a timer interrupt.
// int interval = 1000000; // cycles; about 1/10th second in qemu.
*(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval;
// prepare information in scratch[] for timervec.
// scratch[0..2] : space for timervec to save registers.
// scratch[3] : address of CLINT MTIMECMP register.
// scratch[4] : desired interval (in cycles) between timer interrupts.
reg_t *scratch = &timer_scratch[id][0];
scratch[3] = CLINT_MTIMECMP(id);
scratch[4] = interval;
w_mscratch((reg_t)scratch);
// enable machine-mode timer interrupts.
w_mie(r_mie() | MIE_MTIE);
}
static int timer_count = 0;
void timer_handler()
{
lib_printf("timer_handler: %d\n", ++timer_count);
int id = r_mhartid();
*(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval;
}

12
06-Spinlock/timer.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __TIMER_H__
#define __TIMER_H__
#include "riscv.h"
#include "sys.h"
#include "lib.h"
#include "task.h"
extern void timer_handler();
extern void timer_init();
#endif

53
06-Spinlock/trap.c Normal file
View File

@@ -0,0 +1,53 @@
#include "os.h"
extern void trap_vector();
void trap_init()
{
// set the machine-mode trap handler.
w_mtvec((reg_t)trap_vector);
// enable machine-mode interrupts.
w_mstatus(r_mstatus() | MSTATUS_MIE);
}
reg_t trap_handler(reg_t epc, reg_t cause)
{
reg_t return_pc = epc;
reg_t cause_code = cause & 0xfff;
if (cause & 0x80000000)
{
/* Asynchronous trap - interrupt */
switch (cause_code)
{
case 3:
lib_puts("software interruption!\n");
break;
case 7:
lib_puts("timer interruption!\n");
// disable machine-mode timer interrupts.
w_mie(~((~r_mie()) | (1 << 7)));
timer_handler();
return_pc = (reg_t)&os_kernel;
// enable machine-mode timer interrupts.
w_mie(r_mie() | MIE_MTIE);
break;
case 11:
lib_puts("external interruption!\n");
break;
default:
lib_puts("unknown async exception!\n");
break;
}
}
else
{
/* Synchronous trap - exception */
lib_puts("Sync exceptions!\n");
while (1)
{
/* code */
}
}
return return_pc;
}

46
06-Spinlock/user.c Normal file
View File

@@ -0,0 +1,46 @@
#include "os.h"
int shared_var = 500;
void user_task0(void)
{
lib_puts("Task0: Created!\n");
while (1)
{
lib_puts("Task0: Running...\n");
lib_delay(1000);
}
}
void user_task1(void)
{
lib_puts("Task1: Created!\n");
while (1)
{
lib_puts("Task1: Running...\n");
lib_delay(1000);
}
}
void user_task2(void)
{
lib_puts("Task2: Created!\n");
while (1)
{
for (int i = 0; i < 50; i++)
{
spinlock_lock();
shared_var++;
spinlock_unlock();
}
lib_delay(6000);
lib_printf("The value of shared_var is: %d \n", shared_var);
}
}
void user_init()
{
task_create(&user_task0);
task_create(&user_task1);
task_create(&user_task2);
}