先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前在阿里
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
设备号的构成
一个设备号由主设备号和次设备号构成。
主设备号对应设备驱动程序,同一类设备一般使用相同的主设备号。
次设备号由驱动程序使用,驱动程序用来描述使用该驱动的设备的序号,序号一般从 0 开始。
Linux 设备号用 dev_t 类型的变量进行标识,这是一个 32位 无符号整数,内核源码定义为:
/* <include/linux/types.h> */
typedef u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
主设备号用 dev_t 的高 12 位表示,次设备号用 dev_t 低 20 位表示。
内核提供了几个宏定义,供驱动程序操作设备号时使用:
/* <include/linux/kdev_t.h> */
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
宏 MAJOR 从设备号 dev 中提取主设备号。宏 MINOR 用来从设备号 dev 中提取次设备号。宏 MKDEV 用来将主设备号 ma 和 次设备号 mi 组合成 dev_t 类型的设备号。
另外,内核也提供了从设备文件 i-节点结构(inode 结构体)中获取主次设备号的函数,如下:
/* <include/linux/fs.h> */
/* 获取次设备号 */
static inline unsigned iminor(const struct inode *inode)
{
return MINOR(inode->i_rdev);
}
/* 获取主设备号 */
static inline unsigned imajor(const struct inode *inode)
{
return MAJOR(inode->i_rdev);
}
通过函数源码可知,获取主设备号和次设备号最终是通过宏定义完成的。
内核管理设备号
以字符设备为例,向内核中注册设备号,内核是如何分配和管理设备号的呢?
在编写字符设备驱动时,可以通过如下两个系统调用向内核注册设备号:
- register_chrdev_region()
注册一系列连续的字符设备号,主设备号需要函数调用者指定。此函数的原型为:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
参数 from 为设备编号,包含主设备号和次设备号。参数 count 用于指定连续设备号的个数,即当前驱动程序所管理的同类设备的个数。参数 name 为设备或驱动的名字。
执行成功,返回 0。失败,则返回一个负值的错误码。
- alloc_chrdev_region()
注册一系列连续的字符设备号,主设备号是由内核动态分配得到的。此函数的原型为:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
参数 dev 为函数的传出参数,用于记录动态分配的设备号,如果申请多个设备号,则此参数记录这些连续设备号的起始值。
参数 baseminor 指定首个次设备号。参数 count 用于指定连续设备号的个数。参数 name 为设备或驱动的名字。
执行成功,返回 0。失败,则返回一个负值的错误码。
接下来,看看这两个函数的内部实现流程。
register_chrdev_region()
该函数的内核源码为,关键部分已加注释:
/* <fs.char_dev.c> */
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
dev_t to = from + count;
dev_t n, next;
/* 循环,注册多个连续的设备号 */
for (n = from; n < to; n = next)
{
/* 计算得到下一个设备号 */
next = MKDEV(MAJOR(n)+1, 0);
/* 判断是否超限 */
if (next > to)
next = to;
/* 向内核注册指定的设备号 */
cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
/* 如果失败,则释放已申请的设备号资源 */
to = n;
for (n = from; n < to; n = next)
{
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
return PTR_ERR(cd);
}
由代码内容可知,这个函数的核心处理流程是通过内部调用 __register_chrdev_region()
实现的。
这个函数的主要功能是,将要使用的设备号注册到内核的设备号管理体系中,避免多个驱动程序使用相同的设备号,而引起的混乱。
如果注册设备号已经被使用,则会返回错误码告知调用者,即调用失败。如果成功,则函数返回 0。
struct char_device_struct
在调用过程中,会涉及到一个关键的数据结构 struct char_device_struct
,其定义如下:
#define CHRDEV_MAJOR_HASH_SIZE 255
static struct char_device_struct {
struct char_device_struct *next; /* 链表指针 */
unsigned int major; /* 主设备号 */
unsigned int baseminor; /* 次设备号 */
int minorct; /* 此设备号个数 */
char name[64]; /* 设备名称 */
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
定义结构体的同时,还定义了一个全局性的指针数组 chrdevs,是内核用来分配和管理设备号的。数组中的每一个元素都是指向 struct char_device_struct
类型的指针。
函数 register_chrdev_region()
的主要功能是将驱动程序要使用的设备号记录到 chrdevs 数组中。
__register_chrdev_region()
核心处理函数 __register_chrdev_region() 内部,首先会分配一个 struct char_device_struct
类型的指针 cd,然后对其进行初始化(已经去除无关代码):
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
...
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
/* 根据主设备号计算索引,搜索 chrdevs 数组,判断主设备号是否可用 */
i = major_to_index(major);
for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next)
{
if (curr->major < major)
continue;
if (curr->major > major)
break;
if (curr->baseminor + curr->minorct <= baseminor)
continue;
if (curr->baseminor >= baseminor + minorct)
break;
goto out;
}
/* 初始化信息 */
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
/* 将分配的 cd 加入到 chrdevs[i] 中 */
if (!prev) {
cd->next = curr;
chrdevs[i] = cd;
} else {
cd->next = prev->next;
prev->next = cd;
}
...
}
函数申请完内存资源后,开始扫描 chrdevs 数组,确保当前注册的设备号可用。如果设备号占用,函数返回错误码,即调用失败。
如果设备号可用,则用设备号和名字信息初始化。初始化完成后,将 struct char_device_struct
加入到内核管理设备号的链表中。
alloc_chrdev_region()
此函数由内核动态分配设备号,该函数的内核源码如下,关键部分已加注释:
/* <fs.char_dev.c> */
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
/* 向内核注册设备号 */
cd = __register_chrdev_region(0, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
/* 得到动态获取的首个设备号 */
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}
![](https://img-blog.csdnimg.cn/img_convert/9a8cb5f8c0ec69e6499adead0da6e95b.png)
最全的Linux教程,Linux从入门到精通
======================
1. **linux从入门到精通(第2版)**
2. **Linux系统移植**
3. **Linux驱动开发入门与实战**
4. **LINUX 系统移植 第2版**
5. **Linux开源网络全栈详解 从DPDK到OpenFlow**
![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/59742364bb1338737fe2d315a9e2ec54.png)
第一份《Linux从入门到精通》466页
====================
内容简介
====
本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。
![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/9d4aefb6a92edea27b825e59aa1f2c54.png)
**本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。**
> 需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
从事Linux平台开发的各类人员。**
> 需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**