那年,学习《Linux内核修炼之道》——子系统的初始化:那些入口函数
《Linux内核修炼之道》精华版
早上上班坐地铁要排队,到了公司楼下等电梯要排队,中午吃饭要排队,下班了追求一个女孩子也要排队,甚至在网上下载个什么门的短片也要排队,每次看见人群排成一条长龙时,才真正意识到自己是龙的传人。
那么下面咱们就说说队列(链表)。
使用链表的目的很明确,因为有很多事情要做,于是就把它放进链表里,一件事一件事的处理。
比如在USB子系统里,U盘不停的提交urb请求,USB键盘也提交,USB鼠标也提交,那USB主机控制器咋应付得过来呢?很简单,建一个链表,然后你每次提交就是往里边插入,然后USB主机控制器再统一去调度,一个一个来执行。
这里有力得证明了,谭浩强大哥的C程序设计是我们学习Linux的有力武器,书中对链表的介绍无疑是英明的,谭大哥,您不是一个人在战斗!内核中链表的实现位于include/linux/list.h文件,链表数据结构的定义也很简单。
21 struct list_head {22 struct list_head *next, *prev;23 };list_head结构包含两个指向list_head结构的指针prev和next,由此可见,内核中的链表实际上都是双链表(通常都是双循环链表)。
通常,我们在数据结构课堂上所了解的链表定义方式是这样的(以单链表为例):struct list_node {struct list_node *next;ElemType data;};通过这种方式使用链表,对每一种数据类型,都要定义它们各自的链表结构。
而内核中的链表却与此不同,它并没有数据域,不是在链表结构中包含数据,而是在描述数据类型的结构中包含链表。
比如在hub驱动中使用struct usb_hub来描述hub设备,hub需要处理一系列的事件,比如当探测到一个设备连进来时,就会执行一些代码去初始化该设备,所以hub就创建了一个链表来处理各种事件,这个链表的结构如下图。
(1)声明与初始化。
链表的声明可以使用两种方式,一种为使用LIST_HEAD宏在编译时静态初始化,一种为使用INIT_LIST_HEAD()在运行时进行初始化。
Linux学习之Workqueue
struct global_cwq {} 每个CPU都有一个gcwq,主要完成除了内存回收的紧急工作队列任务,
和在CPU下线过程中的工作任务这两个任务外的所有任务。
这两个任务是极高优先级和极紧迫任务,他们将会被worker直接执行,避免被管理。
struct cpu_workqueue_struct {} 特定CPU上的工作队列
struct wq_flusher {} 刷新清空结构体,不重要。
struct workqueue_struct {} 工作队列结构体,直接由work_struct结构体构成,如果是多个CPU,
将工作项加入到工作队列中:
extern int queue_work(struct workqueue_struct *wq, struct work_struct *work);
任务进到自定义的队列中。其实质也是调用了一个queue_work_on。
则先由work_struct构成cpu_workqueue_struct,然后再构成该workqueue_struct。
系统在启动是,启动了六个公共的默认共享工作队列。
-> 执行核心process_one_work()。
PS,根据标志位优先执行高优先级任务所开辟的thread,也通过process_one_work()。
用该宏创建一个队列。 --> alloc_workqueue() --> __alloc_workqueue_key() 位置:workqueue.h
#define create_freezable_workqueue(name) alloc_workqueue((name), WQ_FREEZABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, 1)
嵌入式实验心得体会
嵌入式实验心得体会学期开始,我们开始学习《嵌入式系统及应用》,由于初次接触嵌入式系统,感觉蛮难的,所以收获不是很大,很多的概念都比较模糊,等到学期完毕开始做嵌入式课程设计时,真是茫然无从下手,自从拿到设计主题后,我就像热锅上的蚂蚁,一个字“急”。
最后实在没有方法,逼着自己去学习,查资料,总算对嵌入式有了浅层理解。
嵌入式系统本身是一个相对模糊的定义,一个手持的MP3和一个PC104的微型工业控制计算机都可以认为是嵌入式系统。
总体来说,嵌入式系统是“用于控制,监视或者辅助操作机器和设备的装备”。
一个典型的桌面Linux系统包括3个主要的软件层---linux内核、C库和应用程序代码。
内核是唯一可以完全控制硬件的层,内核驱动程序代表应用程序与硬件之间进展会话。
内核之上是C库,负责把POSIX API转换为内核可以识别的形式,然后调用内核,从应用程序向内核传递参数。
应用程序依靠驱动内核来完成特定的任务。
在了解了根底知识之后,我开始进展上机操作,当然,其中遇到很多的难题,很多东西都是第一次接触,又没有别人在旁边指导操作,完全凭借自己去摸索练习。
其中的困难可想而知。
然而坚持就是胜利,牙一咬眼一闭坚持做下去,而通过本次实验,我感觉收获还是蛮多的。
可能我对于嵌入式的知识学习的还是不太多,但是这之外的东西收获颇丰。
它让我学会了如何通过自己的努力去认知一个新事物,更重要的是端正自己的学习态度,只有真正下功夫去学习,才能有收获,正所谓“一份耕耘,一份收获。
”没有付出,何谈回报呢?再者,通过本次实验,我也学会了如何去分析问题,如何找出自己设计中的缺乏,继而去排除解决问题,这就是一个自我学习的过程。
当我们通过实验去学习理论知识时,自己动手得出的结论,不仅能加深我们对嵌入式的理解,更能加深我们对此的记忆。
当然,在这其中,我也发现自己的许多缺乏之处,由于学期伊始我没有好好学习,才落到如此地步,这也可以说是一个教训吧!我相信在以后的学习工作中,我一定会端正自己的学习态度,一丝不苟的去对待每一件事。
linux内核-input子系统解析
linux内核input子系统解析华清远见刘洪涛Android、X windows、qt等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过、或越来越倾向于标准的input输入子系统。
因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量不大。
但如果你想更灵活的应用它,就需要好好的分析下input子系统了。
一、input输入子系统框架下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core ),驱动层和事件处理层(Event Handler)三部份组成。
一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。
注意:keyboard.c不会在/dev/input下产生节点,而是作为ttyn终端(不包括串口终端)的输入。
二、Input driver编写要点1、分配、注册、注销input设备struct input_dev *input_allocate_device(void)int input_register_device(struct input_dev *dev)void input_unregister_device(struct input_dev *dev)2、设置input设备支持的事件类型、事件码、事件值的范围、input_id等信息参见usb键盘驱动:usbkbd.cusb_to_input_id(dev, &input_dev->id);//设置bustype、vendo、product等input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件类型input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED事件支持的事件码for (i = 0; i < 255; i++)set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY事件支持的事件码include/linux/input.h中定义了支持的类型(下面列出的是2.6.22内核的情况)#define EV_SYN 0x00#define EV_KEY 0x01#define EV_REL 0x02#define EV_ABS 0x03#define EV_MSC 0x04#define EV_SW 0x05#define EV_LED 0x11#define EV_SND 0x12#define EV_REP 0x14#define EV_FF 0x15#define EV_PWR 0x16#define EV_FF_STATUS 0x17#define EV_MAX 0x1f一个设备可以支持一个或多个事件类型。
鸟哥的linux私房菜答案
鸟哥的linux私房菜答案【篇一:鸟哥的linux私房菜-基础篇-学习总结】s=txt>6.1 用户与用户组的定义6.2 linux文件权限概念6.2.1 linux文件权限与属性1.一般权限1)对文件,r指用户可读取文件内容;w指用户可以编辑、新增、修改文件内容但不能删除文件;x指用户可以执行该文件。
2)对目录,r指用户可以查询目录结构(ls);w指用户可对目录下的文件删除、重命名、转移、新建等。
x指用户能进入该目录,使其成为工作目录2. 特殊权限1)setuid:(-rwsr-xr-x)使文件(仅可执行文件)在执行过程中,执行者拥有文件所有者的权限。
2) setgid:(rwxrwsr-x)对于文件,仅对可执行文件而言,在执行过程中,执行者拥有文件用户组的权限。
对于目录,用户需同时拥有r与x权限才能进入该目录,用户在此目录下的有效用户组变为该目录的用户组,用户在此目录下新建的文件的用户组与此目录的用户组相同。
3) sbit:(drwxrwxrwt)对于目录,目录下的文件或目录,只有文件/目录所有者和root能够进行删除动作。
4)特殊权限的设置:4 setuid、2 setgid、1 sbit(chmod 7777 filename)。
3. 隐藏权限1) chattr +(-)i filename:使文件不能被删除、改名、设置链接文件、写入或添加数据。
2) chattr +(-)a filename:使文件只能增加数据。
3)4. 默认权限1)文件初始默认权限为666,目录初始默认权限为777.umask设置默认权限,如umask为0022,则文件默认权限为644(rw-r—r--),目录默认权限为755(rwxrwxrwx).6.2.2如何改变文件和目录的权限与属性1. chgrp1) chgrp grpname file/dirname: 改变文件/目录的所属用户组2. chown1) chown ownname file/dirname:改变文件/目录的所属用户3. chmod1) chmod 777 file/dirname:改变文件/目录的所属用户的权限4. touch命令1) touch -d “2 days ago” filename:修改文件时间2) touch -t 0709150202 filename:修改文件时间3) touch filename:新建文件4) modification time (mtime):文件数据内容更改时间5) status time (ctime):文件权限属性更改时间6) access time (atime):文件读取时间6.3 linux目录配置6.3.1 linux目录配置标准fhs(filesystem hierarchy standard) fhs是一种目录规划标准。
Linux内核架构和工作原理详解
Linux内核架构和工作原理详解作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。
目前支持模块的动态装卸(裁剪)。
Linux内核就是基于这个策略实现的。
Linux 进程采用层次结构,每个进程都依赖于一个父进程。
内核启动init程序作为第一个进程。
该进程负责进一步的系统初始化操作。
init进程是进程树的根,所有的进程都直接或者间接起源于该进程。
virt/ ---- 提供虚拟机技术的支持。
Linux内核预备工作理解Linux内核最好预备的知识点:懂C语言懂一点操作系统的知识熟悉少量相关算法懂计算机体系结构Linux内核的特点:结合了unix操作系统的一些基础概念Linux内核的任务:1.从技术层面讲,内核是硬件与软件之间的一个中间层。
作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。
2.从应用程序的层面讲,应用程序与硬件没有联系,只与内核有联系,内核是应用程序知道的层次中的最底层。
在实际工作中内核抽象了相关细节。
3.内核是一个资源管理程序。
负责将可用的共享资源(CPU时间、磁盘空间、网络连接等)分配得到各个系统进程。
4.内核就像一个库,提供了一组面向系统的命令。
系统调用对于应用程序来说,就像调用普通函数一样。
内核实现策略:1.微内核。
最基本的功能由中央内核(微内核)实现。
所有其他的功能都委托给一些独立进程,这些进程通过明确定义的通信接口与中心内核通信。
2.宏内核。
内核的所有代码,包括子系统(如内存管理、文件管理、设备驱动程序)都打包到一个文件中。
内核中的每一个函数都可以访问到内核中所有其他部分。
目前支持模块的动态装卸(裁剪)。
Linux内核就是基于这个策略实现的。
哪些地方用到了内核机制?1.进程(在cpu的虚拟内存中分配地址空间,各个进程的地址空间完全独立;同时执行的进程数最多不超过cpu数目)之间进行通信,需要使用特定的内核机制。
系统初始化函数集(subsys_initcall)和初始化段应用
系统初始化函数集(subsys_initcall)和初始化段应用前言:前段时间做一个项目需要设计一个动态库,并希望在加载库的同时自动执行一些初始化动作,于是联想到了linux内核众子系统的初始化,于是研究之,并在过这程中发现了初始化段的存在,利用初始化段实现了该功能。
工作一年,笔记积累多了,慢慢变得杂乱无章,于是开博,一方面整理笔记,梳理知识,另一方面和大家交流,共同进步。
keyword:subsys_initcall, init, init_call1 系统初始化调用函数集分析(静态)1.1 函数定义在linux内核代码里,运用了subsys_initcall来进行各种子系统的初始化,具体怎么初始化的呢?其实并不复杂。
以 2.6.29内核作为例子。
在<include/linux/init.h>下就能找到subsys_initcall的定义:#define pure_initcall(fn) __define_initcall("0",fn,0)#define core_initcall(fn) __define_initcall("1",fn,1)#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)#define postcore_initcall(fn) __define_initcall("2",fn,2)#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)#define arch_initcall(fn) __define_initcall("3",fn,3)#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)#define subsys_initcall(fn) __define_initcall("4",fn,4)#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)#define fs_initcall(fn) __define_initcall("5",fn,5)#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)#define device_initcall(fn) __define_initcall("6",fn,6)#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)#define late_initcall(fn) __define_initcall("7",fn,7)#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)而__define_initcall又被定义为#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn所以subsys_initcall(fn) == __initcall_fn4 它将被链接器放于section .initcall4.init 中。
Linux内核初始化流程笔记
分类: LINUXgfree.wind@gmail博客: 本文的copyleft归gfree.wind@gmail所有,利用GPL发布,能够自由拷贝,转载。
但转载请维持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
===================================================== =================================================如前文的流程,bootloader将kerenel加载到内存中。
全数引导进程是四步1:boot PROM phase2:boot Programs phase3:kernel initialization phase4:init phasesystem初始化,检测和,检查设备和创建设备树,设置consolekernel初始化进程kernel self -initialization 内核自检loading of kernel modules 载入内核模块reading of the kernel configuration file in /etc/system 读内核配置文件staring of the /sbin/init process 运行/sbin/init进程bootblk是用于装载第二个引导程序ufsboot的主引导程序bootblk是被PROM的boot设备的引导扇区装载的ufsboot程序是用了装载两部份核心genunix和unix的installboot是用来在磁盘分区上安装bootblk的genunix is the platform-independent generic kernel file ,while unix is the platform-specific kernel component.整个的引导进程:PROM from the boot sector of the boot device-->bookblk-->ufsboot-->genunix(是一个独立平台的一般内核文件) and unix(是一个特殊平台内核文件)-->其它那个地址加载的kernel镜像,并非是真正的可执行文件,而是一个紧缩的镜像文件。
linux操作系统( 课后习题答案)
1.简述linux的内核版本号的构成。
答:由3个部分数字构成,其形式如下Major.minor.patchlevel major:表示主版本号,通常在一段时间内比较稳定。
minor:表示次版本号,如果是偶数,代表这个内核版本是正式版本,可以公开发行;而如果是奇数,则代表这个内核版本是测试版本,还不太稳定仅供测试。
patchlevel:表示修改号,这个数字越大,则表明修改的次数越多,版本相对更完善。
2.如何理解linux发行版本含义?它由哪些基本软件构成?答:linux的基础是其内核,但光有内核是无法满足用户需要的,必须构成发行套件,即发行版。
系统引导管理程序(Boot Manager、用户界面、X-Window系统、系统管理、Internet服务、文件和打印服务、应用程序、工具和库程序3.linux的运用领域主要有哪些?答:Intranet 、服务器、嵌入式系统、集群计算机等方面 4.Linux主要特点。
答:多用户、多任务、多平台、漂亮的用户界面、硬件支持、强大的通信和联网功能、应用程序支持4.X Window由哪3个部分组成?分别有何功能?答:Server(服务器)、Client(客服端)、通信通道Server:控制实际显示器和输入设备的程序。
Client:Client是使用系统窗口功能的一些应用程序。
通信通道:负责Server与Client之间的信息传输。
5.什么是桌面环境?linux下的桌面环境主要有哪两种?答:为用户管理系统、配置系统、运行应用程序等提供统一的操作平台。
Linux最常用的桌面环境:KDE和GNOME。
6.在GNOME桌面环境下如何获取帮助信息?答:(1)GNOME桌面环境提供帮助浏览器程序help,单击【主菜单】——【帮助】命令即可启动,单击文字链接可查看相关的联机帮助信息。
(2)如果已安装文档光盘,则可单击【主菜单】——【文档】命令,选择查看已安装的文档。
(3)当运行运用程序时,单击该程序的【帮助】——【目录】或者【目录内容】也可查看该程序的帮助信息。
linux系统的初始化
Linux系统的初始化本文的内容是讲述从Linux系统启动部分。
即当你按下power键到你看到login:的整个过程牵扯的东西很多很多。
所以专门写一个专题来将将这个东西。
本文不局限于Linux的启动过程的分析,同时兼顾对系统结构方面的介绍。
本文的另外一个目的是为了自己找工作,复习一下相关的知识。
与这个部分相关的代码主要是:1、Bootsect.s,setup.S和head.S2、Init 目录下面的main.c/arch/i386下面的其他代码也是很重要的。
1、基础知识1.1 386的体系结构80386的芯片内部被分为独立的六个处理部件:总线接口部件,执行部件和分段部件,分页部件,指令预取和指令译码部件。
其中分段部件和分页部件统称为MMU内存管理部件。
这六个部件是并行工作的,构成了一个6级流水体系结构,从而大大提高了处理效率。
对于程序设计人员来讲要了解386的寄存器结构要比知道物理上的实现更加重要。
80386的内部有30多个寄存器。
其中可以分为如下五类:通用寄存器,段寄存器,系统地址寄存器,控制寄存器,以及调试和测试寄存器。
1.1.1通用寄存器:在应用程序用来对程序的数据进行存取和计算。
80386有8个32位的通用寄存器,如下图1.2所示。
他们是在16位寄存器的扩展。
故命名为EAX,EBX,ECX,EDX,EBP,ESI,EDI 和ESP。
数据可以是1位,8位,16位,32位和64位(MMX)。
访问寄存器的尺寸可以为8位,16位,和32位,并给不同的尺寸赋予不同的名字。
比如EAX是32位,AX是16位,AL和AH是8位。
两个附加寄存器保留了当前指令流的状态信息。
EIP寄存器包含的是当前指令的下一条指令的偏移地址。
EFLAGS中包含了与各个指令相关的信息位。
EIP或IP与CS段寄存器共同指出了下一条被执行指令的存储器地址。
EFLAGS的寄存器包含如下字段:图1.1 EFLAGS寄存器(图来源于IA32手册)CF 进位标志PF 奇偶标志当某个算术运算操作生成有偶数个1时,该位被置位AF 辅助进位标志当AL的寄存器低半个字节发生进位时被置位。
Linux系统详解 系统的启动、登录注销、与开关机
Linux系统详解第六篇:系统的启动、登录注销、与开关机前言:本系列文章取材广泛,有来自于互联网的,有来自教科书的,有来自自己的笔记的,也有来自自己对Linux的经验积累的。
此系列的文章都是经过长时间的整理并多次修改后发布。
本文章的目的是为了让刚入门Linux的读者能够快速的入手而无需到此搜索网络和寻找教材,本系列文章也适应已经对Linux有一定的基础的读者加深印象或者说是回顾吧。
由于个人的力量是有限的,所以在编写过程中难免有错别字或者疏漏的地方,希望众大读者如果发现本文章有什么疏漏的地方请指出,以让把文章修改得更好,从而让更多的读者在阅读的时侯减少阅读误解。
就像Linux的诞生一样也是经过大众的参与而逐步发展并强壮起来的,一人为”人”两人为”从“三人为”众“。
众志成城!所以让我们,共同努力,共同学习,共同进步;快乐分享,快乐工作,快乐成长!这可是我一直倡导的主题精神O!本篇学习目标∙了解Linux系统的启动原里。
∙了解Linux系统的终端。
∙熟悉Linux的登录和注销;关机、重启、系统基本操作。
∙熟悉Linux系统中用户的切换,和查看系统中和登录用户有关的其他操作。
正文:了解Linux系统的启动原里。
Linux的启动过程简介:当用户打开计算机的电源的时候,首先会进行BIOS自检,根据BIOS中设置的启动设备进行启动。
接着计算机读取硬盘上的MBR记录冰启动设备上安装的引导程序GRUB或者LILO,引导程序接着开始引导Linux系统。
此时,BIOS会将CPU的控制权交给Linux系统,Linux 系统重新进行硬件自检让后引导内核,接下来执行init父进程,系统会根据/etc/inittab文件选择系统的启动级别并调用rc.sysinit和/etc/rc目录中的脚步。
最后到达用户的登录界面,用户输入正确的凭据后,就进入系统。
到此,Linux启动启动完成。
上面只是简单的介绍了Linux系统启动过程,下面让我们对Linux系统的启动过程进行详细的讲解。
Linux内核修炼之道
开本:7871092 印张:22.75 字数:537 千字 印数:1 –
000 册
ISBN 978-7-115-22585-6 定价: 元 读者服务热线:(010)67132692 印装质量热线:(010)67129223 反盗版热线:(010)67171154
推荐序
推荐序
利用开源代码软件的优势推动创新
华清远见教育集团总裁
季久峰
Linux 内核修炼之道
Preface
Open Source and Innovation
Google wouldn’t be the company it is today without Open Source Software .As a leading Internet technology company, we heavily use and release Open Source code into existing communities and creating new projects like Chrome, Android and others. As such, we value and try to encourage Open Source development and we see the participatory model of technology development as a key factor in the development of the Internet and in computer science. I congratulate Farsight on the publishing of "The Zen of Linux Internals" and it is my hope that this book will foster further interest in Linux and Open Source among the developer communities in China. I look forward to seeing what new Chinese open source developers there have in store for the us all. Best wishes to you all, developers in China!
Linux系统内核模块函数调用及命名空间
Linux系统内核模块函数调用及命名空间Linux系统内核模块函数调用及命名空间2010-07-20 19:14内核模块是如何开始和结束的用户程序通常从函数main()开始,执行一系列的指令并且当指令执行完成后结束程序。
内核模块有一点不同。
内核模块要么从函数init_module或是你用宏module_init指定的函数调用开始。
这就是内核模块的入口函数。
它告诉内核模块提供那些功能扩展并且让内核准备好在需要时调用它。
当它完成这些后,该函数就执行结束了。
模块在被内核调用前也什么都不做。
所有的模块或是调用cleanup_module或是你用宏module_exit指定的函数。
这是模块的退出函数。
它撤消入口函数所做的一切。
例如注销入口函数所的功能。
所有的模块都必须有入口函数和退出函数。
既然我们有不只一种方法去定义这两个函数,我将努力使用"入口函数"和"退出函数"来描述它们。
但是当我只用init_module和cleanup_module时,我希望你明白我指的是什么。
模块可调用的函数程序员并不总是自己写所有用到的函数。
一个常见的基本的例子就是printf()你使用这些C标准库,libc提供的库函数。
这些函数(像printf())实际上在连接之前并不进入你的程序。
在连接时这些函数调用才会指向你调用的库,从而使你的代码最终可以执行。
内核模块有所不同。
在hello world模块中你也许已经注意到了我们使用的函数printk()却没有包含标准I/O库。
这是因为模块是在insmod加载时才连接的目标文件。
那些要用到的函数的符号链接是内核自己提供的。
也就是说,你可以在内核模块中使用的函数只能来自内核本身。
如果你对内核提供了哪些函数符号链接感兴趣,看一看文件/proc/kallsyms。
需要注意的一点是库函数和系统调用的区别。
库函数是高层的,完全运行在用户空间,为程序员提供调用真正的在幕后完成实际事务的系统调用的更方便的接口。
【读书笔记】Linux系统管理初学者指南读书笔记1——第1-2章
【读书笔记】Linux系统管理初学者指南读书笔记1——第1-2章本博客记录⼀下《Linux系统管理初学者指南》这本书的读书笔记,最近由于想做⼀些关于嵌⼊式的项⽬,所以需要对Linux系统进⾏学习,正好之前买过这本书,内容还可以,能作为⼊门阅读第1章了解并安装Linux系统Linux系统的发展与特点Linux的发展历史Multics计划UNIX系统:1970年为UNIX元年,1970年1⽉1⽇0:00在UNIX系统中作为计算机时间的起点类UNIX操作系统MINIX系统Linux系统:Linus系统内核:负责完成操作系统基本功能的程序,主要作⽤是负责统⼀管理计算机中的硬件资源,提供⽤户擦操作界⾯,提供应⽤程序的运⾏环境Linux系统中的内核程序称为KernelGNU计划:世界知名的⾃由软件项⽬,决定了Linux系统⾃由、开放的属性Kernel作为Linux系统的核⼼,只能实现系统的基本功能,⽽⼀个完整的Linux系统应该包括Kernel和应⽤程序两部分,⽽⽆论是Kernel还是Linux,其中的绝⼤部分应⽤程序属于GNU计划,遵守GNU计划中的GPL或LGPL协议Linux的发⾏版本:作为⽤户使⽤的都是运⾏在内核之上的各种应⽤程序,很多公司或组织在Linux内核的基础上添加了各种管理⼯具和应⽤软件,构成了⼀个完整的操作系统将系统内核和应⽤软件封装在⼀起的操作系统成为Linux发⾏版,⼀般平时接触和使⽤的Linux系统都是Linux的发⾏版RedHat LinuxFedoraRHEL(RedHat Enterprise Linux)CentOS(Community Enterprise Operation System,社区企业操作系统)DebianUbuntuKali LinuxSUSELinux系统较为⼴泛的应⽤是⽹络服务器以及各种嵌⼊式系统在VMware Workstation安装虚拟机设置虚拟机的⽹络环境:桥接(bridged)模式:虚拟机像是⼀个独⽴主机,与物理主机是同等地位,可以通过物理主机的⽹卡访问外⽹,外部⽹络中的计算机也可以访问此虚拟机,桥接模式对应的虚拟⽹络名称为VMnet0仅主机(host-only)模式:仅主机模式对应的是虚拟⽹络VMnet1,这是⼀个独⽴的虚拟⽹络,与物理⽹络之间是隔离开的,所有设为仅主机模式的虚拟机之间以及虚拟机与物理主机之间可以相互通信,但它们与外部⽹络中的主机⽆法通信NAT(⽹络地址转换)模式:NAT模式对应的虚拟⽹络是VMnet8,物理主机就像⼀台⽀持NAT功能的代理服务器,⽽虚拟机就像NAT的客户端⼀样,虚拟机可以使⽤物理主机的IP地址直接访问外部⽹络中的计算机,但由于NAT技术的特点,外部⽹络中的计算机⽆法主动与NAT模式下的虚拟机进⾏通信Linux系统的基本操作⼀个完整的Linux系统是由Kernel和各种应⽤软件组成的,Linux系统的桌⾯环境(X Window)也是由应⽤软件来提供的,负责X Window桌⾯环境的软件主要有两个:GNOME和KDEX Window只是Linux系统的⼀个应⽤软件,并没有集成到Linux的内核中,因此⽤户可以根据需要选择是否运⾏图形界⾯ShellLinux系统的字符界⾯称为Shell(Shell本来是指系统的⽤户界⾯,为⽤户与系统内核进⾏交互操作提供了⼀种接⼝,图形界⾯和字符界⾯都属于Shell,图形界⾯的Shell称为GUI,字符界⾯的Shell称为CLI,由于Linux系统的操作以字符界⾯为主,因此Shell通常专指CLI)Shell是Linux系统中的⼀个应⽤程序,将⽤户输⼊的命令解释成系统内核能理解的语⾔,命令执⾏后再将结果以⽤户可以理解的⽅式显⽰出来Linux系统中负责提供Shell功能的软件有很多,⽐如sh、Csh、Zsh和Bash等,CentOS系统中默认使⽤的是Bash启动ShellLinux是⼀个多⽤户操作系统,可以同时接受多个⽤户登陆,⽽且还允许同⼀个⽤户从不同的终端进⾏多次登陆,在使⽤安装Linux系统的计算机时,虽然⽤户⾯对的也是⼀个物理终端设备,但可以在这⼀个物理终端上通过虚拟终端打开多个互不⼲扰,独⽴⼯作的页⾯Linux中提供的虚拟终端默认有6个,其中第1个是图形界⾯,第2-6个则是字符界⾯,可以通过Ctrl+Alt+F1-F6在不同的虚拟终端进⾏切换,同时在字符界⾯下执⾏startx命令也可以进⼊图形界⾯(在Ubuntu中Ctrl+Alt+F1-F6代表不同的虚拟终端,⽽Ctrl+Alt+F7则是返回图形界⾯,书中描述的是CentOS)虚拟终端的缩写为tty,执⾏tty命令就可显⽰⽤户⽬前所在的终端编号除了虚拟终端外,还有⼀种启动Shell的⽅式称为伪终端,缩写为pts,在图形界⾯中,右键打开终端即可弹出⼀个运⾏在图形环境中的字符界⾯窗⼝,这就是⼀个伪终端,在其中执⾏tty命令,其中的结果为/dev/pts/0,表⽰这是系统启动的第⼀个伪终端(伪终端的编号从0开始)启动Shell后,可以看到类似于zyh@zyh-virtual-machine:~$的命令提⽰符,命令提⽰符是Linux字符界⾯的标志zyh:表⽰当前登录的⽤户账户zyh-virtual-machine:表⽰本机的主机名是⼀个特殊符号,泛指⽤户的家⽬录):表⽰⽤户当前所在的位置,即⼯作⽬录($:表⽰当前登录的是普通⽤户,如果是管理员⽤户则变为#可以通过Ctrl+C键发出中断信号来中断命令的运⾏Shell命令格式Shell命令由命令名、选项和参数共3个部分组成:命令名 [选项] [参数](命令名、选项和参数之间必须⽤空格分隔)命令名:描述命令功能的英⽂单词或缩写选项:调节命令的具体功能选项通常以-开头,当有多个选项时,可以只使⽤⼀个-符号部分选项以--开头,这些选项通常是⼀个单词或词组少数命令的选项不需要-符号参数:命令的处理对象,可以是⽂件、⽬录或⽤户账号等内容在Shell中,⼀⾏可以输⼊多条命令,命令之间⽤分号分隔,如果在⼀⾏命令后加上****符号,就表⽰另起⼀⾏继续输⼊通过上下⽅向键可以找出曾经执⾏过的命令关机与重启命令关机:poweroff重启:rebootshutdown:既可以⽤来关机⼜可以⽤来重启⽴即关闭系统:shutdown -h now⽴即重启系统:shutdown -r now15分钟后关闭系统:shutdown -h +1510分钟后重启系统,并将信息发给登陆到本机中的各⽤户:shutdown -r +10 'The system will be rebooted!!'对于延时运⾏的关机重启操作,可以⽤shutdown -c命令或Ctrl+C组合键取消相⽐于poweroff和reboot命令,shutdown命令在执⾏前会给所有登陆⽤户发送警告信息,因次更加安全第2章⽂件和⽬录管理本章主要介绍⼀些常⽤的Linux系统的⽂件和⽬录管理类命令Linux设计思想⼀切皆⽂件:在Linux系统中,不只数据以⽂件的形式存在,其他资源(包括硬件设备)也被组织为⽂件的形式,例如硬盘以及硬盘中的每个分区在Linux中都被视为⼀个⽂件整个系统由众多的⼩程序组成:整个Linux系统是由众多单⼀功能的⼩程序组成的,每个⼩程序只负责实现某⼀项具体功能,⽐如Linux的绝⼤多数命令,其实各⾃有⼀个相应的⼩程序,如果需要完成⼀项复杂任务,只需将相应的命令组合在⼀起即可尽量避免与⽤户交互:在对系统进⾏管理操作时,要尽量避免⽤户的参与,因为Linux系统主要⽤作服务器的操作系统,在服务器上执⾏的操作最好通过编写脚本程序完成,从⽽⾃动化地完成某些功能使⽤纯⽂本⽂件保存配置信息:如果需要改动系统或程序中的某项功能,只需编辑相应的配置⽂件Linux系统中的⽂件和⽬录Linux的⽬录结构在Windows系统中,为每个磁盘分区分配⼀个盘符,在资源管理器中通过盘符就可以访问相应的分区,每个分区使⽤独⽴的⽂件系统,每个分区都有⼀个根⽬录,如C:\、D:\等Linux系统的绝⼤多数发⾏版遵循FHS(Filesystem Hierarchy Standard)⽂件系统层次化标准,采⽤统⼀的⽬录结构,按照FHS标准,整个Linux⽂件系统是⼀个倒置的树形结构,系统中只存在⼀个根⽬录,所有的⽬录和⽂件都在同⼀个根⽬录下在Linux系统中定位⽂件或⽬录时,使⽤/进⾏分隔(区别于Windows的),在整个树形⽬录结构中,使⽤/表⽰根⽬录,根⽬录就是Linux⽂件系统的起点,在根⽬录下按照⽤途不同划分有很多⼦⽬录下⾯是遵循FHS标准的典型⽬录结构:以下是CentOS 7中⼀些常见的⽬录及其作⽤:/boot:存放Linux系统启动所必需的⽂件,Kernel被存放在这个⽬录中/etc:存放Linux系统和各种程序的配置⽂件,Linux中的很多操作和配置都是通过修改配置⽂件实现的(类似于Windows系统中的注册表)/dev:存放Linux系统中的硬盘、光驱和⿏标等硬件设备⽂件/bin:存放Linux系统中常⽤的基本命令,任何⽤户都有权限执⾏/sbin:存放Linux系统基本的管理命令,只有管理员权限才可以执⾏/usr:软件的默认安装位置,类似于Windows系统中的Program Files⽬录/home:普通⽤户家⽬录(也称为主⽬录)/root:超级⽤户root的家⽬录/mnt:⼀般是空的,⽤来临时挂载存储设备/media:⽤于系统⾃动挂载可移动存储设备/tmp:临时⽬录,⽤于存放系统或程序运⾏时产⽣的⼀些临时⽂件,可供所有⽤户执⾏写⼊操作pwd(print working directory)命令:⽤于显⽰⽤户当前所在的⼯作⽬录路径/:是Linux系统的根⽬录,也是其他所有⽬录的起点/root:根⽬录下的⼀个⼦⽬录,⽤途是作为管理员root⽤户的家⽬录,家⽬录主要⽤于存放⽤户的各种数据Linux系统中普通⽤户的家⽬录默认集中存放在/home⽬录中,以⽤户名命名cd(change directory)命令:⽤于切换⼯作⽬录单纯执⾏cd命令,将默认返回到当前⽤户的家⽬录~表⽰当前⽤户的家⽬录cd -可以在最近⼯作过的两个⽬录之间进⾏切换关于路径的⼀些基本概念:绝对路径:以根⽬录/作为起点,可以准确地表⽰⼀个⽂件或⽬录所在的位置相对路径:以当前的⽬录为起点,在开头不使⽤/符号,输⼊的时候更加简单.:表⽰当前⽬录..:表⽰当前⽬录的上⼀级⽬录⽂件和⽬录操作命令ls(list)命令:以列表的⽅式显⽰⼀个⽬录中包含的内容ls显⽰结果以不同的颜⾊来区分⽂件类别,蓝⾊代表⽬录,灰⾊代表普通⽂件,绿⾊代表可执⾏⽂件,红⾊代表压缩⽂件,浅蓝⾊代表链接⽂件touch命令:⽤于创建空⽂件或修改已有⽂件的时间戳mkdir(make directory)命令:⽤于创建新的空⽬录rmdir(remove directory)命令:可以删除指定的⽬录(必须是空⽬录,没有任何⽂件和⼦⽬录)cp(copy)命令:复制⽂件或⽬录cp [选项] 源⽂件或⽬录⽬标⽂件或⽬录mv(move)命令:⽤于移动⽂件或对⽂件重命名mv [选项] 源⽂件或⽬录⽬标⽂件或⽬录rm(remove)命令:删除⽂件或⽬录-f选项:强制删除,⽆须⽤户确认-r选项:递归删除整个⽬录⽂件内容操作命令cat(concatenate)命令:⽤于查看⽂本⽂件内容more命令和less命令:分页显⽰⽂件内容head命令和tail命令:显⽰⽂件开头或末尾的部分内容wc命令:⽤于统计⽂件中的⾏数、单词数和字节数echo命令:⽤于输出指定的字符串或变量的值在变量名称加前导符号$,可以引⽤⼀个变量的值grep命令:⽤于在⽂本⽂件中查找并显⽰包含指定字符串的所有⾏diff命令:⽤于⽐较多个⽂本⽂件之间的差异⽇期和时间的相关命令date命令:显⽰或修改⽇期和时间⽂件查找命令locate命令:简单快速的⽂件查找命令find命令:强⼤的⽂件查找命令,可以实现的⽂件的精确查找xargs命令:find辅助命令内部命令和外部命令Linux系统中的命令总体上分为内部命令和外部命令两⼤类内部命令:指集成在Shell中的命令,属于Shell中的⼀部分,只要Shell被执⾏,内部命令就⾃动载⼊内存,⽤户可以直接使⽤外部命令:很多的Linux命令独⽴于Shell之外,称为外部命令Linux系统中的绝⼤多数命令属于外部命令,⽽每个外部命令都对应了系统中的⼀个可执⾏的⼆进制程序⽂件,这些⼆进制程序⽂件主要存放在下列⽬录中:普通命令:/bin、/usr/bin和/usr/local/bin管理命令:/sbin、/usr/sbin和/usr/local/sbin其中,普通命令是指所有⽤户都可以执⾏的命令,管理命令则只有管理员root才有权限执⾏,Linux系统默认将外部命令程序⽂件的存放路径保存在⼀个名为PATH的环境变量中type命令:判断⼀个命令是内部命令还是外部命令which命令:查找外部命令所对应的的程序⽂件其他辅助命令ln命令:⽤于为⽂件或⽬录建⽴快捷⽅式(Linux系统中称为链接⽂件)alias命令:⽤于设置命令别名系统定义的别名命令:ll(相当于ls -l)unalias命令可以取消所设置的别名命令history命令:查看命令历史记录help命令:查看内部命令帮助信息对于外部命令,可以使⽤通⽤命令选项"--help"man命令:查看命令帮助⼿册clear命令:清除当前终端屏幕的内容ctrl+L也有相同的效果其他技巧通配符和扩展符在Linux系统中执⾏命令时,可以通过⼀些特殊符号对多个⽂件进⾏批量操作,从⽽提⾼操作效率通配符:通⽤的匹配信息的符号*:匹配任意数量的任意字符:在相应的位置上匹配单个字符[]:匹配指定范围内的任意单个字符,如[a,b,c]是表⽰a、b、c任意⼀个字符;[a-z]表⽰任意⼀个⼩写字母扩展符:在扩展符({})中可以包含⼀个以逗号分隔的列表,并将其⾃动展开为多个路径或⽂件名管道符|通过管道符|可以把多个简单的命令连接起来以实现更加复杂的功能,管道符⽤于连接左右两个命令,将|左边命令的执⾏结果作为|右边命令的输⼊命令的换⾏Linux命令⾏需要换⾏可以使⽤\,出现>时可以继续输⼊命令。
内核心得——精选推荐
内核⼼得内核启动过程总结之前配置编译过内核源代码,在交叉编译源代码后产⽣了三个⽂件(还有其他⽂件)分别是vmlinuz、vmlinux、vmlinux32,其中vmlinuz是可引导的、压缩了的内核,将该内核拷贝到系统⽂件/boot⽬录下,再配置下/boot/boot.cfg ⽂件,将启动时选择内核的信息和加载内核的地⽅写⼊就可以实现内核的移植。
其实移植过程和正常内核启动过程的原理是⼀样的。
系统加电启动后,MIPS处理器默认的程序⼊⼝时0xBFC00000,此地址在⽆缓存的KSEG1的地址区域内,对应的物理地址是0x1FC00000,即CPU从0x1FC00000开始取第⼀条指令,内核是系统引导程序把内核加载到内存中的,如果内核是经过压缩的,那么⾸先执⾏/arch/mips/boot/compressed的head.S ⽂件去建⽴堆栈并解压内核映像⽂件,然后去执⾏/arch/mips/kernel 下的head.S,如果是没有压缩的内核则直接去执⾏该head.S。
linux内核启动的第⼀阶段就是从kernel⽂件夹下的head.S开始的,kernel_entry()函数就是内核启动的⼊⼝函数,这个函数是与体系结构相关的汇编语⾔编写的,它⾸先初始化内核堆栈段,来为创建系统的第⼀个进程0进程作准备,接着⽤⼀段循环将内核映像的未初始化数据段bss段清零,最后跳转到/init/main.c中的start_kernel()初始化硬件平台相关的代码。
kernel_entry() - arch/mips/kernel/head.STLB初始化,Cache初始化清除BSS段准备参数 argc/argp/envp设置栈jal start_kernel (init/main.c)怎么为第⼀个进程0进程作准备?第⼀个进程涉及到init_thread_union,这个结构体在include/linux/sched.h得到定义extern union thread_union init_thread_union;union thread_union{struct thread_info thread_info;unsigned long stack[THREAD_SIZE/sizeof(long)];};THREAD_SIZE是⼀个宏定义#define THREAD_SIZE (2*PAGE_SIZE) #define PAGE_SIZE (_AC(1,UL)<内核把进程存放在任务队列的双向循环链表中,链表中的每⼀项都是类型为task_struct、称为进程描述符的结构,进程描述符中包含⼀个具体进程的所有信息,linux通过slab分配器分配task_struct结构,每个任务都有⼀个thread_info结构,它在内核栈的尾部分配,结构中的task域中存放的是指向该任务实际task_struct的指针,thread_info结构在⽂件中定义。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
内核选项的解析完成之后,各个子系统的初始化即进入第二部分—入口函数的调用。通常USB、PCI这样的子系统都会有一个名为subsys_initcall的入口,如果你选择它们作为研究内核的切入点,那么就请首先找到它。
669 int count = preempt_count();
670
671 for (call = __initcall_start; call < __initcall_end; call++) {
672 ktime_t t0, t1, delta;
673 char *msg = NULL;
133 #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
134
135 #define __initcall(fn) device_initcall(fn)
这些入口有个共同的特征,它们都是使用__define_initcall宏定义的。它们的调用也不是随便的,而是按照一定顺序的,这个顺序就取决于__define_initcall宏。__define_initcall宏用来将指定的函数指针放到.initcall.init节里。
128 #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
129 #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
689 delta = ktime_sub(t1, t0);
690
691 printk("initcall 0x%p", *call);
692 print_fn_descriptor_symbol(": %s()",
693 (unsigned long) *call);
.initcall.init节
内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init数据、bass等等。这些对象文件都是由一个称为链接器脚本的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。 vmlinux.lds是存在于arch/<target>/目录中的内核链接器脚本,它负责链接内核的各个节并将它们装入内存中特定偏移量处。在vmlinux.lds文件里查找initcall.init就可以看到下面的内容
另外,这些被执行的函数有可以完成一些需要异步执行的任务,flush_scheduled_work函数则用于确保do_initcalls函数在返回前等待这些异步任务结束。
666 static void __init do_initcalls(void)
667 {
668 initcall_t *call;
122 #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
123 #define arch_initcall(fn) __define_initcall("3",fn,3)
124 #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
这就告诉我们.initcall.init节又分成了7个子节,而xxx_initcall入口函数指针具体放在哪一个子节里边儿是由xxx_initcall的定义中,__define_initcall宏的参数决定的,比如core_initcall将函数指针放在.initcall1.init子节,device_initcall将函数指针放在了.initcall6.init子节等等。各个子节的顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init中的函数指针,等等。不同的入口函数被放在不同的子节中,因此也就决定了它们的调用顺序。
朱德庸在《关于上班这件事》里说,要花前半生找入口,花后半生找出口。可见寻找入口对于咱们这一生,对于看内核代码这件事儿都是无比重要的。
但是很多时候,入口并不仅仅只有subsys_initcall一个,比如PCI。
117 #define pure_initcall(fn) __define_initcall("0",fn,1)
719 }
720 }
721
722 /* Make sure there is no pending stuff from the initcall sequence */);
724 }
680 (unsigned long) *call);
681 printk("/n");
682 t0 = ktime_get();
683 }
684
685 result = (*call)();
686
687 if (initcall_debug) {
688 t1 = ktime_get();
698 print_fn_descriptor_symbol("%s()/n",
699 (unsigned long) *call);
700 }
701
702 if (result && result != -ENODEV && initcall_debug) {
694 printk(" returned %d./n", result);
695
696 printk("initcall 0x%p ran for %Ld msecs: ",
697 *call, (unsigned long long)64 >> 20);
125 #define subsys_initcall(fn) __define_initcall("4",fn,4)
126 #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
127 #define fs_initcall(fn) __define_initcall("5",fn,5)
do_initcalls()函数
那些入口函数的调用由do_initcalls函数来完成。
do_initcall函数通过for循环,由__initcall_start开始,直到__initcall_end结束,依次调用识别到的初始化函数。而位于__initcall_start和__initcall_end之间的区域组成了.initcall.init节,其中保存了由xxx_initcall形式的宏标记的函数地址,do_initcall函数可以很轻松的取得函数地址并执行其指向的函数。
708 preempt_count() = count;
709 }
710 if (irqs_disabled()) {
711 msg = "disabled interrupts";
712 local_irq_enable();
713 }
714 if (msg) {
130 #define device_initcall(fn) __define_initcall("6",fn,6)
131 #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
132 #define late_initcall(fn) __define_initcall("7",fn,7)
118
119 #define core_initcall(fn) __define_initcall("1",fn,1)
120 #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
121 #define postcore_initcall(fn) __define_initcall("2",fn,2)
.initcall.init节所保存的函数地址有一定的优先级,越前面的函数优先级越高,也会比位于后面的函数先被调用。
由do_initcalls函数调用的函数不应该改变其优先级状态和禁止中断。因此,每个函数执行后,do_initcalls会检查该函数是否做了任何变化,如果有必要,它会校正优先级和中断状态。