Zephyr -- Mutex

Mutex Zephyr Mutex有下面几个重要特性: lock count。该线程lock了mutux的次数。为0的时候表示unlock。 owning thread。 当一个线程拥有锁,其他线程在都在等待锁释放。一旦锁释放后,等待时间最长优先级最高的线程将被调度。 Zephyr中ISRs中不允许使用Mutex。 Reetrant Locking Zephyr Mutex是可重入锁,可多次加锁。 这样拥有锁的线程可以访问相关资源,不用管是否上锁。 可重入锁的目的就是防止死锁,导致同一个线程不可重入上锁代码段,目的就是让同一个线程可以重新进入上锁代码段。 Priority Inheritance 优先级继承。如果当前线程拥有锁,此时来了一个优先级更高的线程想获取锁,该线程会开始block等待。 此时系统会把拥有锁的线程优先级提高到和等待的线程相同,来尽快完成临界区的任务。一旦释放了锁,线程优先级又会调回去。 CONFIG_PRIORITY_CEILING用来配置最高提高到的优先级等级,默认为0为无限制。 Implementation Defining a Mutex struct k_mutex my_mutex; k_mutex_init(&my_mutex); //或 K_MUTEX_DEFINE(my_mutex); Locking a Mutex k_mutex_lock(&my_mutex, K_FOREVER); // 无限阻塞等待获取锁 if (k_mutex_lock(&my_mutex, K_MSEC(100)) == 0) { // 等待100ms,没获取到返回 /* mutex successfully locked */ } else { printf("Cannot lock XYZ display\n"); } Unlocking a Mutex k_mutex_unlock(&my_mutex); Futex Futex(fast userspace mutex)是比Mutex更轻量级的互斥访问原语。是一种用于用户空间应用程序的通用同步工具。 int k_futex_wait(struct k_futex *futex, int expected, k_timeout_t timeout) int k_futex_wake(struct k_futex *futex, bool wake_all) API Reference User Space Mutex API 把k_xxx前缀换成sys_xxx,比如sys_mutex_lock()。...

2024-04-11 · 3 min

Zephyr -- Threads

Threads CONFIG_MULTITHREADING打开Zephyr多线程功能。 Zephyr线程有下面几个关键的特性: Stack area。 线程的栈大小可修改。 thread control block k_thread。用来保存线程的一些metadata。 entry point function。线程开始执行运行的函数。 scheduling priority。支持配置调度优先级。 thread option。提供线程的一些特殊配置。 execution mode。Supervisor/User mode。依赖于CONFIG_USERSPACE。 Lifecycle k_thread_create(): 创建线程。 k_thread_join(): 阻塞等待线程终止。 k_thread_abort(): 发生异常情况,线程可以由自己或其他线程来终结。 k_thread_suspend(), k_thread_resume(): 线程suspend后只有通过resume才能重新调度。 Thread States Thread Stack objects 初始化线程栈相关属性,如果线程只在内核运行,用K_KERNEL_STACK_XXX。 如果是user space线程,用K_THREAD_STACK_XXX。 如果CONFIG_USERSPACE没打开,那么K_THREAD_STACK等于K_KERNEL_STACK。 Thread Priorities 优先级数字越小,优先级越高。 cooperative thread可配置的优先级为 -CONFIG_NUM_COOP_PRIORITIES到-1。 preemptible thread可配置的优先级为0到 (CONFIG_NUM_PREEMPT_PRIORITIES-1)。 可见cooperative thread的优先级肯定比preemptible thread高。 Cooperative thread是需要主动交出CPU控制权的,否则会一直执行该线程。 Preemptible thread是根据时间片轮转调度的,会自动切换线程。 Meta-IRQ Priorities // TODO: APIs 创建线程: 方法1: k_tid_t k_thread_create(struct k_thread *new_thread, k_thread_stack_t *stack, size_t stack_size, k_thread_entry_t entry, void *p1, void *p2, void *p3, int prio, uint32_t options, k_timeout_t delay) 线程栈必须使用 K_THREAD_STACK_DEFINE or K_KERNEL_STACK_DEFINE定义。 线程栈大小必须是传入给K_THREAD_STACK or K_KERNEL_STACK宏的大小。或者利用K_THREAD_STACK_SIZEOF()/K_KERNEL_STACK_SIZEOF(),对用K_THREAD_STACK/K_KERNEL_STACK创建线程返回的结构体。 e....

2024-04-11 · 2 min

Operating Systems Three Easy Pieces(OSTEP) - Concurrency

Chapter26 Concurrency: Introduction 线程和进程类似,但多线程程序,共享 address space 和 data。 多线程各自独享寄存器组,切换进程的时候需要上下文切换。 进程上下文切换的时候把上一个进程的寄存器组保存到 PCB(Process Control Block)中,线程切换需要定义一个或更多新的结构体 TCBs(Thread Control Blocks)来保存每个线程的状态。 线程切换 Page table 不用切换,因为线程共享地址空间。 线程拥有自己的栈。 26.1 Why use threads? Parallelism。单线程程序只能利用一个 CPU 核,多线程程序可以并行利用多个 CPU 核,提高效率。 防止 I/O 操作导致程序 block。即使是单核 CPU,多线程程序可以在一个线程执行 I/O 操作的时候,其他线程继续利用 CPU。 如果是 CPU 密集型工作,单核 CPU 跑多线程对效率提升不大 26.2~26.4 Problem of Shared data 创建两个线程,分别对全局变量 counter 加 1e7, 最后结果会不等于 2e7。 #include <stdio.h> #include <pthread.h> #include "common.h" #include "common_threads.h" static volatile int counter = 0; void *mythread(void *arg) { printf("%s: begin\n", (char *) arg); int i; for (i = 0; i < 1e7; i++) counter = counter + 1; printf("%s: done\n", (char *) arg); return NULL; } int main(int argc, char *argv[]) { pthread_t p1, p2; printf("main: begin (counter = %d)\n", counter); Pthread_create(&p1, NULL, mythread, "A"); Pthread_create(&p2, NULL, mythread, "B"); // join waits for the threads to finish Pthread_join(p1, NULL); Pthread_join(p2, NULL); printf("main: done with both (counter = %d)\n", counter); return 0; } pthread_create: 创建线程。 pthread_join: 阻塞等待线程终止。...

2024-04-01 · 12 min

USB_Reset_Suspend_Resume

Reset 整个reset过程,host需要拉低D+的时间\(T_{DRST}\) 至少 > 10ms。 High-speed capable hubs and devices在reset过程中还会执行一套reset protocol: Hub确认接的设备不是Low-speed, 如果是low-speed,不会有下面的reset握手协议。 Hub把D+拉低,进入SE0状态。 Device检测到SE0: 如果device是从suspend状态进入reset,那么device在不少于2.5us后进行high-speed detection handshake。 如果device是从non-suspend full-speed状态进入reset,那么device需要在2.5us~3ms之间进入handshake。 如果device是从non-suspend high-speed状态进入reset,那么device必须在3ms~3.125ms之间恢复full-spped,通过断开高速终端电阻以及重新接上D+的上拉电阻。接着在100us-875us内开始检测SE0状态,进入handshake。 Device往D-灌电流,形成一个800mv左右的ChripK信号, 该信号保持1~7ms。 Hub检测到ChripK信号后,100us内需要发送三对KJ(k-J-K-J-K-J)。每个单独的J或K都要保持40~60us。KJ对打完之后,在500us内断开D+上拉电阻,使能高速终端电阻,进入高速状态。 Suspend Device有两种进入suspend的方式: 如果连续3帧没有收到SOF包(1ms一帧),device会进入suspend状态。 host driver发送SET_PORT_FEATURE(PORT_SUSPEND)给hub,hub再发送给指定的device。 Resume 从suspend状态resume起来需要保持ChripK状态20ms以上,也有两种方式: Device remote wakeup,device drive K-state 1~15ms。Hub会在1ms内,接管D+, D-,继续产生resume信号直到20ms。 host发送CLEAR_PORT_FEATURE( PORT_SUSPSEND)给hub,hub drive K-state 20ms。 Reference Reset, Suspend, and Resume Commands USB全速设备的挂起、唤醒Resume USB2.0设备从全速模式到高速模式的识别过程及速率协商 USB spec 7.1.7.5~7.1.7.7, 11.9

2024-03-29 · 1 min

Operating Systems Three Easy Pieces(OSTEP) - Memory Virtualization

Chapter14 Memory API 14.2 The malloc() call 分配字符串长度的时候要注意:malloc(strlen(s) + 1),多分配一个byte给\0。strlen函数计算长度不会统计\0。 14.4 Common Errors Not Allocating Enough Memory char *src = "hello"; char *dst = (char *) malloc(strlen(src)); // wrong!!! char *dst = (char *) malloc(strlen(src) + 1); strcpy(dst, src); // work properly strcpy拷贝的时候会把\0也拷贝过去,所以分配长度的时候要strlen()+1。 否则多拷贝的一个byte,会写到heap的其他变量所属的地址,可能会导致fault。 Forgetting to Initialize Allocated Memory malloc分配出来的内存,需要初始化,否则可能是随机内存值。 Forgetting To Free Memory malloc分配出来的内存需要free掉。不然长时间运行的进程会导致内存泄漏。 短时间运行就结束的进程可能不会有这个问题,在进程结束后系统会自动回收内存,但free掉不使用的内存仍然是一个好习惯。 Freeing Memory Before You Are Done With It Freeing Memory Repeatedly Chapter15 Address Translation 地址转换 Chapter16 Segmentation 分段 16....

2024-03-27 · 1 min

Operating Systems Three Easy Pieces(OSTEP) - CPU Virtualization

这篇文章是阅读 OSTEP 3~11 的笔记,主要讲的是 Virtualization 中 Virtualize CPU 的部分。 Chapter4 Process 4.2 Process API 一般 OS 会提供以下的进程 API 来操作进程: Create: 创建进程。 Destroy: 结束进程。 Wait: Wait a process to stop running. 等待进程结束。 Miscellaneous Control: Suspend/Resume… 休眠,唤醒等等。 Status: 查看进程状态。 4.3 Process Creation 首先 OS 将存储在 disk or SSD 的 program 程序加载进 memory 内存。 这边有两种方式,一种是在运行前把 code 和 static data 全部加载进内存。现代操作系统一般会使用第二种方式,懒加载,只加载即将使用的 code 和 data。 分配栈。 分配堆。 分配三个文件描述符,标准输入 0,标准输出 1,错误 2。 4.4 Process Status 进程的状态有:...

2024-03-21 · 5 min

xv6_chapter4 Traps

Lecture gdb调试shell write函数的syscall过程: (gdb) b *0xdec # 在0xde8地址设置断点 (gdb) c (gdb) delete 1 # 删除断点 (gdb) print $pc $1 = (void (*)()) 0xdec (gdb) info r (gdb) x/3i 0xde8 # 打印0xdfe开始的三条指令 0xdfe: li a7,16 0xe00: ecall 0xe04: ret (gdb) p/x $stvec $2 = 0x3ffffff000 # user space virtual address顶部一个page,trampoline page对应kernel trap handler. (gdb) stepi warning Book Traps: system call. 通过ecall进入kernel exception. 除0,invalid virtual address. interrupt. 进入kernel device driver. 根据处理代码不同,可分为三种traps: traps from user space traps from kernel space timer interrupts 4....

2024-02-26 · 1 min

xv6 calling conventions and stack frames RISC-V

caller: not preserved across fn call. 需要调用函数来保存寄存器。参考下面例子中的ra寄存器值。 callee: preserved across fn call. 被调用函数来保存寄存器。 根据CSAPP 3.7.1栈结构看,return address属于前一栈帧保存的,所以fp register应该指向return address的地址,即当前栈帧的顶部。一个栈帧中保存在最高位地址的是previous fp。所以这里最上面一个stack frame也应该把return address去掉。 sum_to: mv t0, a0 li a0, 0 loop: add a0, a0, t0 addi t0, t0, -1 bnez t0, loop ret // 返回到li t0, 2 sum_then_double: addi sp, sp, -16; // 分配栈空间 sd ra, 0(sp) // ra的值存入sp+0地址。caller保存sum_the_double执行完的返回地址 call sum_to // 这里调用call,ra的值被设置为下一条指令地址,即li t0, 2 li t0, 2 mul a0, a0, t0 ld ra, 0(sp) // 恢复sum_them_double的返回地址 addi sp, sp, 16 ret

2024-02-25 · 1 min

Zephyr -- Porting Guide

SoC Porting Guide https://docs.zephyrproject.org/latest/hardware/porting/soc_porting.html Board Porting Guide https://docs.zephyrproject.org/latest/hardware/porting/board_porting.html west manifests 仓库 west.yml reference: https://docs.zephyrproject.org/latest/develop/west/manifest.html

2024-02-01 · 1 min

xv6 Misc

环境安装 https://pdos.csail.mit.edu/6.828/2023/tools.html $ sudo apt-get update && sudo apt-get upgrade sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu 测试环境: $ riscv64-unknown-elf-gcc --version $ qemu-system-riscv64 --version 在 ubuntu22.04 上的输出 log 为: ~ ❯ qemu-system-riscv64 --version QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24) Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers ~ ❯ riscv64-linux-gnu-gcc --version riscv64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions....

2024-02-01 · 1 min