[置顶] Linux内核工程导论——linux学习和职业曲线(初学者,中级,高级都可参考)

时间:2022-08-28 14:26:14

Linux世界介绍

给自己定级

门外汉:
不会安装操作系统
不会用虚拟机(安装和使用)
入门级:
熟悉常见的发行版,甚至装过并且能用一些特殊发行版(例如kali)做过一些简单的图形界面的使用。
会一些最基础的命令(例如cd、ps、top、ls、ifconfig等这个级别的)
基础级:
可以使用一些常见的命令(touch、tail、date、find、du、fdisk、less、pidof等这个级别的命令)
图形界面操作的比较熟练,并且能够对应一部分的后台命令。
知道一些常用的配置文件的作用(如/etc/hosts、/etc/resolv.conf、/etc/passwd+、/etc/mtab等)
中级:
掌握一些高级的命令(如iftop、iptraf、rsync、ngrep、sar、acct、sg系列、inotail、nmap、lsof、ip、dig、wall、write、mkfs、grub系列、awk、sed、cron等)
熟悉一些高级配置文件的使用(如ld.conf)
熟悉proc、sys文件系统(进行诸如tcp调优等工作)
熟悉uevent(hotplug)、inotify等高级系统特性
懂得根据自己的需求配置编译内核,可以使用脚本编程
会使用iptables
高级:
可以掌握系统的高级特性。如安全的capability、suid、pam、lsm,如详细的kobject体系(可以通过查看sys文件系统取代常用的命令),netfilter的hook使用和bpf(tcpdump)和ebpf的使用),进程的优先级调度(几种实时优先级和非实时优先级的设置),oom分数的调整、cgroup
可以*的选择使用高级的文件系统(如squashfs、gfs、ecryptfs、configfs等)
熟悉X系统、ssh(转发、反弹等高级操作)、socks、tcp wrapper等远程访问相关
可以进行内核的高级定制编译(适当的修改内核代码,追踪解决内核bug)
可以编写基础的内核模块,使用常用的内核编程接口
懂得二进制原理(elf文件格式、objdump、ld、nm、strings等常用二进制软件的熟练使用)
各种bootloader的制作和安装

专家级:
此级别一般是专业方向,不是通用技能。系统的了解和预备知识的准备同样重要。例如tcp的深刻理解才会知道如何做syn的ddos防护(不是内核的那个开关),对无线理论的深刻了解,才能搞无线的内核和应用内容。
内核虚拟化、内核存储逻辑(scsi、pci、usb等专一的和串联的)、无线子系统、音频子系统、显卡子系统、电源管理子系统、网络子系统、电源管理子系统

专家级之前的一般都要学会,工作中都会用到。这里的定级也限于对linux操作系统的使用,一般是软件和系统特性,也并没有涉及太多的编程要求。编程是另外的一个方面,编程能力不好不一定linux水平不行,所以这里的定级不怎么考虑编程水平。

学习方式

使用者

对linux的使用者有用来替代windows的普通桌面用户(如使用startos),可能原因是觉得windows不好用,或者是感情问题,或者是版权问题,或者是为以后深入linux做平滑准备的。
也有命令使用者。这部分人涵盖了很多实际的工作岗位,典型的是运维。运维有很多细分的子岗位:系统管理,软件管理,后台服务器管理(例如数据库,http server),软件部署,安全等。安全有时候划入运维,有时候单独列出。所以,linux用好了就是可以当饭吃的。

桌面使用者

使用桌面可以鼠标点点就行,但是也可以用的很销魂。也有专门的职业工作就是怎么把桌面用起来。例如嵌入式里的裁剪和启动桌面(让startx命令跑起来)。

普通桌面使用者

linux的桌面有很多。普通的使用者一般会直接使用这几个桌面提供的环境和软件。

发行版

    发行版有很多,一般是不同风格或者是服务于不同目的的专用发行版。常见的通用的发行版有:Ubuntu、opensuse、fedora、debian、Mandriva、mint、。目前ubuntu大有一统之势,越来越多的使用者和开发者转向ubuntu,甚至服务器后端以前常用的centos也在慢慢的丢失市场给ubuntu server。
    专用如kali(网络渗透),puppy,lubuntu(精简),coreos,ubuntu core(虚拟化),router os(路由器)。这些专用发行版一般提供给专业用户,普通用户也可以使用,要发挥其全部威力,通常需要更多的行业知识。

桌面环境

    常见桌面环境有很多:ubuntu的unity、gnome、kde、Cinnamon、mate、lxde、xfce。一般各个发行版都会携带不同的桌面魂晶,每个桌面环境的窗口风格都是不一样的,还包括随桌面管理器携带的配套软件一般也是不一样的(例如计算器,包管理器,音乐播放器等)。但是有的流行的软件还是会被移植到不同的桌面管理器上,但是大部分没有。
    gnome、kde、unity的使用者最多。unity目前只用于Ubuntu。ubuntu也并不是只支持unity一种,几乎所有的桌面环境ubuntu都有对应的支持版本。unity的使用率随着ubuntu的普及而迅速上升。

高级桌面使用者

    高级的桌面使用者一般可以*的选择不同的桌面环境,理解每个桌面环境工作的原理,理解X系统,可以远程多终端使用X,*的选择启动甚至不启动X或者X的一部分。理解X和桌面管理器与窗口管理器的区别。

FrameBuffer

    framebuffer模式的显卡本身不具有任何运算数据的能力,他好比是一个暂时存放水的水池.CPU将运算后的结果放到这个水池,水池再将结果 流到显示器.中间不会对数据做处理.在这中情况下,所有显示任务都有CPU完成,CPU负担很重.从frambuffer这个名字我们就能猜测到他的功能 了(数据侦缓冲).
在linux内核中有有framebuffer机制,摹仿framebuffer显卡的这种功能.他的好处是把显卡的硬件结构抽象掉,把所有的显卡都当做一个”水池”来用.应用程序也可以直接读取这个水池的内容.framebuffer的设备名是 /dev/fb
可以用命令:
#dd if=/dev/zero of=/dev/fb
清空屏幕.如果你的X用的是framebuffer的驱动,屏幕将全变黑.通过移动鼠标,切换窗口等动作可以让原先的内容重新显示.
    如果你不用framebuffer驱动,可以在字符模式下做这个试验.屏幕将全部被清空.
以下的命令:
#dd if=/dev/fb of=fbfile
可以将fb中的内容保存下来,以后可以重新写回屏幕:
#dd if=fbfile of=/dev/fb
作用类似于屏幕截图,但是截下的图不能用普通图片浏览器看.

    linux字符界面在高分辨率下,启动时会有一个小企鹅logo,这个logo就是用framebuffer功能写上屏幕的.

X

Linux内核给用户提供了显示frame buffer,所有要显示的东西写到buffer去就好了。也就是内核提供了显示的机制,但是没有提供显示的内容。所以显示内容需要 操作系统去实现。
几乎所有的linux显示的核心都是X系统,X是一种显示协议,实现这个协议的常用软件是Xfree86、motif(商用),xorg、xnest等。所以X也可以实现在windows中,苹果操作系统也是用的X,只是是实现在内核中。现在的发型版一般都默认使用xorg。好多人都看到x11这个词,X11R6 实际上是 X Protocol version 11 Release 6(X协议第11版第六次发行)的意思。

命令使用者

基础的命令完成基础的功能

运维使用者

   运维人员可以说是最专业的linux使用者了,因为他们要关心linux整个系统的运行状况,是对linu系统的使用挖掘的最深的一群人。研发可能会更深入,但是一般不如运维的广度和挥洒自如。
   一个运维的基本功应该是查看系统状态的命令和脚本的编写,深入一些的运维对linux有很深刻的了解。

命令

   运维常用的查看系统资源的命令有:
- 网络
- iftop:查看ip——ip的流量。还可以交互的查看端口到端口,以及进行过滤
- netstat(可以用ss替代)
- iptraf:图形化的观看ip流量
- nethogs
- tcpdump:抓包直接打印或者保存为pcap文件,甚至可以生成bpf代码
- ngrep:把网络数据包当成grep文件一样过滤。快速查看网络数据的神器
- mascan:扫描器
- hscan:扫描器
- nmap:扫描器
- 内存
- vmstat
- io
- iostat:
- iotop
- 进程
- top:基础的进程查看工具。可以动态的查看进程的内存、cpu等系统资源。可以交互
- htop:增强的top,界面更漂亮,功能也略多
- 其他:
- sar
- lsof
- acct
- monit
   一些方便的工具应用:
nping、incron(使用inotify机制,当文件发生变化时自动执行注册脚本,对应于cron是基于时间的,incron基于文件事件),rsync(远程文件同步)、inotial(不是轮训的tail,而是基于文件变化事件的tail)

机制

  • proc文件系统和sys文件系统是运维人员的提高篇,可以从 /proc/pid/下面的文件中看到在上述命令中看到的东西,从/proc/sys/中看到和修改系统当前的参数配置。从/sys/目录下面看到系统当前的物理资源(例如通过rotational文件来判断一个设备是否是ssd),
  • sys文件系统内部的kobject、ksystem、kset机制和uevent,hotplug、udev等要熟悉
  • 可以升级linux内核,熟练使用grub
  • 熟悉使用inotify、rsync、zk、ansible等基础设置软件进行发布和同步
  • 基于对scsi在linux的重要性的了解,了解sg系列命令的使用

脚本

一般的运维人员都会让自己的shell尽量的帅起来,例如使用guake,tmux,zsh等,还要熟练使用ssh远程管理系统,以及相关配置。通常大家都是编写bash脚本。
一般的,运维人员的cron和自动化脚本都是管理的每台机器必有的。

系统管理员

系统管理员与运维人员很类似,不过运维一般出现在互联网企业,系统管理员一般出现在传统企业。一般的,系统管理员比运维更偏向于使用现有工具,而运维对系统的了解和脚本的使用更熟悉一些。

一般对升级内核要求不多,但是升级系统版本还是有的,库的部署,部署环境(docker),解决环境问题,软件发布,配置管理等。
一般对etc下的配置文件都要很熟
一个大头的工作很可能是:修电脑。。。。

后台服务管理员

例如数据库管理员、http server、ntp、dns server、ftp server等各种常见的服务器的搭建和配置管理。说起来容易,但每个软件的配置文件都是一坨。详细阅读doc,多踩坑,祝君好运。

安全使用者

linux的安全系统发展至今很全面,但是还是远远不够,linux距离一个安全的操作系统还有很长的路要走。
- 在linux中的基础安全是UGO文件权限,对于进程的能力限制capbilities。
- 提高的是内核的安全框架lsm,以及在lsm下实现的各种防火墙,例如实现flask框架的selinux和apparmor。
- 还有针对文件的,mount文件系统的时候指定acl就可以针对文件进行访问控制。还有开源的杀毒程序clamav
- 还有访问控制模块pam,可以把进程的认证工作由程序员转交给系统管理员

内核使用者

内核剪裁、编译、修改、移植
   内核的使用者多见于嵌入式开发和运维的内核升级。但是运维的内核升级一般涉及的功能裁剪较少,涉及的漏洞更新和功能增强较多。换句话说运维做内核是为了升级,嵌入式做内核是大部分为了降级。
   很重要的一点,linux内核从2.6.30版本开始,基本不适用于嵌入式系统。虽然仍声称为嵌入式应用做了诸多优化,但是业界的嵌入式开发基本停步在2.6.30版本的内核。你可以看到内核的新功能和增强基本都是为了互联网而产生的,而针对这样的内核进行裁剪也越来越难,甚至要高版本的内核在嵌入式板子上跑起来这个基本的工作也越来越复杂。这也从侧面反映出了互联网的活力和嵌入式行业的守旧。
   内核的裁剪工作最基本的不要求对内核如何实现有太多的了解,但是需要知道内核实现的那些功能有什么,为何需要。例如你得现有net设备,之后net功能才是可选,并且net功能的繁多,一个嵌入式设备根本大部分不需要。内核的编译排版很重要的一点是按照功能的层级划分的,而不是按照功能的重要性划分的。比如你会发现无线系统里rfid,led,业余无线电和wifi是平级的,但是大部分人对前三者是不会使用的,只需要wifi。但是内核的编译选项的组织并没有针对这种需求上的流行程度进行优化。
   所以一个内核裁剪者需要知道几乎所有内核选项的作用,最好多试试。至于针对修改,内核的实现大部分为了通用性,对效率和安全的考量是非常少的。如果你深入内核的代码层次的研究,你会发现内核的实现大部分在你使用的场景,你会有更优的算法。你会想去重新实现。但是,这里有一点linux内核最大的哲学:
内核的主要目的是抢占市场。
   所以,内核会覆盖尽可能多的功能,但是大部分功能的实现都不是企业级的。例如如果你的产品要支持打印机,你一般不会去用内核内置的功能,你会去购买更产品化的内核模块(例如kcodes),如果你的产品要支持samba,你会发现内核对ntfs的支持就是搞笑的,你还是会去购买商用的ntfs内核模块(商用的和开源的是同一个公司开发的)。当你多关注内核的发展时,你会发现开源发展的最好的模块一般是企业支持的,而这背后一般有商用版本存在。这就是内核的本质,出发点是开源的,共享的,发展是靠利益驱动的,繁荣则是完全靠商业的。同样的,商业又会阻碍内核的发展,所以感激尚存的为了理想而生活的程序员。他们该开个账户,我们给他捐款。0….
   移植工作考验的大部分不是内核本身的技能,而是对gcc的了解程度,尤其是内核使用的Makefile系统。所以,想要做好嵌入式内核的移植工作,编译系统和连接的paper看一遍你基本就是大牛了。中国业界的大部分从业者,都不会去看的。我也不知道为啥,他们就是不去看。。。。

开发者

   开发者就是传说中的程序员。刚开始入门的程序员一般会注重语法,helloworld就是这种心态的典型写照。不同级别的程序员用户的素质要求是不一样的。例如写业务代码,需要架构能力和编码标准。写高性能程序则需要数学,算法和高性能编程的硬件相关的知识。写实时代码又是需要另外一套理论体系。编程的语法是基础,但是编程的核心从来都不是语法。
   选择了一门好语言,基本就能确定你要用他来做的事情。不存在万金油的语言,注重效率的和注重快速开发的,注重工程管理,注重描述问题的都不是同样的语言(当然你要用C做web开发我也没办法),甚至还存在专门处理字符串最牛逼的语言(perl)。对于linux来说,linux是个平台,开发高性能代码一般就得是asm,C和C++,需要性能和开发效率折中的可以有golang,脚本化的语言也是都可以用在linux上的,那取决于业务。
   单有语言写个hello world还可以,工程编程最重要的是库。就是代码的复用。一个成熟的程序员和一个入门级的程序员的最大区别不在于语法的熟练程度,在于架构能力和库的复用能力。
   所以,linux之于开发者,不存在编程语言和库上的障碍。基本上是内核所提供的功能上的,以及你如何使用这种功能(利用epoll、inotify等)

桌面应用开发者

   linux下常见的桌面主要是kde和gnome,但是基本没人敢用他们开发工业产品,因为指不定哪天他们就没了。照目前看来,gnome伴随着ubuntu的unity用户UI统一天下的概率很大,说不定日后越来越多的厂商愿意为ubuntu的gnome开发图形界面的应用。目前linux上的产品级的应用的图形界面一般使用Qt,java(swing)等成熟的,可移植的图形库。
   所以目前来看,如果你是桌面程序员(andriod除外),你可能要用java和Qt的C++了。由于andriod也是java,所以最划算的选择是用java(外国人对java有强大的热情,因为他是最早普及的工业化的编码语言,但不代表他是目前最好的)。所以学习桌面应用开发基本上就是学习这两款产品的文档。

使用成熟库的后端开发者

   后端开发者占据了很大一部分it开发就业比例(界面,网站,等前端开发人数最多,变化最快,技术沉淀最难),几乎所有面向社会的程序都有后台服务器(很多单机程序是没有的),也是几乎所有的后台服务器都要存储数据。所以后台开发者要面对的核心开发点就是:网络使用、传输编码、数据存储和多线程编程。至于后台设计的业务,在这4大模块来看几乎可以忽略。所以后端开发技术要求比较高。而目前的服务器几乎被linux一统江湖了,这个趋势还会愈演愈烈,windows在这个领域基本没救了。
   今年golang在后端开发的流行度迅速崛起,但是大部分还是使用C/C++,python做后台开发也有相当大比例的份额(别说人家的效率不行,人家快,效率可以用钱买机器解决)。由于golang大部分使用自带的网络库,所以这节不关golang啥事了。
   网络常用的C/C++后端库是:原生的epoll、libevent、libev、boost::asio,ace,ace一般产业界没人用,较好不叫座,libev理论上比libevent高效,但是实际使用不见得。一般的工业级的开发都是使用libevent或者epoll,也有使用asio(比较少,因为C++难度高,大部分网络服务是C的)。
   传输编码问题,以前是直接使用自定义的格式或者自定义的json后面加压缩,后来发展出了序列化。再后来序列化进一步发展形成了protocolBuffer, thrift, avro等大公司主导的传输格式。目前一般要用网络传输数据protocol buffer用的最多,thrift强势崛起,avro刚刚起步,但是特性不俗。
   数据存储问题。mysql几乎是大小系统的第一选择。非常小的可能会使用sqlite,涉及到非IT大型企业可能用商用数据库比较多(自己不能开发,卖他的人又得赚钱),nosql里mongodb用的比较多,但是近年各种nosql数据库千奇百怪。有专门存放图的数据库,也有存储下载内容的分级的(rocksdb),也有存储地理信息的等等。如果你是专业方向的开发者,可能这些专门的数据库更适合你。
   多线程编程,在C/C++的世界里没有太多的选择,一般是pthread,C++可以用boost:thread或者C++11的thread,其后台也是pthread,pthread基本可以一统江湖。
   所以使用库的linux开发者只需要了解库的用法,当然对库后台是怎么调用操作系统具体实现的,能够了解也是非常好的。

系统级后端开发者

   如果你打算看看你的发行版上装的那些莫名其妙的库是用来干嘛的,例如libncurses,libnss,libfuse等,而这些一般你平时开发应用程序都用不到,那么你基本做的就是系统级的后端开发了。系统开发与操作系统的关联很大,学习系统开发就是在学习操作系统。
   系统开发对内核信息的获取要通过proc和sys,这是一定要熟练掌握的。这两个proc还比较容易,但是内容也很多。sys则比较庞杂,内容更多。例如你得清楚的知道/proc/sys/kernel/core_pattern里面存的是core dump的路径,ulimit -c可以用来设置core的大小,默认是0.这些基础的背景知识以及整个文件系统衍生出来的知识点是系统级后端开发的基础。
   这些常用的开发内容包括:fifo文件,uevent,inotify,netlink,nice(实时进程),cpu亲和度,cgroup虚拟化,ptrace进程跟踪,子进程创建和控制,信号处理,文件锁,向量化的读写文件,文件描述符操作,socket调用,epoll,文件与目录链接控制,锁,磁盘配额校验,进程记账,权限控制,运行优先级(io和cpu),低级端口操作和sg直接发scsi命令,交换分区控制,pdflush,kswapd等内核进程的调优,模块的装载与卸载,内存映射与加锁,cache操作,直接的网络操作,用户管理,消息队列、信号量与共享内存等。
   系统级的后端开发直接是面向内核的使用,也就是系统级的开发者基本就是内核的合格使用者。

运维开发者

   运维开发者比较接近于系统开发者,但是运维开发者比较多的使用cron命令,脚本,着重于系统资源的监控和划分。现在流行的devops例如ansible工具让运维与开发一气呵成。运维开发者首先是一个运维使用者,运维系统例如全网监控系统,包发布系统,主机探测系统,域名系统等都是运维开发者的方向。一个运维开发者不做具体业务,也不是直接为具体业务服务,而是让具体业务可以专注于具体业务。
   一般的技能要求:常用运维命令,脚本,python

安全开发者

   安全开发者有两种,一种是如何让自己开发的软件更安全,另一种是就是开发安全防护软件。例如病毒扫描,防火墙,入侵检测,漏洞管理,权限控制等。除了对安全使用者的技能的掌握外,还需要更深入的了解白帽子们的安全防护细节和原理。通常能防的人也能攻,不知道别人怎么攻就在防基本是瞎防。攻防是互动进步的。
   内核里有很多针对安全开发的特性提供:内核加密接口和秘钥环、ASLR(进程启动栈随机化)、LSM机制。做安全开发对系统本身的特性利用不大,对攻防手法的理解要求比较大。另外,防御系统一般是在业务的前面,所以要求低延时和高吞吐。所以基本上只能使用C/C++(如果你见到哪个防火墙用python写的,麻烦给我膜拜一下)。
   所以安全开发的核心是业务和高效编程的能力。而高效编程例如对dpdk、sse指令集的使用就是一个专门的学科了。业务就是安全相关的知识点。

应用后台开发者

   大部分应用的后台就有两个要求:开发快,问题少。所以现在的市面上你会见到大把的时候golang、python甚至java做后台开发的案例。这种形式的后台开发基本与操作系统无关,懂得基本的linux系统使用即可。人们可以专注的面向业务。

内核开发者

   内核开发者的难度应该是最难的,如果谁提交了一个patch被内核接受了,那是很了不起的事情。因为内核本身进展就非常大,并且内核开发没有市场上的职业对应,基本全靠偶然接触或者兴趣。职业最多的是驱动开发和内核裁剪小修改,另外文件系统开发和网络开发对内核也涉及到一些内核开发。由于内核的庞杂和耦合性比较重,学习本身就很难了,更别说开发。但是如果你已经可以开发了,那很多牛逼的开发就可以如探囊取物了,就像写一个C++程序那么顺手。

驱动开发者

   驱动开发对uevent、kobject系统的了解需求比较多,明白udev程序和dev目录的工作原理,设备号的管理,基本的内存申请和使用。进阶的可以了解内核socket编程,进程的控制等。内核驱动的编程最主要的还是业务,要知道你控制的设备的寄存器和对应的总线在内核中的逻辑。例如所有磁盘都是使用scsi命令,都要经过scsi层,usb,pci等总线的运作和内核接口的使用。

文件系统开发者

   由于一切皆文件的思想,所以文件系统在linux中特别重要。你得可以创建虚拟的设备,得学会利用dev下的设备,重要的,你得学会使用fd。这个fd就是简单的C里面open一个文件之后生成的那个,但是也是socket()之后生成的那个,由于是系统资源,所以fd是跨进程的,你只要知道fd的号码,就可以在其他进程中直接使用,比如0,1,2(实际不是这个号码)号的fd就是输入输出和错误。所以通过shell其实可以做好多事情(O(∩_∩)O)
   内核层次的文件系统开发,必须得了解文件使用的整个流程,一切皆文件只是最上面的vfs层,往下还有通用块层(在这里要进行重要的电梯算法),还要scsi层,要把对逻辑文件的访问变为对物理存储访问的命令,还要pci层,还要usb层。逻辑成真总要经过物理。所以物理协议实现的了解也是必须的。
   内核中提供了很多默认的文件系统操作,很多实现的文件系统都直接使用的默认的实现。有一类重要的文件系统是fuse,在用户端实现的,像个程序一样。这是内核为版权保护做的折中。内核现在越来越多的倾向于把功能让给用户空间,大内核的思想在收缩。因为开源的力量在一个点上确实不如商业高效。
   文件系统一般可以以模块的方式提供,可以很简单也可以很复杂。所以文件系统开发对内核的了解与其他差别不大,但是对文件系统本身有比较高的知识储备要求。例如完整性校验,extends大块,磁盘配额,磁盘访问控制acl,热插播,B+树等。

网络开发者

   Linux内核本身的网络协议栈比较低效,但是可以应付绝大多数的使用情况。使用内核原生的协议栈时,一般是使用netfilter的hook,用内核的模块做一些事情,对内核代码本身的修改是不建议的。具体做安全还是包变换,有很多netfilter的iptable本身就可以做,bpf更是提供了可编程的规则。所以内核层面的网络开发核心是netfilter。
   对于有高性能要求的,一般有新浪的fastsocket和intel的dpdk这两种协议栈可以供选择。fastsocket目前还不支持长连接,但是nginx这种短连接应用会收益良多。dpdk没有socket的概念,纯粹的包处理,而且是在用户空间,完美的支持多CPU和numa系统。所以你可以看到阿里,腾讯,谷歌,百度等都是用dpdk来做的网络。