Linux kernel development

2023-05-16

这几天一直在读经典的linux入门书《Linux kernel development》第三版即LKD,这是我第一次读英文版的技术书,颇有些高兴。之所以读,一是因为学过操作系统的理论知识,想看看货真价实的OS到底怎么实现的,其二则是在知乎上请教了一位linux的大牛,他给的建议就是初学linux最好的是这本《linux kernel development》,遂读了一番。读过之后,感觉读英文版总体不是很难,但是有一些地方确实不容易理解。而且以前读其他外国人写的书,就发现外国人习惯一句话至少说三遍,前前后后的写,这本书也是其中之一。这本书总体是对linux 2.6版本的概况,没有大篇幅的源代码,有对主要部件例如process、schedule、Interrupt、synchronization、MM、VFS、page等概述,有比喻和例子,而且语言幽默。尤其对一些主要struct、function和parameters讲解地很清楚,尤其是参数中有flag传参时对各种可选项列表而出。中断的一章难度大,有读不懂的地方只能去看中译版,而且很多知识细想一下就必须查查资料才能懂得。然而读着读着发现,内核的总体编程思想和OOP极为相似,而且作者也在书中提到VFS虚拟文件系统中结构体的设计和面向对象的思想很类似,也是C语言中支持函数指针,可以在struct中设置指向函数的指针,这样的话和面向对象中的对象有何区别?同时发现书中也是有些许错误的。这本书之后应该是《深入理解LINUX内核》。在此记录读书笔记和心得。


总体来看,几乎书中提到的struct中除了struct_operation类型,其他的结构体中都会有例如atomic_t、pinlock、xxx_operation等变量类型。同时linux中应用的数据类型书中列举了三种:Queue、Map和binary tree,而binary tree尤指rb-tree红黑树。对进程的管理使用双向链表,调度算法会使用队列,映射UID使用map,而且使用自平衡二叉树来实现map。在对链表的实现中,kernel定制了list_head来统一所有链表:
struct list_head {
struct list_head * next;
struct list_head * prev;
}
所有的链表内都加入该结构,链表的向前向后遍历使用的就是list_head的next和prev指针,而且能够通过在某个结构体中嵌入list_head,采用 container_of宏从list_head得到父结构的任何变量,因为在C语言中,一个给定结构中的变量偏移在编译时地址被ABI固定,ABI是应用二进制接口Application Binary Interface,ABI规定了机器代码使用的CPU指令集、大小端配置、对齐和寄存器使用限制等等,就是因为结构体对齐的编译时规定从而可以获得父结构体的变量。结构体对齐在第19章也有提到,对齐指的是数据在内存中存储的位置需要是其类型大小的整数倍,也就是大小为2的N次幂的数据类型其存储地址低N位都是0,因为这样才能整除。之所以对齐是为了CPU访存的访问效率,而没有对齐的数据例如结构体中的数据则会在存储的时候添加额外的空间补齐,所以对于结构体来说应该是长数据类型放在前面,而且可以在定义结构体类型的时候使用__attribute((aligned (n)))来说明。

linux kernel 中大量使用了macro宏,由于他的便捷利用,这点在开始的几章就有提到。


kernel中使用task_struct来表示进程,同时linux坚持thread线程只是共享资源的进程,不另对线程进程表示。process的状态被linux分为了五种:TASK_RUNNING、TASK_INTERRUPTIBEL、TASK_UNINTERRUPTIBEL、__TASK_TRACED、__TASK_STOPPED和僵死状态,其中TASK_INTERRUPTIBEL和TASK_UNINTERRUPTIBEL区别在于前者可以收到signal或者event发生而转换为RUNNING,而后者对signal信号的到来没有反应。不同状态的进程链接在不同的双向链表中,受到不同的调度。但是在网上查资料看到不同的linux版本中进程状态不同。每一个进程都有自己标识符PID。linux中包括用户级进程、用户级线程和内核级线程。用户级线程在用户空间实现,自我调度,因为linux内核看不到用户级线程,这样线程切换不必陷入内核效率高速度快,但是用户级线程阻塞则代表整个进程阻塞也就是其他同进程的用户级线程也阻塞因为内核看不到线程只见线程导致内核对该进程schedule了。内核线程,标准的内核空间进程,可调度和抢占,是为了运行后台操作例如flush、ksoftirqd软中断由内核自我管理,且只运行于内核态不能切换到用户态,内核线程可能一直处于等待状态或者周期运行的状态来实现特定任务。4G内存空间中,低3G由用户进程访问,高1G空间由内核和用户进程使用,每个用户进程私有空间3G,因为进程进行系统调用的时候可以陷入内核空间导致进程能够使用的空间是4G。一个CPU上同一时间只能运行一个进程。程序在编译之后形成的地址空间是虚拟地址空间,由虚拟到物理的映射需要段描述符表或者页表,linux支持页表,即使CPU架构本身在于段表。内核占据最高1G的空间,但是是虚拟地址,映射到物理地址之后一定是从0X00000000开始,而3G位置0XC0000000在linux称为PAGE_OFFSET。所以用户空间的虚拟地址到物理地址的映射需要段描述符表或页表和TLB快表由硬件支持提供,而内核则是对于一个虚拟内存空间x其物理空间为x-0xc0000000,该PAGE_OFFSET值在linux的page.h文件作于预编译命令定义。为什么linux要把内核放在0XC0000000~0XFFFFFFFF中,网上写的大多是null指针在用户空间和中断向量在高地址。


linux2.6通过sys_fork()函数进行进程创建,实际调用do_fork()函数传递不同CLONE_FLAGS来实现对fork() / clone() / vfork()函数的调用,而do_fork()函数核心调用copy_process()函数。fork()之后子进程完全复制父进程的资源,有自己的task_struct和pid,但是考虑到复制性能和子进程是否会用到复制的资源问题,采用COW写时复制推迟真正的复制,如果子进程发生写入则资源复制。fork()调用执行一次返回两个值,对于父进程,fork函数返回子程序的进程号,而对于子程序,fork函数则返回零。而fork之后的父和子进程不共享堆栈、变量等数据空间,自己有自己的运行空间,但是共享程序指令,父子进程各自执行,其执行顺序收到linux kernel进程调度算法的控制。vfork()之后的父子进程共享地址空间,vfork()的子进程必须显式调用exit()来结束,同时vfork()也是返回两个值。vfork()之后父进程会被阻塞直至子进程调用exit()或者调用exec()执行另一程序,也就是父进程挂起而子进程运行。而clone()则有选择的复制父进程的资源具体复制哪些资源由CLONE_FLAGS指明,没有复制的采用指针的形式给子进程共享,clone()返回子进程的pid,clone()通过CLONE_VFORK参数说明父进程挂起而子进程运行直至子进程唤醒父进程,若没有CLONE_CFORK则两者抢占运行。


linux进程调度考虑到I/O密集型和CPU密集型即计算密集型的不同,并没有采用简单的时间片方法,采用了CFS完全公平算法,实质上是根据nice值按照优先级为进程分配处理机比例而非单纯的时间片即采用几何加权的方式而非算法加权方式,而这个比例来源于当前可运行进程runnable的整体运行时间,每次选择最小的运行时间的进程调度。nice值衡量分配处理器比例的值,值越大优先级越低。同时CFS设置了一个估值targeted latency。CFS引入了一个minimum granularity,为1毫秒。linux在调度实体sched_entity中使用vruntime变量来记录真实运行时间进而分析他还能运行多长时间,所以CFS的核心在于:pick the task with the smallest vruntime,就是其抢占时机取决于新的可运行程序消耗多少处理器使用比。如果消耗的使用比比当前进程小,则新进程立刻投入运行,抢占当前进程。此时红黑树上场了,linux使用RBTREE管理runnable进程,这样可以直接从最左子节点得到最小值因为rbtree也是一颗排序树同时是自平衡树是根据其自身性质实现平衡,而且linux将下一个最左子节点缓存起来进一步提高效率,但改变的时候要考虑到cache的更改。之前采用CFS进行调度的普通进程,基于优先级进行调度。linux2,6中支持的实时调度策略有两种:FIFO和RR。实时调度的优先级高于普通调度这是为了更快响应用户请求。进程的调度方式是schedule()函数。进程休眠是自己将自己状态状态改变移除红黑树放在相应队列中,而唤醒则是进程被设置为可运行状态移入树中。同时里虐支持用户级进程抢占式运行和内核线程抢占运行。


用户级进行通过系统调用陷入内核态,此时内核代表用户进程运行,之后再返回用户状态。系统调用的实现在X86中是通过汇编指令软中断INT 0X80或者SYSENTER。在系统调用前,参数被写入CPU寄存器,在调用系统调用服务程序前,把参数拷贝到内核堆栈中,返回值写入EAX寄存器供用户态进程读取。




SMP的全称是“对称多处理“(Symmetrical Multi-Processing)技术,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。系统将任务队列对称地分布于多个CPU之上,从而极大地提高了整个系统的数据处理能力。所有的处理器都可以平等地访问内存、I/O和外部中断。在对称多处理系统中,系统资源被系统中所有CPU共享,工作负载能够均匀地分配到所有可用处理器之上。CONFIG_SMP配置项控制内核是否支持SMP。


linux中中断的产生是随机异步的,而异常是在一条执行之后同步产生的。linux将中断处理分为两个阶段The Top Halves 和The Bottom Halves,之所以分开执行是为了让top halves执行的尽可能快,而其他工作留在bottom havles中。尽可能快的执行中断处理程序,可以中断嵌套,不能sleep。中断分为可屏蔽的和不可屏蔽的。硬件设备控制器通过IRQ线向CPU发出中断,可以通过禁用某条IRQ线来屏蔽中断。中断描述符表是根据中断类型码来取得中断处理程序入口地址,进而处理中断。linux的top halves即interrupt handler运行在interrupt context中,而bottom halves则分为三种softirq / tasklet / work queue,仅有work queue运行于process context中,其实就是一个进程。tasklet基于softirq。这些bottom halves的执行就需要靠内核线程运行来实现了,每个处理器都有一组辅助处理他们的内核线程。,softirq由softirq_action表示,函数__do_softirq():

u32 pending;

pending = local_softirq_pending();

if(pending) {

struct softirq_action * h;

set_softirq_pending(0);

h = softirq_vec;

do {

if (pending & 1)  h->action(h);

h++;

pending >>= 1;

}while(pending);

}

softirq适用于时间要求最高的bottom halves。softirq支持中断不能sleep,同一个处理器上不能同时运行相同的softirq,但是不同的processor可以,所以需要锁来处理同步问题,使得更加复杂。tasklet由tasklet_struct表示,tasklet不存在这个问题因为tasklet不能在不同处理器上运行相同的tasklet,即相同的tasklet不会并发的执行即使在不同处理器上。tasklet分为两种:HI_SFOTIRQ和TASKLET_SOFTIRQ,两者优先级不同前者高。tasklet的调度通过tasklet_schedule()和tasklet_hi_schedule()来运行。当大量的软中断触发时内核唤醒一组最低优先级最高nice值的线程处理,以免其他线程一直由于软中断的触发而starve。最后一种是work queue,运行于process context,而非interrupt context中,可被调度可sleep,由workqueue_struct表示,其核心是一个cpu_workqueue_struct数组,使用work_struct代表需要处理的工作,有很多类型的工作线程,而对于一种工作线程一个处理器上只有一个,由cpu_workqueue_struct表示,而workqueue_struct表示的就是整台机器上同一种工作线程的集合。


产生并发原因书中列举了五个:INTERRUPT / SOFTIRQ AND TASKLET / KERNEL PREEMPTION / SLEEPING AND SYNCHRONIZATION WITH USER-SPACE / SYMMERTICAL MULTIPROCESSING。而对于同步问题,涉及原子类型操作atomic operation,原子类型的定义:

typedef struct {

volatile int counter;

} atomic_t;

spin lock自旋锁,linux中与其他OS不同的是他不会递归自旋的。自旋锁是最多由一个线程拥有的锁机制,等待锁的线程是不sleep的,所以持有自旋锁的线程应该是短时间完成任务的。例如自旋锁可以用在中断处理程序中而信号量不能因为信号量运行线程睡眠,而在将自旋锁使用在中断处理程序上时,获取锁之前要禁止当前处理器上的中断处理请求,因为如果不禁止的话中断处理请求可能会中断内核代码去请求获得已经被持有的锁,但是持有锁的中断被该请求中断了,造成死锁。使用自旋锁来保护那些可抢占进程上下文代码的bottom half,或者保护top half和bottom half共享的数据因为top half可以抢占bottom half的运行。第三个涉及读写自旋锁rw spin lock,一个或多个读者可以拥有一把锁而最多只能有一个写者有一个锁,而且linux中读写锁是读者优先的。第四,支持PV操作的信号量semaphore,支持线程sleep,就是得不到锁就加入等待队列里,等到持有锁的进程释放的时候就抢占式运行。这很适合长时间持有锁的进程,不适用于短时进程,因为短时进程使用semaphore会导致频繁的context switch这些额外功增加之后导致进程运行时间受到压榨,不适用interrupt context,而且取得semaphore的时候不能持有spin lock因为你能睡眠啊。semaphore有锁最多持有者数量的计数变量,当该变量为1的时候就成了mutex,信号量也可以分为rw semaphore。第五,mutex,就是可sleep的spin lock,解锁的进程必须是持有锁的进程,进程持有mutex的时候不能exit,中断程序不能获取mutex。第六,支持写者优先的顺序锁。涉及到锁的问题,就会考虑到编译器为了优化而做的重排序,而linux kernel提供使用rmb()设置读内存屏障防止读屏障两侧的读操作被out of order,wmb()提供写操作屏障,而mb()提供两者,barrier()组织编译器的重排序优化。


阶段性发生的事件使用系统时钟,可以产生时钟中断的可编程硬件,其中断类型码依赖于不同的架构设计。而实时时钟RTC则是由主板上的小电池供电一直运行,记录时间。定时器用于执行延后的任务,设置延后时间和执行任务启动定时器,定时器不会周期运行,超时后自动撤销,应用广泛,由timer_list表示。内核在时钟中断发生后执行定时器,他作为软中断在bottom half中运行,内核按照超时时间将所有的定时器分作五组,当定时器超时时间接近时向下移。


linux对内存的管理采用分页式管理,对页的抽象struct page,采用page来跟踪所有的页,他是相关物理页的映射。内核把不同的页划分为不同的zone区,有四个区:ZONE_DMA / ZONE_DMA32 / ZONE_NORMAL / ZONE_HIGHMEM,区使用struct zone表示。kmalloc()函数获得至少size字节的物理上连续的内存空间,释放使用kfree(),而空间分配也需要标识,因为不同的zone功能权限不同,flag分为三种:action modifiers / zone modifier / type。action modifier说明内核如何分配请求的内存,zone modifier说明区哪里分配,type表明内存分配的特定类型。vmalloc()分配虚拟地址连续不保证物理地址连续的空间。linux增加slab层作为通用数据结构缓存层,来加快数据结构的请求和释放,slab层把不同的对象划分为高速缓存组,每个组存放不同类型的对象,这些cache又划分为多个slab,每个slab中才把持着这些数据结构对象,就是一种为了加快数据结构对象分配和释放效率的分作多层的层级结构,而kmalloc()接口建立在slab上,当然slab采用struct slab结构表示。

用户进程通过system call陷入内核态之后内核使用内核栈来运行的,内核栈很小而且固定,而每个进程创建的时候都会获得一个内核栈空间,一般是2页大小,所以系统调用的底层函数不能是递归的。内核栈与进程体struct task_struct使用两个指针完成:


linux为中断处理程序提供中断栈的概念


VFS虚拟文件同是应为一个系统内可以安装很多种不同类型的文件系统,而采用一种存储于内存的中间层来屏蔽掉这些差异为上层系统调用提供统一的服务接口。而对于VFS,存在四种主要的struct:保存具体文件系统元信息的superblock :super_block;存储内核操作文件或目录所有信息的inode  ; 代表路径组件的dentry ; 代表进程中打开的文件的file。dentry的状态分为三种:used / unused / negative。dentry和file并不是真正与磁盘上数据对应而是存储在内存中结构体类型。描述每个filesystem功能和行为的struct:file_system_type,他代表了文件系统的一个具体实例。从进程角度来看,每个进程打开的文件使用files_struct来表示,其中fd_array指向了打开文件列表。而task_struct中使用fs来指向fs_struct来表示和该进程相关的文件系统信息。


外设分为块式、字符式和网络式,各有特点。linux采用块缓存的方式,实际上就是内存和磁盘之间的cache,一个块可以包含几个部分但不超过一页,使用buffer_head表示,他的作用就是描述从磁盘块到物理内存块的映射。linux使用bio来代表正在执行的块式I/O操作,他本身不存储I/O内容,而是通过bi_io_vec指向bio_vec数组的首地址,通过bi_idx对该数组进行索引,而该数组中的每一项bio_vec都指向者page,这才是操作的内容或者说数据。bio_vec结构体实际上由page、offset和len构成。从另一个角度看,每一个块I/O请求由一个bio实体表示,每一个请求可能要操作一个或多个块,这些块是存储在bio_vec结构体数组中,表示每一个块的方式是使用页指针、偏移量和块长度来表示的。而bio中使用bio_cnt来计数bio结构体的使用次数,当其为0时就撤销bio。linux采用请求队列的方式对请求进行管理,每一个节点都是一个请求,而这些请求会对磁盘操作。如何调度这些请求来最大地提高磁盘访问效率就是个问题。linux采用merge and sort方式,把对相近位置磁盘区域的请求进行合并就可以一次访问磁盘满足多个请求,同时以对磁盘访问的位置为依据进行sort。这里面涉及到linux 电梯算法、最后期限算法、预测I/O算法(没看明白)、完全公平队列I/O算法、空I/O调度。


mm_struct描述一个进程的地址空间。虚拟内存区域由vm_area_struct描述,指定了地址空间内连续区间上的一个独立内存范围,每个内存区域由其自己的属性和访问权限,每个vma代表不同类型的内存区域,不同类型的虚拟内存区域例如C库程序段、代码段、数据段、栈段、bss段等,其权限和属性由vm_area_struct中的vm_flags设置。通过mm_struct中两个变量mmap和mm_rb之一访问内存区域,但是两者包含完全相同的vm_area_struct结构体指针。内核使用mmap()和do_mmap()来扩展进程内存区域。另外linux采用三级页表管理页:PGD / PMD / PTE,PTE指向page结构。对于页的管理涉及页缓存和页写回。页cache缓存的内存页面,在执行对磁盘操作之前会检查是否在cache中已经有了该页缓存。收集脏页的时候采用LRU 、LRU/N。linux使用address_space结构体来管理缓存项和页I/O操作。一个被缓存的文件只与一个address_space关联,但是可以有多个vm_area_struct,即一个物理页对多个虚拟页。从磁盘块的角度看,每个磁盘块也以块式I/O缓冲映射在物理内存中存储于页cache中,就是说实际上就只有页cache,但是里面存储磁盘块的缓存。脏页写回发生在低于设置的阀值时;脏页驻留内存超过一定时间时;用户主动调用sync()和fsync()方法时,内核由一组flusher线程完成该工作,这组线程周期性的被唤醒,这就需要system timer和内核线程的概念了。



遗留问题:其实linux才学了一点点,先列出看书的时候想到的不明白的问题和来自网络的答案,以后一定回来好好看看。

linux内核抢占和用户抢占:http://blog.csdn.net/gatieme/article/details/51872618

linux为何采用红黑树来维护进程管理:https://www.zhihu.com/question/20545708/answer/44370878

可重定位装入: http://blog.csdn.net/xinshen1860/article/details/22733953

可重入性 :http://blog.csdn.net/acs713/article/details/20034511

异常、中断、陷入的区别 : http://blog.chinaunix.net/uid-26931176-id-3216697.html

机器字长、存储字长: http://www.cnblogs.com/claremore/p/4802881.html

linux中的radix tree。



参考文章:  
  http://blog.csdn.net/legenddcr/article/details/51353302
 http://blog.csdn.net/zkf11387/article/details/7662450
 http://www.cnblogs.com/zhaoyl/p/3620204.html
 http://blog.csdn.net/lxl584685501/article/details/46889405

 http://soft.chinabyte.com/os/339/11875339.shtml

http://blog.csdn.net/chenjiayi_yun/article/details/26242245


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux kernel development 的相关文章

  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • linux下无法创建僵尸进程

    嗯 我有一个奇怪的问题 我无法在我的项目中创建僵尸进程 但我可以在其他文件中创建僵尸进程 有简单的说明 int main if fork 0 printf Some instructions n else sleep 10 wait 0 r
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • 使用 Python 绘制 2D 核密度估计

    I would like to plot a 2D kernel density estimation I find the seaborn package very useful here However after searching
  • 如何使用 Cloud Init 挂载未格式化的 EBS 卷

    Context 我正在使用https wiki jenkins io display JENKINS Amazon EC2 Plugin https wiki jenkins io display JENKINS Amazon EC2 Pl
  • 执行“minikube start”命令时出现问题

    malik malik minikube start minikube v1 12 0 on Ubuntu 18 04 Using the docker driver based on existing profile Starting c
  • 如何在linux中以编程方式获取dir的大小?

    我想通过 C 程序获取 linux 中特定目录的确切大小 我尝试使用 statfs path struct statfs 但它没有给出确切的大小 我也尝试过 stat 但它返回任何目录的大小为 4096 请建议我如何获取 dir 的确切大小
  • 无需超级用户即可在 Linux 中打开 RAW 套接字

    我必须编写一个在 Linux 上运行的 ping 函数 语言是 C 所以 C 也可以 在网上搜索并查看源代码ping命令 事实证明我应该创建一个原始套接字 icmp sock socket AF INET SOCK RAW IPPROTO
  • 如何获取 (Linux) 机器的 IP 地址?

    这个问题和之前问的几乎一样如何获取本地计算机的IP地址 https stackoverflow com questions 122208 get the ip address of local computer 问题 但是我需要找到一个的I
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • 从 Xlib 转换为 xcb

    我目前正在将我的一个应用程序从 Xlib 移植到 libxcb 但在查找有关我有时使用的 XInput2 扩展的信息时遇到了一些麻烦 libxcb 中有 XInput2 实现吗 如果是的话 在哪里可以找到文档 目前我在使用此功能时遇到问题
  • 内核的panic()函数是否完全冻结所有其他进程?

    我想确认内核的panic 功能和其他类似kernel halt and machine halt 一旦触发 保证机器完全冻结 那么 所有的内核和用户进程都被冻结了吗 是panic 可以被调度程序中断吗 中断处理程序仍然可以执行吗 用例 如果
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac
  • 如何查找哪个 Yocto 项目配方填充图像根文件系统上的特定文件

    我经常与 Yocto 项目合作 一个常见的挑战是确定文件为何 或来自什么配方 包含在 rootfs 中 这有望从构建系统的环境 日志和元数据中得出 理想情况下 一组命令将允许将文件链接回源 即配方 我通常的策略是对元数据执行搜索 例如gre
  • Linux 为一组进程保留一个处理器(动态)

    有没有办法将处理器排除在正常调度之外 也就是说 使用sched setaffinity我可以指示线程应该在哪个处理器上运行 但我正在寻找相反的情况 也就是说 我想从正常调度中排除给定的处理器 以便只有已明确调度的进程才能在那里运行 我还知道
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • 无法显示 Laravel 欢迎页面

    我的服务器位于 DigitalOcean 云上 我正在使用 Ubuntu 和 Apache Web 服务器 我的家用计算机运行的是 Windows 7 我使用 putty 作为终端 遵循所有指示https laracasts com ser

随机推荐

  • glib简单记录包括字符串,主循环,回调函数和xml解析

    一 将最近用到的glib字符串功能整理了下直接用程序记录比较好看懂 define MAX LEN 100 gchar demo char msg gchar pcfgfile 61 NULL para 61 NULL va list arg
  • 产品管理精华:第十六,品味

    美感是第一道关卡 xff0c 丑陋的数学在世界上无法生存 G H 哈代 xff0c 一个数学家的道歉 品味 xff0c 如今很少听到这个词了 xff0c 可能被一些新的 xff0c 流行的叫法替代了 很长一段时间 xff0c 谈论品味这玩意
  • 《产品管理精华》目录

    产品管理精华 目录 本文是这个目录 xff0c 可以快速找到你需要的内容 xff1a 产品管理精华 xff1a 第一 xff0c 谁是优秀的产品经理 xff1f 产品管理精华 xff1a 第二 xff0c 工具 xff0c 适合自己的最好
  • 电商分销的社交化运营实践

    最近一段时间专注于研究社交化的电商分销运营体系 xff0c 如何最大化利用碎片化社会资源来整合营销渠道 目前最有代表性的电商分销就是全民分销概念 xff0c 它的最大特点在于通过社交网络 xff08 大部分是通过微信商城开店 43 三级返佣
  • 《产品管理精华》序

    今日事 xff0c 今日毕 xff01 xff0c 说起来容易 xff0c 做起来难 知道为什么要去做一件事 xff0c 这样做了哪些人都受到实惠 xff0c 领悟生活的趣味 xff0c 自己是否也得到了快乐和满足 xff0c 每一份付出都
  • 个人简介

    经验丰富的产品设计师 xff0c 专门从事与产品设计相关的研究 趋势分析 理念探索和产品研发 专注于政策脉搏和市场趋势的研究 xff0c 成功研发多个互联网相关领域产品 xff0c 对移动互联网产品架构 运营管理有较为深入的管控能力 欢迎大
  • 产品管理精华:第一,谁是优秀的产品经理?

    34 人因为梦想而伟大 34 英格丽 褒曼 每个人的心中都有一个梦想 xff0c 梦想是美好的 xff0c 但是实现梦想的道路是曲折的 xff0c 无数人在实现梦想的道路上遭遇了无数曲折 xff0c 尽管如此 xff0c 他们依旧大步向前
  • 产品管理精华:第三,需求调研,从用户出发

    因为需要 xff0c 让我更加完美 佚名 产品经理都会遇到 客户 用户 这两个概念念 xff0c 它们谁更重要也一直争论不休 用户 近乎苛刻的需求可以不断产品体验和质量 xff0c 同时产品投入市场之后都会遇到变现这个问题 xff0c 总需
  • 第1章 概述--PADS的历史版本

    1986年 xff1a PADS PCB xff0c DOS操作系统 1989年 xff1a PADS Logic xff0c DOS操作系统 1990年 xff1a PADS 2000 xff0c DOS操作系统 1993年 xff1a
  • docker安装图形化界面

    分享第一份Java基础 中级 高级面试集合 Java基础 xff08 对象 43 线程 43 字符 43 接口 43 变量 43 异常 43 方法 xff09 Java中级开发 xff08 底层 43 Spring相关 43 Redis 4
  • 嵌入式软件开发岗面试题

    1 单片机IO口开漏输出和推挽输出有什么区别 xff1f 答 xff1a 开漏输出 xff1a 开漏输出只能输出低电平 xff0c 如果要输出高电平必须通过上拉电阻才能实现 就类似于三极管的集电极输出 推挽输出 xff1a 推挽输出既可以输
  • pytorch 查看模型结构 网络参数

    用法比较简单 xff0c 不过容易忘 xff0c 记录一下 假设已定义好模型 xff0c 名为model 查看模型结构 xff1a gt gt gt print model 查看网络参数 xff1a for name parameters
  • 【Linux网络编程】select函数实现TCP并发服务器

    I O多路复用 应用程序中同时处理 多路 输入输出流 xff0c 若采用 阻塞模式 xff0c 将得不到预期的目的 xff1b 若采用 非阻塞模式 xff0c 对多个输入进行轮询 xff0c 但又太浪费 CPU 时间 xff1b 若设置 多
  • 【校招】面试_华为_通用软件工程师_二面

    1 面试信息 面试形式 xff1a 视频面试 面试时间 xff1a 2020 03 25 11 30 00 AM 面试时长 xff1a 40分钟 面试职位 xff1a 软件技术开发部 通用软件工程师 xff08 无线网络产品线 通用软件开发
  • 你必须会的启发式搜索算法--A*算法

    一 算法原理 A 算法 xff0c 就是解决在一个平面 grid地图中寻找起点到终点的最短路径问题的算法 xff0c 类似于Dijkstra算法和BFS算法一样 xff0c 属于广度优先搜索 实际上它还是一个启发式搜索算法 xff0c 什么
  • C/C++/LINUX 资源网站

    C C 43 43 LINUX 资源网站 本博客记录学习 开发中常用的网站 http www cplusplus com c 43 43 官网 xff0c 包含c 43 43 介绍以及一些简单的使用样例 目前主要用来查询 STL 的使用 h
  • 焊接单片机最小系统板,驱动lcd1602

    今天分享一些我制作单片机最小系统板的过程以及遇到的问题和解决办法 本人萌新一枚 xff0c 写文章就是我们大家互相学习交流 之前学习的是郭天祥的tx 1c单片机 xff0c 书中的例程都写的差不多了 xff0c 就想着自己焊接一个最小系统板
  • [ROS]极简开发环境建立流程(新手适用)

    ROS开发环境的建立 一 前言二 操作系统环境1 Ubuntu2 VMWare 可选 三 ROS运行环境1 ROS2 ROS周边设置3 ROS开发包及帮助获取方式 四 ROS开发环境1 建立工作空间 workspace2 Original开
  • 伽马函数与贝塔函数的定义

    伽马函数 称以下函数 61 0
  • Linux kernel development

    这几天一直在读经典的linux入门书 Linux kernel development 第三版即LKD xff0c 这是我第一次读英文版的技术书 xff0c 颇有些高兴 之所以读 xff0c 一是因为学过操作系统的理论知识 xff0c 想看