突然发觉一直在和Linux死磕,虽然是断断续续的,不过可以称得上是坚持不懈了,甚至有点相爱相杀的感觉。在形影相吊的日子里,作为大龄假程序猿,能够静得下心来玩味Linux内核,失落、慰藉、迷茫、坚持······,个中滋味,一言难尽。也许那本关于Windows驱动的书取名《寒江独钓》,作者也有此意吧:如果告诫读者,如入此门,须当问心无悔!
于2022世界杯决战正酣之际,作为连伪球迷都算不上的——假球迷,居然在决赛前由衷地为梅西祈祷夺冠,唯持之以恒、百折不挠、舍身取义的精神所触动而已,无它,当然也包括传说中的精湛球技——显然假球迷不懂,也不会懂,也不需要懂。平心而论,这样的边际祝福从上帝视角来看,对于姆巴佩是不公平的,但太渴望梅西圆梦了。当时只觉得世界欠他一座大力神杯,同时更不希望历史记下这笔欠账^_^。——一觉醒来——假球迷是不会熬夜去看球赛的,何况大龄假球迷——,梅西夺冠、加冕、封神,《早安隆回》鸾凤和鸣,如同Linux内核的进程管理、内存管理、网络通信等一样,近在咫尺,又远在天边。总之就是一句话:没事找事,关你屁事!
诚然,此言属实。
就像别人说的:“梅西眼里有光”。确实,能够感受到的,也只有光了。同样,Linux忠实地为我们服务如此之久,众人仅视其为工具,而且是免费的工具或平台,唯有那一小撮脑子冒烟的人,才认为其是一种哲学,是经典,这些人分两种:天才和疯子。
可能我就属于疯子吧。疯就疯吧。
疯子总有些豪言壮语,或者偏执固见。比如觉得那些开发应用软件的都是玩积木,搞搞内核、驱动等才是真正的程序员或工程师。疯子总有高人一筹的地方,令人叹服,但大多是难以入世,与周围格格不入。题外话,今天看到怀疑学习C语言意义和价值的论调,我想这大抵就是专业和业余的区别吧。上学时也藐视过汇编,心想这玩意晦涩难懂,开发效率低下,有着C、C++、java等不学,开这课程干吗。后来——经事后,想知其然更知其所以然,居然恶补了一些汇编的原理,打脸不?现在为了效率,尽量用JavaScript+H5,为了发疯,用C和汇编,什么C++、Java,甚至Go什么的都略过了。
有收获吗?当然有,很大——谈个项目,把乙方的售前问得一愣一愣的。这甲方难缠,下次带个技术来。什么?技术也被弄懵了?算了,跳过吧。这事还得干啊,好吧。也别追问了,弄得双方都下不了台,领导都不答应呀。对!——躺平、忽略、放弃精益求精的技术做派,嗯,这不太阳照常升起吗?
面对Linux复杂的运转过程,一直很茫然、现在也是。不知道从哪里下手,如果狼吃刺猬。看了一堆关于Linux内核的书,也没什么感觉。直到看到一篇博文里提到:从内核某一个功能维度垂直研究,效果会好一些。深以为然。2023年春节前后,工作时间上有点空闲,突发奇想,以前曾尝试学习过Linux进程的堆栈溢出,遇到不少坑,开阔了一点眼界,但还是不甚了了。再出发!做一次尝试吧,细究一下Linux的内存管理之道。有点“虽千万人,吾往矣”的气概O(∩_∩)O哈哈~
于是又翻开了书,结果所获无几。
角度不对,本想探究一下内存管理的内内外外,结果,网上的文章千篇一律,几本书籍如同字典,讲了一个功能模块,源码分析得头头是道,可它到底起到了什么作用呢?这个功能谁来调用?什么时候调用?调用的目的和场景是什么?没一个能说清楚的。
要说裨益也不是完全没有,有时老天着急了也会给点提示的。突然一天灵光咋现,意识到脱离了进程研究内存管理是死路一条!于是转而从Linux进程的角度入手,先了解一下Linux内核进程和内核开发,这涉及到了驱动,ko文件,按下不表。
查看进程信息是吗?好了,top、ps -ef、cat /proc/(...),呵呵一搜又是一堆Linux命令。突然感觉自己貌似很牛逼的样子,这些东西都太肤浅了,我们要到内核翻一个更有深度和内涵的:
Linux进程信息保存在task_struck结构里,系统为每个进程——更准确地说是线程——准备一个task_struct结构,记录进程/线程的相关信息。可见,系统对待进程和线程是平等的。那么系统怎么把进程和线程关联起来呢?
task_struct结构里有两个成员变量:
pid_t pid;//进程的唯一标识
pid_t tgid;// 线程组的领头线程的pid成员的值
在Linux系统中,一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的PID,并被存放在tgid成员中。只有线程组的领头线程的pid成员才会被设置为与tgid相同的值。注意,getpid()系统调用返回的是当前进程的tgid值而不是pid值。(线程是程序运行的最小单位,进程是程序运行的基本单位。)
————————————————
版权声明:本文为优快云博主「lc_29503203」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qq_29503203/article/details/54618275
以上是从其他大神的帖子上搬过来的,还没验证过。注意:pid是唯一的,全局唯一,只有领头线程的pid才会与tgid相同,领头线程即为主线程。
好了,找到某个进程的task_struct对象,然后把信息读取并显示出来,岂不就一锤定音了?确实是,可怎么找呢?
——没有老师,没有同学,甚至还想投资给自己增点值,问了希赛的销售小姐姐。小姐姐耐心地跟我说:这个真没有!我们有阿里云、CISP、华为认证、软考初中高级,甚至还有会计、一建......,看明白了,咱家独领风骚不可能,另类倒是真的。瞧瞧人家能给予的知识多实惠。好吧,众里寻他千百度,还是度娘颜永驻。
要找task_struct呀,你得去问内核。身处ring3,用户态,够不着内核啊。别急,给你个通道:Linux模块或驱动。
这是个古老的话题,Linux模块或驱动开发的帖子一搜一大片。不知道从事此项工作的开发者还有多少,前景怎样:-),总之,至少是迈进了一小步。Linux内核实现了模块化,可以动态加载/列出/卸载模块,命令如下:
sudo insmod ***.ko
lsmod
sudo rmmod ***.ko
模块ko文件,可以看作是Windows操作系统里的dll、sys文件,不过这个文件是工作在内核里的,ring0层。显而易见,这只是万里长征的第一步:找到了和内核打交道的通道,接下来就得扪心自问,我们到底想干什么、要干什么、能干什么?回到源头,该从哪里入手呢?(虚拟)内存是用于支持进程运行的,提供存放代码、数据、堆栈等空间的,那么从简单处入手,查看下进程信息吧。
一步一步来,ko文件如何生成呢?它有基本格式,在里面写一个读取task_struct结构的函数即可实现愿望:
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev_init helloworld init\n");
//printk("<7>" "chrdev_init helloworld init\n");
//printk("<7> chrdev_init helloworld init\n");
return 0;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit helloworld exit\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
特别声明:代码段以上是从网上 (linux驱动开发(一) - biaohc - 博客园)搬来的,非原创。值得注意的是:MODULE_LICENSE("GPL");是必须的,否则会有错误提示。可能源于Linux系统或者Linus大力提倡开源精神吧。不过我很赞同。另外注意,在内核态,输出要用printk函数,它会输出日志,查看日志用dmsg工具。有了这些方法,终于可以和内核直接对话了,再也不用看其它应用程序或者不同shell的脸色了!
敲了敲门:要看看进程信