Linux内核速览_2_内核开发的特点

前言

"<Linux内核设计与实现>(原书第三版)"---以下称"本书"第2章"从内核出发"部分内容解读

引入

编程这个工作一直以来都是以"理性","逻辑"作为标签的,但很多时候我更希望靠"感觉"编程.感觉是建立在足够熟练和理解基础上的,学习的过程可以当成积累"感觉"的过程.

作为一个初学内核的人来说,"内核"是个怎样的感觉呢?

1>内核本身是个程序---在应用程序的下层,内核直接控制CPU和硬件.

2>内核可以访问绝对地址,并掌握最重要的数据--如可以访问中断寄存器,某些中间数据.

笔者在这里引入一个概念:中间数据.数据分为可以映射入硬件的数据和不能映射入硬件的数据,前者称为硬数据,后者称为中间数据.举个例子,在电脑上看见的像素点(或者一张图一个表格等),他们将被写入显存并显示在屏幕上,这些数据是硬数据.而在编程过程中,表示状态(可以用枚举类型)的数据是中间数据

内核开发特点

本书P15页有具体介绍

无libc库或无标准头文件

本书P15倒数第5段:与用户空间的应用程序不同,内核不能链接使用标准C函数库

---这个很好理解,标准C函数库在内核上层,封装了内核函数库.内核函数库提供了标准C的实现.在不会内核编程的情况下,标准C就是程序员的基础API(只会调用不会定义的函数).

之所以说内核仍然能使用标准C,是因为内核代码和标准C长得很像,例如

复制代码
//伪代码
//用户空间scanf的函数定义
void scanf(...,...){    //形参略
    _scanf(...,...);    //内核函数
}    

本书P16列举了printf和printk的例子,与此类似

复制代码
//伪代码
//用户空间怕printf的函数定义
void printf(...,...){    //形参略
    printk(...,...);    //内核函数
}    

本书P16第二段提到了++标志位提供优先级++ .说明在printf的函数体中,有标志位参与(上述伪代码未表达)---++标志位++可能类似于CPU标志位寄存器在内核空间的映射---内核空间有一个字节长度的全局变量来表示它(虽然现在不知道它叫什么,或者它的绝对地址)

GNU C

Linux内核用的语法和标准C有一些区别,在标准C的基础上做了扩充,其中有的内容比较有意思

内联函数

内联函数的条件:代码长度短,调用次数少

写法:static inline + 函数定义(返回值+空格+函数名+(+形参定义+)

内联函数还是在优化代码的时候考虑,平常不要想太多.原因是从使用条件来讲,代码要多短,调用次数多少次才够得上用内联函数,这些问题没有明确说明.所以内联函数的使用方法:++可以在代码优化阶段尝试把一些短且用得少的函数定义成内联函数,再看是否能提高工作效率++.

内联函数的特点:占空间大,反应时间短.其中为什么反应时间短,这个问题笔者在C++学习时没明白,到理解了"可执行文件"的内容后才有了答案.以下给出main函数执行的抽象

复制代码
int main(void){
    fun1();
    fun2();
}

程序的执行是在不停调用函数-函数返回.对应的操作大概是

复制代码
//伪代码
call fun1;
ret;
call fun2;
ret;

这个过程实际上是在"查表"(见笔者前面贴),每执行一次call,就到静态数据区查找对应函数地址,并开始执行编译后的二进制代码,然后返回.如果定义的是内联函数,编译后直接执行,少了调用-返回的过程.但是非内联函数编译后只在静态数据区有一份(查询执行),内联函数每调用一次都使用一份空间.

笔者个人认为内联函数的用处不是很大,但是有必要理解它的工作机制

内联汇编

内联汇编在本书P17只占了几行,但它很重要,意味着程序员可以真正使用操作系统的核心资源,做任何想做的事.

分支声明

编译器给出的优化,具体怎样优化和CPU的运行机制有关可以不管,掌握其用法

分支声明的用法:程序员先判定分支条件的概率,再根据概率调整写法.例如

复制代码
//C语言代码
//伪代码
if(表达式)
    做事A;
else
    做事B;

如果表达式不成立(等于0)的概率高,则改为

复制代码
//伪代码
if(unlikely(表达式))    //表达式为0的概率高
    做事A;
else
    做事B;

反之如果表达式成立的概率高,把unlikely换成likely

没有内存保护机制

本书P18有描述,容易理解,程序员进入内核编程,控制重要资源,权限大责任也大

容量小而固定的栈

栈的机制是每调用一个函数,在没返回(ret)之前,当前函数的数据将压栈.例如

复制代码
//伪代码
void fun1(){
    ...
    fun2();
}

void fun2(){
    ...
    fun3();
}

fun1的执行调用了fun2(),fun2()的执行调用了fun3().在fun3()执行完毕后,释放fun3()对应的栈空间.当fun2()执行完毕后,释放fun2()对应的栈空间."递归"将占用了巨大的栈空间.

对内核来说,栈空间属于自己分配,所以实际意义不大,不管是8KB还是16KB都是很大的空间了

响应做法:不要在函数定义时嵌套得太深,让栈的空间尽量小,节约内核资源.

同步和并发

这是操作系统的重点和难点,简单的说如何保证多个进程访问共享数据的准确性.

本书P19讲了:常用的解决竞争的办法是自旋锁和信号量.

可移植性

计算机的书籍中经常看见"平台无关"的说法,例如Java是平台无关的,Qt是平台无关的.他们可以运行在不同的操作系统上.为什么平台无关,因为他们底层的由内核程序实现了统一.

Linux是可移植的,指的是可以运行在不同架构的芯片上.内核程序在不同的芯片指令集上是不同的.但是从内核程序上层的程序,如标准C看来,他们是相同的.

相关推荐
程序猿编码5 天前
给你的网络流量穿件“隐形衣“:手把手教你用对称加密打造透明安全隧道
linux·开发语言·网络·安全·linux内核
Watink Cpper5 天前
Ubuntu24.04网络图标消失导致无法上网--排查得到原因:内核和驱动版本不匹配
运维·网络·linux内核·运维开发·debug·ubuntu24.04
十年编程老舅11 天前
深入 Linux 中断:原理详解 + 实战落地
linux·网络·linux内核·c/c++·中断
皮皮哎哟15 天前
ARM驱动基础(基于正点原子IMX6ULL Mini)
arm开发·linux内核·imx6ull·nxp·正点原子、·imx6u mini
程序猿编码17 天前
Linux内核级隐身术:进程与端口隐藏技术剖析
linux·运维·服务器·linux内核·进程
Qt程序员18 天前
Linux 内核 SPI 驱动
linux·linux内核·嵌入式开发·spi
新兴AI民工23 天前
【Linux内核二十九】进程管理模块:CFS调度器check_preempt_wakeup
linux·linux内核·wakeup
十年编程老舅24 天前
窥探内核心脏:深入解析 proc 虚拟文件系统
linux·服务器·数据库·c++·linux内核·文件系统·读写锁
十年编程老舅1 个月前
Linux 多线程高并发编程:读写锁的核心原理与底层实现
linux·c++·linux内核·高并发·线程池·多线程·多进程