Linux内核开发一:常识

1 、Linux 系统启动引导过程

 

第一阶段:BIOD 启动引导阶段----设备上电后,启动第一个程序即 BIOS 程序,此程序一般都被厂商烧录在 BIOS 

 

片中,检测各个硬件设备,包括 CPU、内存、显示卡、硬盘等。在开机时通过 F2F12 或者 DEL 键就可以进入 BOIS

 

BIOS 完成自检后就会按照设置好的启动顺序,决定是沿着本地硬盘驱动器继续运行设备,还是 USB 或者 CD_ ROM

 

引导设备。

 

第二阶段:MBR 启动引导阶段----计算机了安装操作系统之后,默认应该从本地硬盘启动,BIOS 程序会读取每一

 

块硬盘的 0 盘片 0 磁道第一个扇区的内容并执行其中的机器代码,此时 BIOS 的任务就完成了,将系统启动的控制权移

 

交到 MBR 扇区。此扇区不属于任何一个操作系统,共有 512KB,其中前 446bytes 是个引导加载程序,后 64bytes 是

 

该硬盘的分区表。MBR 引导过程的主要任务就从 MBR 扇区中启动引导加载程序。

 

第三阶段:GRUB 启动引导阶段----现在 Linux 普遍使用的引导加载程序就是 GRUB,它允许用户可以在计算机内

 

同时拥有多个操作系统。GRUB 程序被装载到内存中后便显示出 GRUB 的图形界面,在该界面中用户可以通过上下方

 

向键选择需要加载的操作系统的内核,由于 GRUB 的主要任务就是在加载内核文件,因此也称之为内核加载程序。

 

GRUB 程序的配置文件是/boot/grub/grub.conf,有些 Linux 系统比如 SuSe  menu.lst。此文件记录了这台设备里

 

已安装的所有操作系统的信息,如果此文件损坏,系统启动后将直接进入 grub>界面,这是就需要用户手动定位 boot

 

分区、将内核文件加载到根分区、加载磁盘初始化文件。

 

第四阶段:内核引导阶段----当用户选择其中一个内核启动 Linux 操作系统后,GRUB 程序会根据/boot/grub/

 

grub.conf 文件中所设置的信息,从 boot 分区上读取该内核文件并加载到内存中,这是引导控制权就交给了 Linux 内核。

 

Linux 内核获得控制权后,最重要的工作就是在其内存空间启动 init 进程,此进程是 Linux 操作系统所有进程的发起者。

 

第五阶段:init 进程初始化操作系统阶段----/sbin/init 进程起来后,就获得了系统启动的控制权,首先会读取配置文

 

/etc/inittab,此文件决定了 Linux 系统启动后的运行级别, init 进程初始化操作系统的工作具体是执行了 3 个脚本:

 

1)、init 进程执行系统初始化脚本/etc/rc.d/rc.sysinit,对系统进行基本的配置,比如设置 shell 变量,设置网络/

 

时钟,加载硬件驱动模块,激活 LVM 设备,读取/etc/fstab 文件挂载文件系统等一系列工作,到此系统基本算运行起来

 

了。

 

2)、 由 init 进程执行/etc/rc.d/rc 脚本,此脚本根据运行级别参数,终止(K)或启动(S)相应服务,7 个级别运行的

 

服务脚本各自放在 7 个名为/etc/rc.d/rcN.d 的目录下,系统的常用的运行级别是 3 和 5,如果是 0 则系统无法启动,如

 

果是 6 则启动后就自行重启。

 

3)、rc 脚本调用会调用/etc/rc.d/rc.local 脚本,系统允许用户写自己想做的事情而不必写在以上两个系统脚本里。

 

第六阶段:用户登陆阶段

 

1)、启动虚拟终端登陆进程/sbin/mingetty,此时如果是第三级别就可呈现给用户最终的登陆界面了;如果是第

 

五级别,则运行 Linux 图形程序如 X-window 呈现给用户最终的登陆界面。

 

2)、当用户输入用户名和用户口令后,系统首先会根据用户账户文件/etc/passwd 和密码口令文件/etc/shadow

 

用户组文件/etc/group 和组密码文件/etc/gshsdow 验证此用户的合法性及密码口令是否正确,验证通过后就会启动

 

/etc/passwd 文件为该用户所配置的 Shell。此 shell 进程首先会执行全局配置文件/etc/profile/,如果是普通用户登陆还

 

会执行此用户家目录下的/home/用户名/.bash_profile 文件,这两个文件都是配置用户的 Shell 环境的,如 HOME

 

LOGINNAMEPS1  LANG 变量等。这两个文件执行完后,Shell 进程最终开启一个终端会话窗口或图形桌面。

 

系统的关闭和重启

 

根据 Linux 系统的 7 个不同的运行级别,执行 init 0 就是关闭系统,init 6 就是重启系统

 

shutdown 命令可以安全的关闭或重启系统,其实就是通过向 init 进程发送 SIGTERM 信号,要求他将运行级别改

 

0 或者 6 来是实现关闭或重启系统的。如 shutdown –h 0 是立即关闭系统,shutdown –r 5 是 10 秒钟后重启系统。

 

reboot 是重启系统的快捷命令,其实就是调用 shutdown –r 0 实现立即重启的。


 

2 、gcc/g++编译器

 

Linux 平台下编译 C/C++语言程序使用的主要编译器就是 gcc  g++。在使用 gcc 编译程序时默认是一步到位生

 

成二进制指令文件时,但是可以将编译过程细分为 4 个阶段:

 

第一步:预处理---也称之为预编译,这个过程是处理源程序中的 include 头文件包含、#define 宏定义和#undef

 

删除宏、条件编译共三项工作,其实都是简单粗暴的问价替换。单独执行预处理是------gcc –E ***.c,默认是把处理结果输出到屏幕上,也可以指定生成一个对应的.i 文件。

第二步:编译----也称之为狭义的编译,这一步是对预处理后的源代码作语法检查并翻译成汇编语言源代码,单

 

独执行编译是-----gcc  –S ***.c,默认生成对应的.s 汇编文件。

 

第三步:汇编----就是把.s 汇编语言源代码转换成目标代码,但目标代码仍然不能在 CPU 上运行,单独执行这一

 

步是-----gcc –c **.c,默认生成相应的.o 目标文件。目标代码文件可分为两种:不包含 main 函数但包含普通函数实现的和包含 main 函数并直接调用普通函数的。

第四步:链接----链接过程是由 ld 链接程序完成的,将一个或多个.o 目标文件与库文件进行链接最终生成可执行的

 

二进制文件。分为两种,一种是静态链接,另外一种是动态链接。

 

gcc 的基本用法及编译选项:对于 C 语言单文件源程序,直接 gcc **.C,就可以生成默认二进制文件 a.out

 

-o 用于指定输出的文件名,在以上四个过程中都可使用。

 

-Wall 生成尽可能多的告警信息,建议在编译任何程序时都带上此选项。

 

-D_macro(=**) 在命令行定义宏,相当于在程序中执行#define macro 宏名称。

 

-g 在可执行文件中参数符号调式工具如 GNU 的调试工具 gdb

 

-I 指定程序使用头文件的路径,稍后讲解

 

-l 指定编译过程最后所有链接的函数库文件的文件名。

 

-v 显示 gcc 编译时的详细编译过程,包括相关程序的版本号。

 

-Olevle gcc 的编译优化选项,共有 01234 五个级别,优化级别越高,生成可执行文件的运行速度

 

就越快,但消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候

 

才考虑对最终生成的代码进行优化。

 

3 、函数库

 

商业开发项目中,源代码文件数量非常庞大,为了方便管理使用,基本上都是通过库文件加头文件组合的方式对

 

外提供服务。库是一组预先编译好的函数代码(目标文件)的集合,Linux 把 ANSI C 的标准库函数都封装成了库文件

 

/user/lib64/libc.so 和/usr/lib64/libm.so,这些标准系统库文件一般存储在/lib//usr/lib//lib64 和/usr/lib64 目录下。对

 

于这些系统标准库文件,C 语言编译器(具体是 ld 链接程序)知道去哪个路径下搜索,如何链接,如何执行,不需要程

 

序员控制;但是对于用户自己写的库文件,就需要显示得添加 LIBRARY_PATH 环境变量,编译链接时要使用-l 选项告

 

诉编译器要链接对应的库文件,链接动态库的可执行程序还需要添加环境变量 LD_LIBRARY_PATH 才能够顺利执行。

 

Linux 中函数库通常有静态库和动态共享库两种,库文件名总是以 lib 开头,随后是库名,文件名的最后部分是给

 

出库文件的类型,有些库文件最后带有数字表示该库的发行版:.a 代表静态函数库;.so 代表共享函数库如/lib64/libc.so.6

 

3.1 静态库

 

(1)、静态库又称为归档文件(archive),是把多个包含普通函数实现但不包含主函数的.o 的目标文件打包归档成一个库

 

文件。在链接静态库时,是把静态库中的代码直接复制到最终的可执行文件中。这样会导致可执行文件比较大,消耗硬盘的

 

存储空间;不利于代码的修改、扩展和复用。优点是可执行文件一旦编出便不需要库文件,而且运行速度比较快。

 

创建多个包含普通函数实现但不包含主函数的.o 的目标文件,gcc –c ***.c ***.c

 

创建静态库是 ar –r lib 库名.a ***.o ***.o 可在当前目录下生成库文件。

 

链接静态库时,先提供包含主函数与普通函数调用的 main.o 文件,然后配置环境变量 LIBRARY_PATH 包含库文件的路

 

径,最后 gcc main.o –l 库名便可生成最终可执行文件;

 

3.2 共享库

 

共享库又称为动态链接库,创建时是把包含普通函数实现但不包含主函数的.o 的目标文件中的函数的地址编译到库

 

文件中。它的链接方式是这样的:可执行文件本身并不包含函数代码,而是包含了库文件的地址。这样可执行文件比较

 

小,节省存储空间;而且共享库的更新可以独立于依赖它的应用程序。缺点是应用程序执行时,仍然依赖共享库。

 

创建多个包含普通函数实现且不包含主函数的.o 的目标文件,gcc –c -fpic ***.c ***.c

 

创建共享库是 gcc -shared ***.o ***.o -olib 库名.so 可在当前目录下生成库文件。

 

链接静态库时,先提供包含主函数与普通函数调用的 main.o 文件,然后配置环境变量 LIBRARY_PATH 包含库文

 

件的路径,最后 gcc main.o –l 库名便可生成最终可执行文件;

 

执行可执行文件前需要配置环境变量 LD_LIBRARY_PATH,才能顺利运行,并且共享库文件不能删除,共享库是

 

绿色的有执行权。

 

ldconfig 是一个动态链接库管理命令,可以让新添加的动态链接库为系统所共享。ldd 命令可列出可执行文件依赖

 

了哪些共享库及其地址。如果链接了用户创建的共享库,却没有配置 LD_LIBRARY_PATH 环境变量,就会显示出此共

 

享库的地址是 not found

 

4、C 语言程序的错误处理

 

程序员在编写软件时要尽量面面俱到,除了要处理正确的分支,还要考虑出现意外的情况,例如读写文件前文

 

件打不开,或者从底层数据库存取数据时数据库连不上。

 

后期的语言比如 C++Java,都是使用语言自身提供的异常处理机制,如 C++的标准异常的类 exceptionC 语言

 

诞生较早,并没有设计异常机制,而是使用函数返回值和全局变量 errno 处理错误,对错误的情况另外写分支处理。

 

1)、在 Linux 使用 C 语言编程,不管是标准 C 库函数还是 Linux 系统调用接口函数或者是程序员自己开发的函数,

 

利用返回值判断程序正误一般都有四种情况:

 

a、如果返回值是 int,并且返回的数据不可能是负数,用返回-1 代表错误。

 

b、如果返回值是 int,并且返回的数据可能是负数,就用指针参数保存数据,然后返回 0 代表正确,返回-1 代表出错。

 

c、如果返回值是指针类型,就返回 NULL 指针代表出错。

 

d、如果函数不需要考虑错误处理,返回值定义为 void 即可。

 

2)、许多系统调用和 C 库函数都会因为各种各样的原因而失败,它们会在失败时设置外部全局变量 errno 来指定

 

失败的原因。

 

Linux 把错误分为错误码和错误信息。errno 变量就是记录系统的最后一次错误码,是一个 int 型的值,每个错误码

 

都有一个宏名称,如错误 1 就是 EPERM,对应错误信息是 Operatin not permitted;错误 21 就是 EISDIR,对应错误信

 

息是 Is a directory

 

值得强调的是只有当一个系统标准函数调用失败时,errno 才会被重新设置,所以我们不能通过测试 errno 的值来

 

判断是否出现错误,而是先确认本次调用出现错误(如通过返回值)以后再马上使用 errno 查看具体的错误原因。Linux

 

操作系统提供了个专门的 strerror 函数用来报告出现的错误。strerror 函数以错误代码作为参数,映射成字符串并返回,

 

该字符串就是错误码对应的错误信息,在记录错误条件时十分有用。

 

5、Linux 时钟系统及时间编程

 

5.1 Linux 时钟系统

 

Linux 中两套时钟系统时钟:系统时钟和硬件时钟。系统时钟是指当前 Linux Kernel 中的时钟,而硬件时钟则是主

 

板上 CMOS 芯片中由电池供电的时钟。当 Linux 启动时,系统时钟会去读取硬件时钟的设置,然后两者就独立运作了。

 

UTC 时间 GMT 时间指的都是英国伦敦 0 时区的标准时间,CST 时间可以表示美国,澳大利亚,中国,古巴四个

 

国家的本地标准时间。代表中国的就是北京时区,位于东八区,领先 UTC 8 个小时。

 

hwclock  clock 命令可显示本地 CST 硬件时间….

 

date 命令是显示本地 CST 系统时间,date –u 是显示此时的 UTC 系统时间….

 

date –s 用以修改本地 CST 系统时间,hwclock –w 可以把新的系统时间写进硬件时间里…..


 

5.2 Linux 时间编程

 

Linux 中编程和脚本一般都是采用的系统时钟设置。

 

时间数据类型 time_t:在 time.h 中定义:typedef long time_t; 实际是一个长整型,64 位长度。 struct tm 时间类型 tm 结构在 time.h 中定义,ANSI C 标准中使用 tm 结构用来 (broken- down time)。:

 

struct  tm{

 

 

int

tm_sec; /* - 取值区间为[0, 59]*/

 

int

tm_min; /* - 取值区间为[0, 59]*/

 

int

tm_hour; /* - 取值区间为[0, 23]*/

 

int

tm_mday; /* - 取值区间为[1, 31]*/

 

int

tm_mon; /*月份 - 取值区间为[0, 11]*/

 

int

tm_year; /*年份 - 其值为 1900 年至今年数*/

 

int

tm_wday; /*星期 - 取值区间[0, 6]0 代表星期天,1 代表星期 1,以此类推*/

 

int

tm_yday; /*从每年的 1  1 日开始的天数-取值区间为[0, 365]0 代表 1  1 */

 

int

tm_isdst; /*夏令时标识符*/

};

 

 

 

time()函数:

 

参数一般给 NULL,返回从 Uni 纪元到现在的总秒数,time_t 类型。

ctime( )函数:

time 函数的返回值的指针以实际使用的时间形式返回,时间字符串格式为:"Wed Jun 20

21:00:00 2012\n\0",固定是 26 个字符的长度。

localtime( )函数:

将参数 timep 指向的 time_t 时间信息转换成用 tm 结构体表示的本地时区时间这是需要逐个分

拆结构体成员。

 

 

例:

 

 

 

void main (void)

 

 

{

 

 

 

char

*wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

time_t  timep ;

 

struct

tm *p_tm ;

 

timep

=  time(NULL) ;

 

printf ( “%s\n” , ctime(&timep) ) ;

 

p_tm = localtime(&timep); /*获取本地时区时间*/

 

printf ( "%d-%d-%d %s %d:%d:%d\n " , (p_tm->tm_year+1900) , (p_tm->mon+1) , p_tm->tm_mday , wday[p_tm->tm_wday], p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec ) ;

 

}

 

6、Linux 日志系统

 

6.1 syslog 日志系统

 

syslog  Linux 中用以记录系统日志的服务,有些 Linux 中被替代成了 rsyslog,服务脚本是/etc/init.d/rsyslog

 

配置文件/etc/rsyslog.conf 里规定了对于哪个来源的什么级别的的日志消息是什么处理动作。


 

1、消息来源主要有 authpriv 安全/授权信息,ftp 文件传输,user 用户信息等

 

2、日志级别有 8 :debuginfonoticewarningerrcritalertemerg 3、处理动作主要是发往哪个文件中,如/var/log/message/var/log/dmesg /dev/console

Linux 为所有程序产生日志信息的接口是 syslog 函数,向指定的系统日志设施发送一条日志消息,函数原型是 void syslog ( int priority , const char *message , arguments……) ;

1、参数 priority 是日志级别与设施值的按位或,严重级别同上,设施值是日志消息的来源,并非都写入/var/log /messa ges 中,具体要看/etc/syslog.conf 文件的设置。

2、参数 message 是日志消息的消息体,类似 printf 函数可以格式化字符串,控制符%m 可以插入错误变量 errno

 

当前值对应的错误信息。

 

如在判断一个文件打不开后,就可以 syslog ( LOG_ERR|LOG_USER , “ pid = %d , oops = %m ” , getpid() ) ; 向指定的文件打入一条日志信息。该日志的几个字段分别是时间主机名程序名进程 ID 具体原因。

 

6.2 用户日志

 

/var/log/wtmp 用于保存用户成功登陆的记录,使用 last 命令查看

 

/var/log/btmp 用于保存用户登陆失败的记录,使用 lastb 命令查看

 

who  w –fh 命令用于正在登陆用户的详细登陆信息

 

每个用户主目录下的.bash_history 文件记录用户输入所有命令。

 

6.3 应用日志

 

除了由 syslog 服务管理的系统日志外,Linux 系统中的应用软件(Apache)也可以有自己的日志系统,而且日志

 

文件和每条日志的各字段的意义都由程序员自己定义。

 

商业开发中,应该把那些重要的人为操作通过代码内部写文件或脚本里重定向的方式写进日志文件。通过查询日志

 

可以定位软件的故障原因,并追溯到源代码中的某个文件,某个函数,甚至某个变量,要养成对日志关键字段的敏感

 

性。

 

7 、Linux 定时任务

 

7.1 at 定时任务

 

通过 at 命令可以在指定的时间执行指定的命令,特点是任务只会运行一次,运行完毕就不存在了,如 at 13:20

 

2015-01-06 回车确定后输入具体的任务就能设计一个定时任务了,atq 是查询系统当前已有的 at 定时任务,atrm 后跟

 

at 定时任务 ID 就能删除这条定时任务。

 

7.2 crond 定时任务

 

crond 可以根据分钟、小时、日期、月份、星期的组合来周期性地执行指定的任务,Linux 操作系统有默认的 crond

 

定时任务。程序员也可以自定义自己的定时任务。

 

crontab -e 用于启动 vim 创建和修改 cron 计划任务,每条 cron 定时任务的格式固定为分钟小时日期月份星期

 

任务。每个月的周一到周三的上午 11 点 30 分 30 11 * * 1-3

 

crontab -l 可以查看每一条 cron 计划任务,带#号就是被注释了不再运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值