窥探Linux内核进程信息task_struct

突然发觉一直在和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值。(线程是程序运行的最小单位,进程是程序运行的基本单位。)
————————————————
版权声明:本文为CSDN博主「lc_29503203」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/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         // module_init  module_exit
#include             // __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的脸色了!

敲了敲门:要看看进程信息,也就是你家的task_struct结构长啥样,更准确地说是贵处在为各位进程客户建的档案里面写了什么,当然也包括鄙人的档案。内核:久违了!(因为上次想绕过ip、tcp、udp等数据栈,直接手动发送个以太网数据包而第一次上门,所以这次是第二次打交道了)。

既然能来,便是已经享有root权限的贵宾了,请自便吧。task_struct呢,总是待在一起的,您查看便是。友情提醒:所有task_struct都手拉手连成了一个链,叫双向循环链表,上古时期创建的第一个进程:init,也在其中,并且它左手拉着同时期创建的、第二个进程的task_struct结构的手,右手还拉着新建的、最后一个进程的task_struct结构的手。

task_struct结果的成员众多,涉及双向列表的是类型为list_head结构的tasks对象。

tasks结构包含两个指针:

list_head* prev;  //指向前一task_struct结构对象的tasks对象

list_head* next;  //指向后一task_struct结构对象的tasks对象

随便找到哪个进程,均可通过该对象来遍历找到系统中的其它进程,包括init进程,它的“tasks->prev”指向最后一个进程(线程)的tasks对象。不过不推荐这样做,遍历的效率比较低,task_struct支持更高效的、基于红黑树的遍历方式,这不属于今天的讨论范围(还不知道啥是红黑树呢[汗颜])。

本来是溜溜达达地随便找个进程窥探一下,这下好了遇上了一片森林!也罢,就当开阔眼界了。森林的入口在哪呢?嗯,不知道从哪找,也就是说不知道森林的位置,网上找到个入口:同为task_struct结构的init_task,应该是个全局变量,可惜没搜到有关它的传说。这里很希望哪位大佬指点一下,不胜感激。想必也能猜到它是干什么的——内核在开天辟地后为init进程建立的“创世task_struct对象”,或者叫夏娃?顺藤摸瓜,从“夏娃”开始循环遍历,直到“夏娃”再次出现,即可预览所有进程/线程。为了证明“到此一游”,让内核函数printk帮助写一下游记,记录一下遍历过程也未尝不可O(∩_∩)O~

//https://blog.csdn.net/Coder__CS/article/details/53041594

#define __NO_VERSION__

#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif

#include 
#include 
#include 

MODULE_LICENSE("GPL");

int init_querytask(void)
{
	struct task_struct *p=&init_task;
	struct list_head *final=p->tasks.prev;
	struct list_head *phnext=p->tasks.next;
	
	printk("let's begin!--\n");
	while(p->tasks.next!=final)
	{
		printk(KERN_INFO "The Process is \"%s\" (pid %i)\n",p->comm,p->pid);
		printk(KERN_INFO "The task state is %ld\n",p->state);
//		p=p->tasks.next;
		p=list_entry(p->tasks.next,struct task_struct,tasks);
	}
	
	return 0;
}

void cleanup_querytask(void)
{
	printk("Quit!--\n");
}

module_init(init_querytask);
module_exit(cleanup_querytask);

解释一下,遍历依靠list_entry函数。该函数通过task_struct中list_head对象tasks的指针,返回该task_struct对象的指针。特别声明一下,这只针对2.6以上版本的内核。之前的内核里,task_struct结构没有tasks对象,直接记录了指向前后两个task_struct对象的指针。(参见【Linux编译内核】显示进程列表_gamedev˚的博客-CSDN博客)

编译并安装模块。

希望大家都别入坑,一次成功!但上天往往会给人创造诸多意外,来考验人性,除非你是天选之人。就像闭不出户的也能莫名其妙地阳了,摆烂躺平的居然自带连花清瘟属性加持。

Linux成长得很快,隔年不见,路人皆知的“makefile”居然不好使了,捣鼓了半天终于在为数不多的帖子里找到了原因和解决方案,小m长大了,变成了大M:

Ubuntu12.10 内核源码外编译 linux模块--编译驱动模块的基本方法

“主要意思是这种编译方法不能很好的解决相关的依赖体系,主要是源于历史原因,linux内核升级很快,越来越复杂,所以建议使用kbuild体系来自动完成;故下面采用了可行的kbuild体系来完成。”

摘自上文链接。以后的Makefile得这样写:

obj-m:=querytask_struct.o

all:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

这样编译应该没问题了,在bash中执行dmesg可以在日志中查看输出:

窥探Linux内核进程信息task_struct_第1张图片

printk忠实地记录了遍历日志,完成了初探。网上瞥见,有通过修改循环双向列表指针来隐藏进程的文章,待回头抽空试一下,似乎很有意思。

进一步来讲,每次进入内核麻烦printk来输出日志,然后再用dmesg查询结果十分繁琐。况且,和内核的沟通,完全只能通过模块的加载和卸载函数,仅此一次,效率低、成本高,如同去医院看病:医生,我这不舒服。挂号了吗?挂了。拿挂号单来,去化验室检查去吧...(排队检查中)...医生,我昨天的检查结果今天出来了,您看看。挂号了吗?挂了。这个指标高,再去***做个进一步检查...(排队检查中)...医生,昨天我去***检查的结果出来,麻烦您看看。挂号了吗?挂了。可能是***,也可能是***,再去***做个检查吧...(排队检查中)...医生,3天前我做的检查出来结果了,您看看。挂号了吗?挂了。给你开个***药试试吧。一个月后还挂我的号复诊。....(服药中)...医生...挂号了吗?挂了......

马云改变了银行,谁来改变医院呢?

能不能控制程序借助模块驱动直接向内核发送指令并获取输出结果到用户终端呢?似乎能将printk的输出重定向到tty,未曾尝试过,可能大家都觉得鸡肋,没几人愿意去尝试吧。毕竟内核是内核,用户是用户,人家已经打开大门让窥视到了一点内幕,想要堂而皇之地、正大光明地越权带走,还是需要疏通一下渠道的。这个渠道主要有:共享内存、信号、管道等(参见:内核态和用户态通讯知识收集_weixin_30407099的博客-CSDN博客)。方法不少,选择比较优雅一点的netlink机制吧,本想试试共享内存,目前还没有那么大的规模,先从小量数据做起吧。

hi,netlink,交个朋......怎么这么眼熟?怎么看都像tcp、udp的亲兄弟,他俩熟啊。好歹也是一网络规划设计师呀,就靠这哥俩啊。既然是友亲,那还客气啥,什么socket结构、协议号、响应函数等等都了解,合作愉快啊!(参见:

内核态和用户态通讯知识收集_weixin_30407099的博客-CSDN博客)

有熟人带路,这事好办多了:-),下面就拜托多辛苦几趟,把printk的日志抄一份出来吧,谢谢!方法是:内核模块中申请一块缓冲区1024足够了。将进程的信息,如pid等写入,然后利用netlink发送给用户,哦,用户还得有个接收展示进程,也得申请1024的缓冲区,用来接收。如此多次循环,即可在用户态显示全部进程信息。

内核模块代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define NETLINK_TEST 30
#define MAX_MSGSIZE 125
#define USER_PORT 100

MODULE_LICENSE("GPL");

struct sock *nl_sk=NULL;

void sendnlmsg(char *message, int dstPID)
{
	struct sk_buff *skb;
	struct nlmsghdr *nlh;
	int len = NLMSG_SPACE(MAX_MSGSIZE);
	int slen=0;
	
	if(!message||!nl_sk)
	{
		return ;
	}

	skb=alloc_skb(len,GFP_KERNEL);

	if(!skb)
	{
		printk(KERN_ERR "my_net_link: alloc_sck Error.\n");
		return;
	}



	nlh=nlmsg_put(skb, 0, 0, 0, MAX_MSGSIZE, 0);
	
	NETLINK_CB(skb).portid=0;
	NETLINK_CB(skb).dst_group=0;

	slen=strlen(message);
	if(message[slen]!='\0')
	{
		printk(KERN_ERR "=========================%c.....\n",message[slen]);
		slen+=1;
		message[slen]='\0';
	}
	
	memset(NLMSG_DATA(nlh),0,slen+1);
	memcpy(NLMSG_DATA(nlh),message,slen);
printk(KERN_ERR "==========%d===============%s.....\n",slen,NLMSG_DATA(nlh));
	netlink_unicast(nl_sk,skb,dstPID,0);
	printk("send OK!\n");
	return;
}


int querytask(int dstPID)
{
	struct task_struct *p=&init_task;
	struct list_head *final=p->tasks.prev;
	struct list_head *phnext=p->tasks.next;

	char msg_buff[MAX_MSGSIZE];
	
	printk("let's begin!--\n");

	do
	{
		memset(msg_buff, '\0', MAX_MSGSIZE);
		snprintf(msg_buff,MAX_MSGSIZE,"The Process is \"%s\" (pid %i)\nThe task state is %ld\n",p->comm,p->pid,p->state);
		printk(KERN_INFO "%s",msg_buff);
		sendnlmsg(msg_buff, dstPID);
		p=list_entry(p->tasks.next,struct task_struct,tasks);
	}while(p->tasks.next!=phnext);//final

	char path[] ="/bin/bash";
	char *argv[]={path,"-c","whoami>/home/singsh/IamW.txt","",">","/home/singsh/Iam2.txt",NULL};
	char *envp[]={NULL};

	int ret=call_usermodehelper(path,argv,envp,UMH_WAIT_PROC);
	printk(KERN_INFO "\nret=%d\n",ret);
/*
	char *argv[]={"ls","-al","/etc/passwd",NULL};
	char *envp[]={"PATH=/bin",NULL};
	execve("/bin/ls",argv,envp);
*/
	return 0;
}

static void nl_data_ready(struct sock *sk, int len)
{
	struct sk_buff *skb;
	struct nlmsghdr *nlh = NULL;
	while((skb_dequeue(&sk->sk_receive_queue))!=NULL)
	{
		nlh=(struct nlmsghdr*)skb->data;
		printk("%s: received netlink message payload : %s \n",__FUNCTION__,(char*)NLMSG_DATA(nlh));
		kfree_skb(skb);

		sendnlmsg("I see you", nlh->nlmsg_pid);
	}

	printk("received finished!\n");
}

static void netlink_rcv_msg(struct sk_buff *skb)
{
	struct nlmsghdr *nlh=NULL;
	char * umsg=NULL;
	char *kmsg="hello users!!!";
	if(skb->len>=nlmsg_total_size(0))
	{
		nlh=nlmsg_hdr(skb);
		umsg=NLMSG_DATA(nlh);
		if(umsg)
		{
			printk("kernel recv from user:%s ,pid: %d \n",umsg,nlh->nlmsg_pid);
			//send_usrmsg(kmsg,strlen(kmsg));
/*			char* msg="I see you";
			sendnlmsg(msg, nlh->nlmsg_pid);
*/
			int res=querytask(nlh->nlmsg_pid);
			sendnlmsg("be quit!", nlh->nlmsg_pid);
		}
	}
}

struct netlink_kernel_cfg cfg;

static int __init myinit_module(void)
{
	printk("my netlink in\n");
//	nl_sk=netlink_kernel_create(NETLINK_TEST, 0, nl_data_ready, THIS_MODULE);

	cfg.input=netlink_rcv_msg;
	nl_sk=netlink_kernel_create(&init_net,NETLINK_TEST,&cfg);
	return 0;
}

static void __exit mycleanup_module(void)
{
	printk("my netlink out!\n");
	sock_release(nl_sk->sk_socket);
}

module_init(myinit_module);
module_exit(mycleanup_module);

用户端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_PAYLOAD 1024
#define NETLINK_TEST 30

void test()
{
	char str[]="ABCD";//char *str="ABCD";to cause error: *(p+slen)='1'
	char arr[]="abcd";//same as above
	char* str2="DEFG";//same as above
	char str3[]="defg";
	char* varArr="varArr";
/*	varArr[0]='e';
	varArr[1]='e';
	varArr[2]='e';
*/
	char* p;
	p=arr;
	p=str2;
	int slen=strlen(p);
	printf("================%s=========%d....%c.\n",p,slen,p[0]);
//	*(p+slen)='1';
	printf("================%s=========%d....%c.\n",p+slen+1,slen,p[slen]);
	return;
}

void showCurrentUser()
{

	FILE *fp;
	char kernel_user[128];
	memset(kernel_user,0,sizeof(kernel_user));
	fp=popen("sudo whoami","r");
	fgets(kernel_user,sizeof(kernel_user),fp);

	printf("%s\n",kernel_user);
	return;
//	system("whoami");system("uname -a");
}

int main(int argc, char* argv[])
{
	struct sockaddr_nl dest_addr;
	struct nlmsghdr *nlh=NULL;
	struct iovec iov;
	int sock_fd=-1;
	struct msghdr msg;

	if(-1==(sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST)))
	{
		perror("can't create netlink socket!");
		return 1;
	}

	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.nl_family=AF_NETLINK;
	dest_addr.nl_pid=0;
	dest_addr.nl_groups=0;

	if(-1==bind(sock_fd, (struct sockaddr*)&dest_addr,sizeof(dest_addr)))
	{
		perror("can't bind sockfd with sockaddr_nl!");
		return 1;
	}

	if(NULL==(nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD))))
	{
		perror("alloc mem failed!");
		return 1;
	}

	memset(nlh,0,MAX_PAYLOAD);
	nlh->nlmsg_len=NLMSG_SPACE(MAX_PAYLOAD);
	nlh->nlmsg_pid=getpid();
	nlh->nlmsg_type=NLMSG_NOOP;
	nlh->nlmsg_flags=0;

	strcpy(NLMSG_DATA(nlh),argv[1]);

	memset(&iov, 0, sizeof(iov));
	iov.iov_base=(void*)nlh;
	iov.iov_len=nlh->nlmsg_len;
	memset(&msg, 0, sizeof(msg));
	msg.msg_iov=&iov;
	msg.msg_iovlen=1;

	sendmsg(sock_fd, &msg, 0);

	printf("waiting message from kernel!\n");

	while(1)
	{
		memset((char*)NLMSG_DATA(nlh),'\0',1024);
		recvmsg(sock_fd,&msg,0);
		if(strcmp(NLMSG_DATA(nlh),"be quit!")==0)
		{
			break;
		}else{printf("not equal:%s\n",NLMSG_DATA(nlh));}
	//	printf("Got response:%s\n",NLMSG_DATA(nlh));
	}

	close(sock_fd);
	free(nlh);

	return 0;
}

安装模块,运行客户端程序(注:需要随便添个参数),结果见下图 :

窥探Linux内核进程信息task_struct_第2张图片

运行环境如下:

#uname -a

Linux singsh-virtual-machine 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:34:49 UTC 2016 i686 i686 i686 GNU/Linux

忙乎了几周中间过了个后新冠春节,看了《阿凡达2》和《坑》,终于实现了。是不是下一步就要尝尝隐藏进程了(*^__^*) ……。

加深了对几个基础知识点的理解:

  • 程序中的常量是存放在同一内存区的,甚至可以越界访问
  • 当定义char* p="ABCD"时,p指向的是一个匿名的常量char数组,*(p+1)不能修改
  • 当定义char str[]="ABCD"时,str是变量,str[1]的值可以修改
  • 内核可以执行用户态程序、shell等,执行whoami的结果是root
  • 内核中不能使用#include ,这是用户态的库

也许大佬们会说:“就这?”对,就这。参考了不少前人的源码,不,照搬了不少。组装了一个小组件吧。欢迎哪位爱好者在此基础上突发奇想,玩出新花样、改出新高度。隐藏进程的尝试略作稍等。想着隐藏一个进程后,在黑乎乎的窗口中依次查找那个不存在条目,颇费力气,何不体验一下Linux下的窗口界面思想——X windows?

用惯了.net studio平台,一直想逃离,因为它——太大了,吃内存也就罢了,还吃硬盘。查了半天编译器选中了code::blocks,费了半天劲终于在虚拟机里安装成功了。启动平台->编码->编译->运行,然后就没有然后了。各种度娘,各种尝试,终于发现项目全路径不能含有中文!晕菜,可能是13版本的bug吧,apt-get没有新版本的,也没想法去升级。可最终令人苦笑不得的是,它是个集成编译器,也就是个集成编译器,根本没有图形界面可视化开发功能/(ㄒoㄒ)/~~

于是把他的兄弟glade找来,后来居然发现这哥们压根就用不着codeblocks等,自己生成个界面,在bash里直接gcc编译就行了╮(╯▽╰)╭,忙乎一圈返璞归真了。

这是修改后的模块代码:

#ifndef CONNECTKERNEL_H_INCLUDED
#define CONNECTKERNEL_H_INCLUDED

int insModKo();
//int getProcessInfo();
int getProcessInfo(void* callbackprint);

#include "connectKernel.c"

#endif // CONNECTKERNEL_H_INCLUDED
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "connectKernel.h"

#define MAX_PAYLOAD 1024
#define NETLINK_TEST 30

extern GtkListStore *plists;

int insModKo()
{
	FILE *fp;
	char kernel_user[128];

	memset(kernel_user,0,sizeof(kernel_user));
	fp=popen("sudo insmod kmsgs.ko","r");
	fgets(kernel_user,sizeof(kernel_user),fp);

	printf("%s\n",kernel_user);
	return 0;
}

void printToConsole(char* str)
{
    printf("Data from kernel :%s\n",str);
}

int getProcessInfo(void* callbackprint)
{
	struct sockaddr_nl dest_addr;
	struct nlmsghdr *nlh=NULL;
	struct iovec iov;
	int sock_fd=-1;
	struct msghdr msg;

	if(-1==(sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST)))
	{
		perror("can't create netlink socket!");
		return 1;
	}

	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.nl_family=AF_NETLINK;
	dest_addr.nl_pid=0;
	dest_addr.nl_groups=0;

	if(-1==bind(sock_fd, (struct sockaddr*)&dest_addr,sizeof(dest_addr)))
	{
		perror("can't bind sockfd with sockaddr_nl!");
		return 1;
	}

	if(NULL==(nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD))))
	{
		perror("alloc mem failed!");
		return 1;
	}

	memset(nlh,0,MAX_PAYLOAD);
	nlh->nlmsg_len=NLMSG_SPACE(MAX_PAYLOAD);
	nlh->nlmsg_pid=getpid();
	nlh->nlmsg_type=NLMSG_NOOP;
	nlh->nlmsg_flags=0;

//	strcpy(NLMSG_DATA(nlh),"\0");//argv[1]

	memset(&iov, 0, sizeof(iov));
	iov.iov_base=(void*)nlh;
	iov.iov_len=nlh->nlmsg_len;
	memset(&msg, 0, sizeof(msg));
	msg.msg_iov=&iov;
	msg.msg_iovlen=1;

	sendmsg(sock_fd, &msg, 0);

	printf("waiting message from kernel!\n");

    int k=0;
    void (*func)(char*);
    if(callbackprint==NULL)
    {
        func=printToConsole;
    //    printf("not equal:%s\n",NLMSG_DATA(nlh));
    }else{
        func=callbackprint;
    }
	while(1)
	{
		memset((char*)NLMSG_DATA(nlh),'\0',MAX_PAYLOAD);
		recvmsg(sock_fd,&msg,0);
		if(strcmp(NLMSG_DATA(nlh),"be quit!")==0)
		{
			break;
		}else{
            func((char*)(NLMSG_DATA(nlh)));
		}
	//	printf("Got response:%s\n",NLMSG_DATA(nlh));
	}

	close(sock_fd);
	free(nlh);

	return 0;
}

这是修改后的用户端代码,基于GTK+:

#include 
#include 

#include "connectKernel.h"

GtkListStore *plists = NULL;
static void helloWorld (GtkWidget *wid, GtkWidget *win)
{
  GtkWidget *dialog = NULL;

  dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "Hello World!");
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

static gint  btnClick(GtkWidget* w, GdkEventAny* e, gpointer data)
{
    int argc;
    char *argv[100];
  GtkWidget *button = NULL;
  GtkWidget *win = NULL;
  GtkWidget *vbox = NULL;

  /* Initialize GTK+ */
  g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL);
  gtk_init (&argc, &argv);
  g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);

  /* Create the main window */
  win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (win), 8);
  gtk_window_set_title (GTK_WINDOW (win), "Hello World");
  gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
  gtk_widget_realize (win);
  g_signal_connect (win, "destroy", gtk_main_quit, NULL);

  /* Create a vertical box with buttons */
  vbox = gtk_vbox_new (TRUE, 6);
  gtk_container_add (GTK_CONTAINER (win), vbox);

  button = gtk_button_new_from_stock (GTK_STOCK_DIALOG_INFO);
  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (helloWorld), (gpointer) win);
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);

  button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
  g_signal_connect (button, "clicked", gtk_main_quit, NULL);
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);

  /* Enter the main loop */
  gtk_widget_show_all (win);
  gtk_main ();
  return 0;
}

void test2()
{
    static int k=9;printf("in test2 : k=%d\r\n",k++);
}

void test3()
{
    static int k=34;printf("in test3 : k=%d\r\n",k++);
}

void freshData(char* str)
{
    GtkTreeIter iter;
    gtk_list_store_append(plists,&iter);
    static int k=0;
//    test2();test3();printf("k=%d\r\n",k++);
    gtk_list_store_set(plists,&iter,0,k++,1,str,-1);
    printToConsole(str);
}



static void updateList(GtkButton *button, gpointer data)
{
    GtkListStore *plist = (GtkListStore*)(data);

    gtk_list_store_clear(plist);

    if(FALSE&&insModKo())
    {
        return;
    }

    getProcessInfo(freshData);

    return;
}

int main (int argc, char *argv[])
{
    gtk_init(&argc,&argv);

    GtkBuilder *builder=gtk_builder_new();

    if(!gtk_builder_add_from_file(builder,"./UI.glade",NULL))
    {
        printf("cannot load file!");
    }

    GtkWidget *window=GTK_WIDGET(gtk_builder_get_object(builder,"wndMain"));
    g_signal_connect(window, "destroy", gtk_main_quit , NULL);

    GtkWidget *btn=GTK_WIDGET(gtk_builder_get_object(builder,"btnAction"));
    g_signal_connect(btn, "clicked", G_CALLBACK(btnClick) , NULL);//gtk_main_quit

    GtkTreeIter iter;

    GtkListStore *liststr = GTK_LIST_STORE(gtk_builder_get_object(builder,"liststore1"));
/*    gtk_list_store_append(liststr,&iter);
    gtk_list_store_set(liststr,&iter,0,'t',1,"jpg",-1);
*/    plists=liststr;    printf("equl value:%d\r\n",plists==liststr);

    GtkButton *btnRef=GTK_BUTTON(gtk_builder_get_object(builder,"btnRefresh"));
    g_signal_connect(btnRef, "clicked", updateList , (gpointer)liststr);//gtk_main_quit

    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}

用glade设计的界面:

窥探Linux内核进程信息task_struct_第3张图片

 彩蛋:对局部static变量作用的了解更进一步啦

参考文献:

内核态和用户态通讯知识收集
https://blog.csdn.net/weixin_30407099/article/details/99148410

用户空间和内核空间通讯之【Netlink 中】
http://blog.chinaunix.net/uid-23069658-id-3405954.html

linux Netlink通信机制
https://www.cnblogs.com/wenqiang/p/6306727.html

[shell]C语言调用shell脚本接口
https://www.cnblogs.com/aaronLinux/p/6902952.html

在linux内核编程 不能include 的原因
https://blog.csdn.net/u011328417/article/details/51979784


驱动程序中使用系统调用的方法
https://blog.csdn.net/flyer0704090111/article/details/119935081

Linux内核态调用用户态函数
https://blog.csdn.net/zirandu/article/details/120651440

GTK入门学习:glade的使用
https://tennysonsky.blog.csdn.net/article/details/43019923?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-43019923-blog-40583437.pc_relevant_multi_platform_whitelistv3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-43019923-blog-40583437.pc_relevant_multi_platform_whitelistv3&utm_relevant_index=1

GTK+ and Glade3 GUI Programming Tutorial--中文系列
https://blog.csdn.net/xbwee/article/details/4032652


Linux下GTK+3.0开发环境配置
https://www.jianshu.com/p/1401a747e632

使用GTK与Glade创建一个简单的列表的图形详解
https://blog.csdn.net/luan111111/article/details/8667057


三、GTK-3.0关于GtkTreeModel、GtkListStore、GtkTreeView
https://blog.csdn.net/Creationyang/article/details/121436331

GTK+ 编程笔记 (1) -- 编程架构
http://blog.chinaunix.net/uid-21778123-id-1815463.html

GTK+ 3 Reference Manual
http://www.manpagez.com/html/gtk/gtk-3.12.2/GtkListStore.php#gtk-list-store-append

静态局部变量
https://blog.csdn.net/pjh88/article/details/112303283

你可能感兴趣的:(Linux,内核,linux,运维,服务器)