linux内核
文章平均质量分 84
guoguangwu
这个作者很懒,什么都没留下…
展开
-
diagnose-tools 编译报错
diagnose-tools 编译报错原创 2023-03-24 15:39:08 · 443 阅读 · 1 评论 -
ld: lib/lib.a(string.o): in function `stpcpy‘:.../kernel/linux-4.9.299/lib/string.c:258: undefined
ld: lib/lib.a(string.o): in function `stpcpy':.../kernel/linux-4.9.299/lib/string.c:258: undefined原创 2023-01-03 13:14:42 · 352 阅读 · 0 评论 -
unsupported relocation type: r_x86_64_plt32 (4)
unsupported relocation type: r_x86_64_plt32 (4)原创 2022-12-31 18:33:00 · 1100 阅读 · 0 评论 -
netlink套接字
netlink套接字原创 2022-10-08 21:05:37 · 881 阅读 · 0 评论 -
linux-observability-with-bpf make: *** No rule to make target ‘/kernel-src/samples/bpf/bpf_load.c‘
Ubuntu 18.04.2 LTS 环境下git clone https://github.com/bpftools/linux-observability-with-bpf.git进入 linux-observability-with-bpf/chapter-2/hello_world阅读README.md后执行make bpfload,报错make: *** No rule to make target '/kernel-src/samples/bpf/bpf_load.c', nee原创 2022-04-03 09:45:36 · 1531 阅读 · 0 评论 -
进程间通信--信号量
前面讲过,共享内存为进程间通信提供了一种效率很高的手段,但是这种机制所提供的只是狭义的通信手段,而并不提供进程间同步的机能。所以,共享内存作为广义的进程间通信手段还必须要有其它机制的配合。同时,除共享内存以外,还有其他需要共享资源的场合也需要有进程间同步的手段。举例来说,如果有两个进程共享同一个tty终端。。。。。。。...原创 2022-02-18 22:14:17 · 525 阅读 · 0 评论 -
进程间通信-共享内存
共享内存,顾名思义就是两个或更多个进程可以访问同一块内存区间,使得一个进程对这块空间中某个单元内容的改变可以为其它进程所看到。共享内存是针对(命名或无名)管道以及其它机制运行效率比较低的缺陷而设计的。虽然报文队列比之前管道有了很大的改进,但是从运行效率的角度来说却并无什么明显的不同。而共享内存,则由于参数共享的各个进程就像访问普通内存一样地访问所共享的内存区间,其运行时的效率可以很高。对于某些运行效率显得很关键的应用来说,可能会觉得管道或报文队列的速度太慢,所以宁愿放弃一些由这些机制所提供的好处,而采用共享原创 2022-01-20 11:45:38 · 748 阅读 · 0 评论 -
进程间通信-报文传递
从本博客开始,我们用三篇博客的篇幅集中介绍Linux内核对Unix系统V进程间通信机制的继承和实现。早期Unix系统的进程间通信(IPC)机制主要有两种,就是管道和信号。后来,针对普通管道只能在近亲进程之间建立的缺点,又有了命名管道。但是,对于一个现代的操作系统以及日益发展的各种应用来说,这些机制虽然很重要,但也确实存在明显的不足。首先,信号所能载送的信息量很小,单独使用时不适合信息量要求比较大的场合。而管道,即使是命名管道,虽然可用于信息量较大的场合,但是对于不同的应用而言还是有许多缺点,主要有:原创 2022-01-17 21:16:28 · 1152 阅读 · 0 评论 -
系统调用ptrace和进程跟踪
为方便应用软件的开发和调试,从Unix的早期版本开始就提供了一种对运行中的进程进行跟踪和控制的手段,那就是系统调用ptrace。通过ptrace,一个进程可以动态地读写另一个进程的内存和寄存器,包括其指令空间、数据空间、堆栈以及所有的寄存器。与信号机制(以及其他手段)。。。。。。...原创 2022-01-16 13:25:08 · 1910 阅读 · 0 评论 -
进程间通信-信号
如前面博客所述,信号(signal,亦称软中断)机制是软件层次上对中断机制的一种模拟。从概念上说,一个进程接收到一个信号与一个处理器接收到一个中断请求是一样的。而一个进程可以向另一个(或另一组)进程发送信号,也跟在多处理器系统中一个处理器可以向其他处理器发出中断请求一样。当然,对一个处理器的中断请求并不一定来自其他处理器,也可以是来自各种中断源,甚至来自处理器本身。相应地,信号也不一定都来自其他进程,也可以来自不同的来源,还可以来自本进程的执行。更重要的是,二者都是异步的。处理器在执行一段程序时并不需要停下原创 2022-01-08 19:18:14 · 720 阅读 · 0 评论 -
进程间通信-命令管道
应该说,前一篇“管道”机制是一项重要的发明,它为Unix操作系统所带来的变化是革命性的,甚至可以说,没有管道就没有当初Unix环境的形成。但是,人们也认识到,管道机制也存在着一些缺点和不足。由于管道是一种无名、无形的文件,它就只能通过fork的过程创建与近亲的进程之间,而不可能成为可以在任意两个进程之间建立通信的机制,更不可能称为一种一般的、通用的进程间通信模型。同时,管道机制的这种确定本身就强烈地暗示着人们,只要用有名、有形的文件来实现管道,就能克服这种确定。这里所谓有名是指这样一个文件应该有个文件名,使原创 2022-01-07 22:57:55 · 395 阅读 · 0 评论 -
管道与系统调用pipe
管道机制的主体是系统调用pipe,但是由pipe所建立的管道的两端都在同一进程中,所以必须在fork的配合下,才能在父子进程之间或两个子进程之间建立起进程间的通信管道。我们先来看系统调用pipe。由于管道两端都是以(已打开)文件的形式出现在相关的进程中,在具体实现上也是作为匿名文件来实现的,所以pipe的代码与文件系统密切相关。原创 2022-01-01 21:05:00 · 1505 阅读 · 1 评论 -
传统的Unix进程间通信-概述
对于多用户、多进程的操作系统来说,进程间通信(IPC)是一项非常重要、甚至必不可少的基本手段和设施。在一个多进程操作系统所提供的运行环境下,可以通过两种不同的途径,或者说采用两种不同的策略,来建立起复杂的大型应用系统。一种途径是通过一个孤立的、大型的、复杂的进程提供所需的功能。早期的操作系统往往倾向于前者,而Unix及其衍生的各种系统则倾向于后者。这种基本方法和策略的改变正是Unix操作系统在程序设计领域中引起革命性转变的结果。原创 2022-01-01 15:04:46 · 703 阅读 · 0 评论 -
linux内核中的互斥操作
内核中的很多操作在进行的过程中都不容许受到打扰,最典型的例子就是队列操作。如果两个进程都要将一个数据结构链入到同一个队列的尾部,要是在第一个进程完成了一半的时候发生了调度,让第二个进程插了进来,结果很可能就乱了。类似的干扰也有可能来自某个中断服务程序或bh函数。在多处理器SMP结构的系统中,这种感染还有可能来自另个处理器。不过,除了一个进程主动调用schedule染出CPU的情况(显然不会发生在不容许收到干扰的过程中途)之外,只有在系统空间返回到用户空间的前夕才有可能发生调度。这样的安排使得上述两个进程原创 2021-12-25 15:43:49 · 1427 阅读 · 0 评论 -
linux内核-系统调用nanosleep()与pause()
处于种种原因,运行中的进程常常需要主动进入睡眠状态,并发起一次调度让出CPU。这一定要通过系统调用,或者在系统调用内核才能做到。注意,前面的博客中讲到的系统调用sched_yield与此有所不同,那只是让内核进行一次调度,而当前进程继续保持可运行状态。而这里所说的是,当前进程进入睡眠,也就是将进程的状态变成TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE,并从可执行队列中脱钩,调度的结果一定是其它进程得以运行。并且,进程一旦进入睡眠状态,就需要经过唤醒才能将状态恢复成TASK_原创 2021-12-19 11:44:18 · 5625 阅读 · 0 评论 -
linux内核-强制性调度
linux内核中进程的强制性调度,也就是非自愿的、被动的、剥夺式的调度,主要是由时间引起的。前面讲过,这种调度发生在进程从系统空间返回用户空间的前夕。当然,并非每次从系统空间返回到用户空间时都会发生此类调度。从前面博客引用entry.S的代码片段ret_with_reschedule可以看出,此时是否真的调用schedule,最终还要取决于当前进程task_struct结构中的need_resched是否为1(非0)。因此,问题就归结为当前进程的need_resched是在什么情况下才置成1的。在目前版本的原创 2021-12-19 11:21:53 · 1192 阅读 · 0 评论 -
linux内核-进程的调度与切换
在多进程的操作系统中,进程调度是一个全局性的、关键性的问题,它对系统的总体设计、系统的实现、功能设置以及各个方面的性能都有着决定性的影响。根据调度结果所做的进程切换的速度,也是衡量一个操作系统性能的重要指标。进程调度机制的设计,还对系统复杂性有着极大的影响,常常会由于实现的复杂程度而在功能与性能方面做出必要的权衡和让步。一个好的系统的进程调度机制,要兼顾三种不同应用的需要:...原创 2021-12-12 22:45:44 · 1968 阅读 · 0 评论 -
linux内核-系统调用exit与wait4
系统调用exit与wait4的代码基本上都在kernel/exit.c中,下面我们在引用代码时凡不特别说明出处的均来自这个文件。先来看exit的实现(kernel/exit.c):asmlinkage long sys_exit(int error_code){ do_exit((error_code&0xff)<<8);}显然,其主体为do_exit。先看它的前半部:NORET_TYPE void do_exit(long code){ struct t原创 2021-12-05 15:37:22 · 1650 阅读 · 0 评论 -
linux内核-系统调用execve()
读者在linux内核-系统调用fork、vfork与clone中已经看到,进程通常是按其父进程的原样复制出来的,在多数情况下,如果复制出来的子进程不能与父进程分道扬镳,走自己的路,那就没多大意义。所以,执行一个新的可执行程序时进程生命历程中关键性的一步。linux为此提供了一个系统调用execve,而在C语言的程序库中则又在此基础上向应用程序提供一整套的库函数,包括execl、execlp、execle、execlco、execve和execvp。此外,还有库函数system,也与execve有...原创 2021-11-28 22:01:27 · 4393 阅读 · 2 评论 -
linux内核-系统调用fork、vfork与clone
前面已经简要地介绍过fork与clone二者的作用于区别。这里先来看一下二者在程序设计接口上的不同:pid_t fork(void);int clone(int (*fn)(void *), void *stack, int flags, void *arg, ... /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );系统调用clone的主要作用是创建一个线程,这个线程可以是内核线程,也可以是用户线程。原创 2021-11-21 16:39:45 · 2391 阅读 · 0 评论 -
linux内核-进程三部曲:创建、执行与消亡
就像世上万物都有产生、发展与消亡的过程一样,每个进程也有被创建、执行某段程序以后最后消亡的过程。在linux系统中,第一个进程时系统固有的、与生俱来的或者说是由内核的设计者安排好了的。内核在引导并完成了基本的初始化以后,就有了系统的第一个进程(实际上是内核线程)。除此之外,所有其他的进程和内核线程都由这个原始进程或其子孙进程所创建,都是这个原始进程的后代。在linux系统中,一个新的进程一定要由一个已经存在的进程复制出来,而不是创造出来(而所谓创建实际就是复制)。所以,linux系统(Unix也一样)并不向原创 2021-11-21 09:29:27 · 953 阅读 · 0 评论 -
linux-2.6.11.12内核中的epoll数据结构和机制简析
大家都知道epoll的效率比poll和select性能高很多,原因在于在实现时,内核为epoll设置了一个文件系统,并且使用的是红黑树来增删改查struct epitem,然后在监控的文件描述符有数据时,通过回调将struct epitem挂载到struct eventpoll的就绪队列中,不需要将整个fd集合发送到用户态,并轮询查找就绪fd,效率高。原创 2021-11-14 19:46:47 · 1273 阅读 · 0 评论 -
linux内核-进程与进程调度
进程四要素要给进程下一个确切的定义不是件容易的事情。不过,一般来说linux系统中的进程都具备下列诸要素:有一段程序供其执行,就好像一场戏要有个剧本一样。这段程序不一定是进程所专有,可以与其他进程共用,就好像不同剧团的许多场演出可以共用一个剧本一样。 有起码的私有财产,这就是进程专用的系统堆栈空间。 有户口,这就是在内核中的一个task_struct数据结构,操作系统教科书中常称为进程控制块。有了这个数据结构,进程才能成为内核调度的一个基本单位接受内核的调度。同时,这个结构又是进程的财产登记卡,原创 2021-11-07 20:09:41 · 527 阅读 · 0 评论 -
linux内核-系统调用号与跳转表
文件include/asm-i386/unistd.h为每个系统调用定义了一个唯一的编号,称为系统调用号。部分编号如下所示:#define __NR_exit 1#define __NR_fork 2#define __NR_read 3#define __NR_write 4#define __NR_open 5#define __NR_close 6#define __NR_waitpid 7#define __NR_creat 8#原创 2021-11-07 19:18:23 · 1481 阅读 · 0 评论 -
linux内核-系统调用
如果说外部中断是使CPU被动地、异步地进入系统空间的一种手段,那么系统调用就是CPU主动地、同步地进入系统空间的手段。这里所谓主动,是指CPU自愿的、事先计划好了的行为。而同步则是说,CPU(实际上是软件的设计人员)确切地知道在执行哪一条指令以后就一定会进入系统空间。相比之下,中断的发生带有很大的不可预测性。但是,尽管有着这样的区别,二者之间还是由很大的共性。这是因为,在使CPU的运行状态从用户态转入系统态,也就是从用户空间进入系统空间,这一个基本点上二者是一致的。当然,中断有可能发生在CPU已经运行在系统原创 2021-11-07 09:22:22 · 915 阅读 · 0 评论 -
linux内核-时钟中断
在所有的外部中断中,时钟中断起着特殊的作用,其作用远非单纯的计时所能相比。当然,即使是单纯的计时也已经足够重要了。别的不说,没有正确的时间关系,你用来重建内核的工具make就不能正常运行了,因为make是靠时间标记来确定是否需要重新编译以及链接的。瞌睡时钟中断的重要性还远不止于此。我们在中断的博客中看到,内核在每次中断(以及系统调用和异常)服务完毕返回用户空间之前都要检查是否需要调度,若有需要就进行进程调度。事实上,调度只有当CPU在内核中运行时才能发生。在进程的博客中,读者将会看到进程调度发生在两种情原创 2021-11-06 21:41:34 · 2880 阅读 · 0 评论 -
linux内核-页面异常的进入返回
我们在内存管理中介绍内对页面异常处理时,是从do_page_fault开始的。当时因为尚未介绍CPU的中断和异常机制,所以暂时跳过了对页面异常的响应过程,也就是从发生异常至CPU到达do_page_fault之间的那一段路程,以及do_page_fault返回之后到CPU返回到用户空间这一段路程。我们可以来补上这个缺口了。与外设中断不同,各种异常都有为其保留的专用中断向量,因此相应的初始化也是直截了当的,这一点我们已经在初始化的博客中看到了。为页面异常设置的中断门指向程序入口page_fault(见原创 2021-11-06 16:02:40 · 354 阅读 · 0 评论 -
linux内核-软中断与Bottom Half
中断服务一般都是在将中断请求关闭的条件下执行的,以避免嵌套而使控制复杂化。可是,如果关中断的时间持续太长就可能因为CPU不能及时响应其他的中断请求而使中断(请求)丢失,为此,内核允许在将具体的中断服务程序挂入中断请求队列时将SA_INTERRUPT标志置成0,使这个中断服务程序在开中断的条件下执行。然而,实际的情况往往是:若是服务的全过程关中断则扩大打击面,而全称开中断则又造成不安定因素,很难取舍。一般来说,一次中盾服务的过程常常可以分成两部分。开头的部分往往是必须在关中断条件下执行的。这样才能在不受干扰的原创 2021-11-03 23:23:40 · 725 阅读 · 0 评论 -
linux内核-中断的响应和服务
搞清了i386 CPU的中断机制和内核中有关的初始化以后,我们就可以从中断请求的发生到CPU的响应,再到中断服务程序的调用与返回,沿着CPU所经历的路线走一遍。这样,既可以弄清和理解linux内核对中断响应和服务的总体的格局和安排,还可以顺着这个过程介绍内核中的一些相关的基础设施。对此二者的了解和理解,有助于读者对整个内核的理解。这里,我们假定外设的驱动程序都已经完成了初始化,并且把相应的中断服务程序挂入到特定的中断请求队列中,系统正在用户空间正常运行(所以中断必然是开着的),并且某个外设已经产生了一次原创 2021-10-31 16:46:44 · 997 阅读 · 0 评论 -
linux内核-中断请求队列的初始化
在前一篇博客中,我们讲到中断向量表(更确切地,应该说中段描述表)IDT有两种表项,一种是为保留专用于CPU本身的中断门,主要用于由CPU产生到的异常,如除数为0、页面错等等,以后由用户程序通过INT指令产生的中断(或称陷阱),主要用来产生系统调用(另外还有个用于debug的INT 3)。这些中断门的向量除用于系统调用的0x80外都在0x20以下。从0x20开始就是第二种表项,共224项,都是用于外设的通用中断门。这二者的区别在于通用中断门可以为多个中断源所共享,而专用中断门则是为特定的中断源所专用。由于原创 2021-10-31 12:56:31 · 356 阅读 · 0 评论 -
linux内核-中断向量表IDT的初始化
linux内核在初始化阶段完成了对页式虚存管理的初始化以后,便调用trap_init和init_IRQ两个函数进行中断机制的初始化。其中trap_init主要是对一些系统保留的中断向量的初始化,而init_IRQ则主要用于外设的中断。函数trap_init是在include/i386/kernel/traps.c中定义的:void __init trap_init(void){#ifdef CONFIG_EISA if (isa_readl(0x0FFFD9) == 'E'+('I'<原创 2021-10-30 22:56:56 · 1304 阅读 · 0 评论 -
i386的页式内存管理机制
学过操作系统原理的读者都知道,内存管理有两种,一种是段式管理,另一种是页式管理,而页式管理更为先进。从80年代中期开始,页式内存管理进入了各种操作系统(以Unix为主)的内核,一时成为操作系统领域的一个热点。Intel从80286开始实现其保护模式,也即段式内存管理。但是很快发现,光有段式内存管理而没有也是内存管理是不够的,那样会使它的x86系列逐渐失去竞争力以及作为主流CPU产品的地位。因此,在不久以后的80386中就实现了对页式内存管理的支持。也就是说,80386除了完成并完善从80286开始的段式原创 2021-10-30 16:28:39 · 230 阅读 · 0 评论 -
Intel x86 CPU系列的寻址方式
Intel可以说是资格最老的微处理器芯片制造商了,历史上的第一个微处理器芯片4004就会Intel制造的。所谓x86系列,是指Intel从16位微处理器8086开始的整个CPU芯片系列,系列汇总的每种型号都保持与前面的各种型号兼容,主要有8086、8088、80186、80286、80386、80486以及各种型号的Pentium芯片。自从IBM选择8088用于PC个人计算机以后,x86系列的发证就与IBM PC及其兼容机的发展休戚相关了。其中80186并不广为人知,就与IBM当初决定停止在PC机中使用80原创 2021-10-30 16:02:16 · 1041 阅读 · 0 评论 -
linux内核源代码中的汇编语言代码
任何一个用高级语言编写的操作系统,其内核源代码中总有少部分代码是用汇编语言编写的。读过Unix SysV源代码的读者都知道,在其约3万行的核心代码中用汇编语言编写的代码约2000行,分成不到20个扩展名为.s和.m的文件,其中大部分是关于中断与异常处理的底层程序,还有就是与初始化有关的程序以及一些核心代码中调用的公用子程序。用汇编语言辨析核心代码中的部分代码,大体上是出于如下几个方面的考虑:操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用指令,而这些指令在C语言中并无对应的语言成分。例如,原创 2021-10-29 22:45:30 · 942 阅读 · 0 评论 -
linux内核-X86CPU对中断的硬件支持
本博客不讨论严格意义上的中断响应全过程(比如说,怎样获得中断向量),而是着重讨论CPU在响应中断时,即在得到了中断向量以后,怎样进入相应的中断服务程序的过程。这是从操作系统的角度需要关心的问题。Intel x86 CPU支持256个不同的中断向量,这一点至今未变。可是,早期x86 CPU的中断响应机制非常原始、非常简单的。在实地址模式中,CPU把内存中从0开始的1K字节作为一个中断向量表。表中的每一个表项占四个字节,由两个字节的段地址和两个字节的位移组成。这样构成的地址便是相应中断服务程序的入口地址。这与1原创 2021-10-24 20:09:13 · 2433 阅读 · 0 评论 -
linux内核-中断、异常和系统调用
我们假定博客的读者已经具备了计算机系统结构方面的基础知识,所以本系列博客对中断以及异常(exception)处理的原理和机制不作深入的介绍。缺乏这方面基础的读者不妨先阅读一些微处理器方面的材料。不过,我们也并不要求读者对相关内容已经具备了很深入的理解。事实上,随着我们的介绍和分析,特别是随着各个情景的发展和代码的阅读,读者自会逐步地加深理解。先简要提一下,中断有两种,一种是由CPU外部产生的,另一种是由CPU本身在执行程序的过程中产生的。外部中断,就是通常所讲的中断(interrupt)。对于执行中原创 2021-10-24 17:24:36 · 170 阅读 · 0 评论 -
linux内存管理-系统调用mmap()
一个进程可以通过系统调用mmap,将一个已打开文件的内容映射到它的用户空间,其用户界面为:void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);参数fd代表着一个已打开文件,offset为文件中的起点,而addr为映射到用户空间中的起始地址,length则为长度。还有两个参数prot和flags,前者用于对映射区间的访问模式,如可写、可执行等等,后者则用于原创 2021-10-24 10:21:33 · 2753 阅读 · 0 评论 -
linux内存管理-系统调用brk()
尽管可见度不高,brk也许是最常使用的系统调用了,用户进程通过它向内核申请空间。人们常常并不意识到在调用brk,原因在于很少有人会直接使用系统调用brk向系统申请空间,而总是通过像malloc一类的C语言库函数(或语言成分,如C++中的new)间接地调用brk。如果把malloc想象成零售,brk则是批发。库函数malloc为用户进程(malloc本身就是该进程的一部分)维持一个小仓库,当进程需要使用更多的内存空间时就向小仓库要,小仓库中存量不足时就通过brk向内核批发。前面讲过,每个进程拥有3GB字节原创 2021-10-23 19:30:21 · 6380 阅读 · 2 评论 -
linux内存管理-外部设备存储空间的地址映射
任何系统都免不了要由输入、输出,所以对外部设备的访问是CPU设计中的一个重要问题。一般来说,对外部设备的访问有两种不同的形式,一种叫做内存映射式(memory mapped),另一种叫IO映射式(I/O mapped)。在采用内存映射方式的CPU中,外部设备的存储单元,如控制寄存器、状态寄存器、数据寄存器等等,是作为内存的一部分出现在系统中的。CPU可以向访问一个内存单元一样的访问外部设备的存储单元,所以不需要专门设立用于外设I/O的指令。从前的PDP-11、后来的M68K、PowerPC等CPU都采用这种原创 2021-10-23 16:18:38 · 1299 阅读 · 0 评论 -
linux内存管理-内核缓冲区的管理
可想而知,内核在运行中常常会需要使用一些缓冲区。例如,当要建立一个新的进程时就要增加一个task_struct结构,而当进程撤销时就要释放本进程的task结构。这些小块存储空间的使用并不局限于某一个子程序,否则就可以作为整个子程序的局部变量而使用堆栈空间了。另外,这些小块存储空间又是动态变化的,不像用于内存管理的page结构那样,有多大的内存就有多少个page结构,构成一个静态的阵列。由于事先根本无法预测运行中各种不同数据结构对缓冲区的需求,不适合为每一种可能用到的数据结构建立一个缓冲池,因为那样的话很可能原创 2021-10-22 21:40:03 · 1049 阅读 · 0 评论