水手勇敢

  • 2025-02-04
  • 发表了日志: 《Linux内核深度解析》-文件系统简单介绍的学习

  • 发表了主题帖: 《Linux内核深度解析》-文件系统简单介绍的学习

         一、文件系统架构      1、Linux文件系统的架构如下图,分为用户空间、内核空间和硬件3个部分。   用户空间层面        应用程序使用glibc库封装的标准I/O流函数访问文件。分别使用fopen(打开)、fclose(关闭)、fread(关闭)、fwrite(写)、fseek(设置文件偏移)等。 硬件层面         外部存储设备分别为块设备、闪存、NVDIMM设备。 内核空间层面         由于内核支持多种文件系统类型,为了给用户提供统一的操作接口,内核实现一个抽象层,虚拟文件系统(VFS)。文件系统分为以下4种: 块设备文件系统,机械硬盘和固态硬盘等块设备。常用的是文件系统EXT和btrfs。 闪存文件系统,存储设备为NAND闪存和NOR闪存。常用的是JFFS2、UBIFS。 内存文件系统,常用的是tmpfs。 伪文件系统,是为了使用虚拟文件系统的编程接口。常用的是sockfs、proc、sysfs、hugetlbfs、cgroup等。        二、虚拟文件系统的数据结构        虚拟文件系统定义一套统一的数据结构。 超级块。文件系统的开始部分是超级块,描述文件系统的总体信息,挂载文件系统时,内存中创建结构体super_block。 VFS在内存中把目录组织为一棵树,进程根据树目录访问对应的文件系统。 根据文件系统的超级块格式,注册文件系统类型file_system_type. 索引节点。每个文件对应一个索引节点,每个索引节点有唯一编号。当内核访问存储设备上的文件时,会在内存中创建索引节点的结构体inode。 目录项。当内核访问存储设备上的一个目录项时,会在内存中创建该目录项的一个副本:结构体dentry。 当进程打开一个文件的时候,VFS就会创建文件的一个file结构体,然后为文件表中分配一个文件描述符,最后把文件描述符和file结构体的映射添加到打开文件表中。

  • 2025-02-01
  • 回复了主题帖: 《Linux内核深度解析》-进程状态学习

    秦天qintian0303 发表于 2025-2-1 08:09 过年都不休息啊,大家都好好卷啊    早上起早写的,你看7点多就写完了。

  • 发表了主题帖: 《Linux内核深度解析》-进程状态学习

    进程是在 CPU 及内存中运行的程序代码,而每个进程可以创建一个或多个进程(父子进程)。Linux有以下几种进程状态,分别是: (1)就绪:进程描述符state是TASK_RUNNING,正在运行队列中等待调度器调度。 (2)运行:进程描述符state是TASK_RUNNING,被调度器选中,正在处理器上运行。 (3)轻度睡眠:进程描述符state是TASK_INTERRUPTIBLE,可以被信号打断。 (4)中度睡眠:进程描述符state是TASK_KILLABLE,只能被致命的信号打断。 (5)深度睡眠:进程描述符的字段state是TASK_UNINTERRUPTIBLE,不能被信号打断。 (6)僵尸:进程描述符state是TASKDEAD,字段exit_state是EXIT_ZOMBIE。如果父进程关注子进程退出事件,那么子进程在退出时发送SIGCHLD信号通知父进程,变成僵尸进程,父进程在查询子进程的终止原因后回收子进程的进程描述符。 (7)死亡:进程描述符的字段state是TASK_DEAD,如果父进程不关注子进程退出事件,那么子进程退出时自动消亡。 如图所示: 客户端进程查看脚本为: ps aux 下图为centos运行的进程查看图。   Linux对进程状态的代码如下: /* * The task state array is a strange "bitmap" of * reasons to sleep. Thus "running" is zero, and * you can test for combinations of others with * simple bit tests. */ static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };  

  • 2025-01-31
  • 发表了日志: 《Linux内核深度解析》-系统调用学习

  • 发表了主题帖: 《Linux内核深度解析》-系统调用学习

    一、系统调用         系统调用是内核给用户程序提供的编程接口。用户程序可使用glibc库对单个系统提供的函数,或使用syscall ( ):。系统调用fork()为例: SYSCALL_DEFINE0(fork) //展开后为 asmlinkage long sys_fork(void) { #ifdef CONFIG_MMU return _do_fork(SIGCHLD,0,0,NULL,NULL,0) #else Return -EINVAL; #endif }             需要在系统调用表中保存系统调用号和处理函数的映射关系,sys_call_table如下: /source/arch/x86/kernel/syscall_64.c asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { /* * Smells like a compiler bug -- it doesn't work * when the & below is removed.*/ [0 ... __NR_syscall_max] = &sys_ni_syscall, #include <asm/syscalls_64.h> }; 二、执行系统调用 系统调用划分到同步异常,在异常级别1的异常向量表中,64位调用入口为el0_sync函数。 el0_sync: kernel_entry 0 mrs x25, esr_el1 // 读异常情况寄存器 lsr x24, x25, #ESR_ELx_EC_SHIFT // 判断异常类别 cmp x24, #ESR_ELx_EC_SVC64 // 64-bit 系统调用 b.eq el0_svc //跳转到el0_svc ... el0_svc负责执行系统调用,如果上层调用open系统调用打开文件时,就会从从sys_call_table,根据系统调用号,找到对应的sys_call_table元素,也即sys_open;并执行。代码如下: sc_nr .reg x25 //系统调用数量 scno .req x26 //系统调用号 stbl .req x27 //系统调用表地址 Tsk .req x28 //当前进程的thread_info结构体的地址 el0_svc: adrp stbl, sys_call_table // load syscall table pointer uxtw scno, w8 // syscall number in w8 mov sc_nr, #__NR_syscalls //把寄存器x25设置为系统调用数量 el0_svc_naked: // compat entry point stp x0, scno, [sp, #S_ORIG_X0] // save the original x0 and syscall number enable_dbg //开启调试 enable_irq //开启中断 get_thread_info tsk ldr x16, [tsk, #TI_FLAGS] // check for syscall tracing tbnz x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls? adr lr, ret_fast_syscall // return address,用于返回用户空间 cmp scno, sc_nr // check upper syscall limit b.hs ni_sys //如果超过系统调用限制,跳转到ni_sys 处理 ldr x16, [stbl, scno, lsl #3] // address in the syscall table br x16 // call sys_* routine ni_sys: //超过调用数量限制的处理 mov x0, sp b do_ni_syscall ENDPROC(el0_svc) ret_fast_syscall从系统调用返回用户空间,代码如下 /* * This is the fast syscall return path. We do as little as possible here, * and this includes saving x0 back into the kernel stack. */ ret_fast_syscall: disable_irq // disable interrupts str x0, [sp, #S_X0] // returned x0 ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for syscall tracing and x2, x1, #_TIF_SYSCALL_WORK cbnz x2, ret_fast_syscall_trace and x2, x1, #_TIF_WORK_MASK cbnz x2, work_pending enable_step_tsk x1, x2 kernel_exit 0 ret_fast_syscall_trace: enable_irq // enable interrupts b __sys_trace_return_skipped // we already saved x0 /* * Ok, we need to do extra processing, enter the slow path. */ work_pending: mov x0, sp // 'regs' bl do_notify_resume #ifdef CONFIG_TRACE_IRQFLAGS bl trace_hardirqs_on // enabled while in userspace #endif ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for single-step b finish_ret_to_user /* * "slow" syscall return path. */ ret_to_user: disable_irq // disable interrupts ldr x1, [tsk, #TSK_TI_FLAGS] and x2, x1, #_TIF_WORK_MASK cbnz x2, work_pending finish_ret_to_user: enable_step_tsk x1, x2 kernel_exit 0 ENDPROC(ret_to_user) work_pending调用do_notify_resume函数,代码如下: asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int thread_flags) { /* * The assembly code enters us with IRQs off, but it hasn't * informed the tracing code of that for efficiency reasons. * Update the trace code with the current status. */ trace_hardirqs_off(); do { if (thread_flags & _TIF_NEED_RESCHED) { schedule(); } else { local_irq_enable(); if (thread_flags & _TIF_UPROBE) uprobe_notify_resume(regs); if (thread_flags & _TIF_SIGPENDING) do_signal(regs); if (thread_flags & _TIF_NOTIFY_RESUME) { clear_thread_flag(TIF_NOTIFY_RESUME); tracehook_notify_resume(regs); } if (thread_flags & _TIF_FOREIGN_FPSTATE) fpsimd_restore_current_state(); } local_irq_disable(); thread_flags = READ_ONCE(current_thread_info()->flags); } while (thread_flags & _TIF_WORK_MASK); }  

  • 2025-01-17
  • 回复了主题帖: 《Linux内核深度解析》-异常处理

    oxlm_1 发表于 2025-1-17 09:23 一个模块一个模块地啃还是可以搞懂,只是linux内核太大了,内容太多了,没书完全不知道从哪开始啃 操作系统要坚持看,睡觉前比起床前进步一点点,极少成多,慢慢就有感觉了。这种知识不是一会半会的功夫。

  • 2025-01-16
  • 回复了主题帖: 《Linux内核深度解析》-异常处理

    T_T1111 发表于 2025-1-16 20:29 表示没有看懂,看着不像ARM或者X86 我写的不全,另外Linux内核确实比较难。

  • 2025-01-14
  • 回复了主题帖: 《Linux内核深度解析》-异常处理

    风尘流沙 发表于 2025-1-14 14:35 已下载学习了,谢谢楼主的介绍。 共同进步

  • 发表了主题帖: 《Linux内核深度解析》-异常处理

    《Linux内核深度解析》-异常处理 一、异常级别 二、异常分类 异常分为同步异常和异步异常。如下图所示   三、异常处理     以出现用户模式(异常级别0)下访问数据时生成的页错误异常为例。 el0_sync: kernel_entry 0 mrs x25, esr_el1 // read the syndrome register lsr x24, x25, #ESR_EL1_EC_SHIFT // exception class cmp x24, #ESR_EL1_EC_SVC64 // SVC in 64-bit state b.eq el0_svc cmp x24, #ESR_EL1_EC_DABT_EL0 // data abort in EL0 b.eq el0_da //调用el0_da cmp x24, #ESR_EL1_EC_IABT_EL0 // instruction abort in EL0 b.eq el0_ia cmp x24, #ESR_EL1_EC_FP_ASIMD // FP/ASIMD access b.eq el0_fpsimd_acc cmp x24, #ESR_EL1_EC_FP_EXC64 // FP/ASIMD exception b.eq el0_fpsimd_exc cmp x24, #ESR_EL1_EC_SYS64 // configurable trap b.eq el0_undef cmp x24, #ESR_EL1_EC_SP_ALIGN // stack alignment exception b.eq el0_sp_pc cmp x24, #ESR_EL1_EC_PC_ALIGN // pc alignment exception b.eq el0_sp_pc cmp x24, #ESR_EL1_EC_UNKNOWN // unknown exception in EL0 b.eq el0_undef cmp x24, #ESR_EL1_EC_BREAKPT_EL0 // debug exception in EL0 b.ge el0_dbg b el0_inv     对于用户模式(异常级别0)下生成的同步异常,入口是el0_sync。该入口函数在arch/arm64/kernel/entry.s中 通过上面代码,跳到对应的异常处理函数,  其中:访问数据时生成的页错误异常,通过(   b.eq    el0_da                   //调用el0_da   )这行代码,进入   el0_da   函数。 下面为 el0_da   函数代码 el0_da: /* * Data abort handling */ mrs x26, far_el1 //获取数据的虚拟地址 // enable interrupts before calling the main handler enable_dbg_and_irq //开启调试异常和中断 ct_user_exit bic x0, x26, #(0xff << 56) mov x1, x25 mov x2, sp bl do_mem_abort //调用do_mem_abort函数 b ret_to_user    通过do_mem_abort(  ) 函数将异常信息进行处理。

  • 2025-01-13
  • 回复了主题帖: 《Linux内核深度解析》-内存管理

    hjh0512 发表于 2025-1-13 19:13 这个看的容易理解,有没有完整版的,哪里能找到 《Linux内核深度解析》这本书上的,我稍微捡着一些总结了一下,你可以找找网上有没有电子版,或者买一本看一下。  

  • 发表了主题帖: 《Linux内核深度解析》-内存管理

    《Linux内核深度解析》-内存管理 一、内存管理架构         内存分为:用户空间、内核空间、硬件3个层面   二、内核地址空间(ARM64)         布局如下图;   三、内存管理应用系统调用接口 mmap()创建内存映射 mremap()扩大或缩小已存在的内存映射 munmap()删除内存映射 brk()设置堆上限 mprotect()设置虚拟内存的访问权限  

  • 2025-01-03
  • 发表了主题帖: 《Linux内核深度解析》之进程的创建

    进程的创新主要有fork、clone等的系统调用来完成。通过函数_do_fork实现。 _do_fork主要完成以下工作: 1、调用copy_process函数 2、调用wake_up_new_task函数唤醒新进程 /* * Ok, this is the main fork-routine. * * It copies the process, and if successful kick-starts * it and waits for it to finish using the VM if required. */ long do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr) { struct task_struct *p; int trace = 0; long pid; if (unlikely(current->ptrace)) { trace = fork_traceflag (clone_flags); if (trace) clone_flags |= CLONE_PTRACE; } p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. */ pid = IS_ERR(p) ? PTR_ERR(p) : p->pid; if (!IS_ERR(p)) { struct completion vfork; if (clone_flags & CLONE_VFORK) { p->vfork_done = &vfork; init_completion(&vfork); } if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) { /* * We'll start up with an immediate SIGSTOP. */ sigaddset(&p->pending.signal, SIGSTOP); set_tsk_thread_flag(p, TIF_SIGPENDING); } p->state = TASK_STOPPED; if (!(clone_flags & CLONE_STOPPED)) wake_up_forked_process(p); /* do this last */ ++total_forks; if (unlikely (trace)) { current->ptrace_message = pid; ptrace_notify ((trace << 8) | SIGTRAP); } if (clone_flags & CLONE_VFORK) { wait_for_completion(&vfork); if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); } else /* * Let the child process run first, to avoid most of the * COW overhead when the child exec()s afterwards. */ set_need_resched(); } return pid; } copy_process函数的主要工作: 通过dup_task_struct函数为进程分配内存空间。 通过sched_fork函数为进程设置调度器 通过copy_semundo、copy_files、copy_fs、copy_sighand、copy_signal、copy_mm、copy_namespaces、copy_io、copy_thread_tls函数复制共享资源。   wake_up_new_task函数的主要工作: 将新进程的状态切换到TASK_RUNNING 将新进程加入到运行队列

  • 2024-12-25
  • 回复了主题帖: 《Linux内核深度解析》之环境准备与第一章学习

    qzgiky 发表于 2024-12-24 11:28 学习手搓一个操作系统 一开始想着自已做一个的,不过这个系统有点太庞大了。现在感觉如果想自己编,应该从基础学起,比如说:有一定的RTOS的基础,然后如果做的话,先考虑低版本的系统开始做。

  • 2024-12-24
  • 发表了主题帖: 《Linux内核深度解析》之环境准备与第一章学习

    《Linux内核深度解析》第一章学习 一、准备工作 首先在Index of /pub/linux/kernel/v4.x/网站下载对应的4.12版本。   通过sourceinsight进行代码查看.     没有发现start.S文件,怀疑自己的解压方式有问题,采用lubuntu的share文件夹进行解压,依然没有发现对应文件。   学习代码结构 start.S的作用 Start.S左右启动的开始主要有以下作用: 通过调用各种板卡函数save_bool_params来保存重要的寄存器。 调用函数reset_sctrl来初始化系统控制寄存器。 根据处理器异常情况设置寄存器。 为处理器的缺陷打补丁。 简单设置主从处理器 跳到main函数 main函数的作用 为调用函数board_init_f做准备。调用board_init_f函数 把 U-Boot 程序复制到内存中,之后处理器从内存中取数据。 重新定位栈 调用函数 board_init_r,以此执行 init_sequence_r中的每个函数,直到run_main_loop函数。

  • 2024-12-11
  • 回复了主题帖: 读书入围名单: 《Linux内核深度解析》

    个人信息无误,确认可以完成阅读分享计划

最近访客

< 1/1 >

统计信息

已有6人来访过

  • 芯积分:112
  • 好友:--
  • 主题:7
  • 回复:7

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言