This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Get Stack init info at runtime

Hi,

Is there any way to get information about stack and heap at runtime in a C function? I'm looking for a var/table/way to get Stack_Base, Heap_base, Stack_limit and heap_limit at runtime.

That's because I'm trying to make a very very small multitasking kernel for Cortex-M3 and I need somehow to know the Stack memory avail for my alloc routines.

Regards,

Christos Houtouridis,
Electrical Engineer

Parents
  • There are no ARMCC that magically place any initial stack or heap into RAM.

    There is a startup file, where _you_ specify stack size and heap size.

    Next thing - a task for small embedded processors are normally threads - not processes. So they don't allocate any memory for any global variables. All threads share the same global memory - each thread then have own stack space for auto variables.

    And you - who implement tasks - can implement solutions for thread-local storage, i.e. for a function call or a global pointer to point at memory reserved for this specific thread. And when you do a task-switch, you remap so another task asking for thread-local storage gets a pointer to different memory.

    Next thing: It doesn't matter what compiler you have. When _you_ implement this kernel, then _you_ need to implement a function similar to:

    create_thread(tread_function_ptr, stack_pointer,stack_size)
    


    and let stack_pointer point to a global block of RAM (statically or dynamically allocated).

    Or you can skip the parameter "stack_pointer" and just supply a "stack_size" parameter - and then let the kernel _you_ write perform the allocation.

    So turn it anyway around: It is still _you_, the user of your kernel, that must specify how much stack space each task needs. And you, the user of the kernel, who have to specify how large heap to have. Or implement a different solution instead of malloc()/new - especially since malloc() and new both suffer from potential memory fragmentation. Having 20kB free space on the heap doesn't mean that the 20kB are a single, continuous, block. It can just as well be 10 small 2kB blocks or one 10kB block and 100 blocks of 100 bytes each.

    So we are discussing design of kernel functionality just because it is _you_ (or a consultant you pay) who have to implement the functionality you are requesting. But only after you have first managed to set together a specification for how it is expected to function.

    A common way to have threads is to have:

    uint64_t main_stack[MAIN_STACK_SIZE/8];
    uint64_t io_stack[IO_STACK_SIZE/8];
    uint64_t alarm_stack[ALARM_STACK_SIZE/8];
    
    void main_task(void) {
        ...
    }
    
    void io_task(void) {
        ...
    }
    
    void alarm_task(void) {
        ...
    }
    
    void main(void) {
        os_init();
        os_start_task(main_task,main_stack,MAIN_STACK_SIZE);
        os_start_task(io_task,io_stack,IO_STACK_SIZE);
        os_start_task(alarm_task,alarm_stack,ALARM_STACK_SIZE);
        os_start_scheduling(); // Doesn't return - starts slicing time between available tasks.
    }
    

Reply
  • There are no ARMCC that magically place any initial stack or heap into RAM.

    There is a startup file, where _you_ specify stack size and heap size.

    Next thing - a task for small embedded processors are normally threads - not processes. So they don't allocate any memory for any global variables. All threads share the same global memory - each thread then have own stack space for auto variables.

    And you - who implement tasks - can implement solutions for thread-local storage, i.e. for a function call or a global pointer to point at memory reserved for this specific thread. And when you do a task-switch, you remap so another task asking for thread-local storage gets a pointer to different memory.

    Next thing: It doesn't matter what compiler you have. When _you_ implement this kernel, then _you_ need to implement a function similar to:

    create_thread(tread_function_ptr, stack_pointer,stack_size)
    


    and let stack_pointer point to a global block of RAM (statically or dynamically allocated).

    Or you can skip the parameter "stack_pointer" and just supply a "stack_size" parameter - and then let the kernel _you_ write perform the allocation.

    So turn it anyway around: It is still _you_, the user of your kernel, that must specify how much stack space each task needs. And you, the user of the kernel, who have to specify how large heap to have. Or implement a different solution instead of malloc()/new - especially since malloc() and new both suffer from potential memory fragmentation. Having 20kB free space on the heap doesn't mean that the 20kB are a single, continuous, block. It can just as well be 10 small 2kB blocks or one 10kB block and 100 blocks of 100 bytes each.

    So we are discussing design of kernel functionality just because it is _you_ (or a consultant you pay) who have to implement the functionality you are requesting. But only after you have first managed to set together a specification for how it is expected to function.

    A common way to have threads is to have:

    uint64_t main_stack[MAIN_STACK_SIZE/8];
    uint64_t io_stack[IO_STACK_SIZE/8];
    uint64_t alarm_stack[ALARM_STACK_SIZE/8];
    
    void main_task(void) {
        ...
    }
    
    void io_task(void) {
        ...
    }
    
    void alarm_task(void) {
        ...
    }
    
    void main(void) {
        os_init();
        os_start_task(main_task,main_stack,MAIN_STACK_SIZE);
        os_start_task(io_task,io_stack,IO_STACK_SIZE);
        os_start_task(alarm_task,alarm_stack,ALARM_STACK_SIZE);
        os_start_scheduling(); // Doesn't return - starts slicing time between available tasks.
    }
    

Children
  • Dear Per,

    For start I have to say that I agree with both of your messages. It is exactly how you describe it.
    Now. You said
    "There is a startup file, where _you_ specify stack size and heap size"

    And this is the reason I need to pass this _user_demand_ to my kernel. I dont want to specify the avail RAM sizes in two places both the startupxxx.s and in some xxconfig.h header. I need to know how much memory the _user_ (probably me again) decide to left for stack/heap in the startupxxx.s. So my kernel will read this information and do its magic. That's why I ask for a way to read the Stack_base etc...(the labels used in the startup file).

    Of course there are ways like "read the SP just after the system init" or "do a search using exception handler", or even "do specify eventually RAM sizes and addresses in two places", but I seek for a much more elegant way.

    I have to say that this is not the first time i make my own kernel. I have made one for x86. It is just the first time that i build something so freaking small. And I will make it a small module to use it beside any "my-kernel-compatible" project.

    Regards,

    Christos Houtouridis

  • Again - you don't seem to have read the above comments very well - there is no real advantage in having a configuration indicating the amount of memory avail to the kernel. Now rewind, read this sentence once again (exit infinite loop). You don't need this if you allocate all the memory you need in compile time - end of discussion. What is the problem?

  • Dear Tamir,

    There is no problem with that. I just want to avoid the "compile time alloc" thing. Of-course if I can not find a solution i'll use something like that

    uint8_t kstack_privil[KERNEL_PRIVILEGED_STACK_SIZE];
    uint8_t kstack_unprivil[KERNEL_UNPRIVILEGED_STACK_SIZE];
    

    And I'll make the allocator to give stack slices from these tables.

    Christos.

  • You can always allocate memory from the heap. But remember that systems that are built using such a kernel will require the have heap space allocated as well.