题目
返回指定进程的相关时间信息,如进程创建时间、进程在用户态及内核态的运行时间、进程的所有子孙进程在用户态的运行时间及在内核态的运行时间等。
调用原型
1.copy_to_user()
2.find_get_pid()
参数含义:
*pid_t nr:指定pid号
typedef int pid_t;
返回值:
*pid
解析函数:
*find_get_pid()中获取pid的方式是调用get_pid()函数,而 get_pid()函数需要调用find_vpid(nr)
*同时,在获取pid之前我们对这段临界区代码加锁,获取后,解锁
相关调用:
get_pid()
find_vpid()
*current宏指向当前进程
*current https://blog.csdn.net/dragon101788/article/details/8079273
find_pid_ns()
*用于从namespace下找到对应的pid结构
*PID Namespace对进程PID重新标号,即不同的Namespace下的进程可以有同一个PID。
*内核为所有的PID Namespace维护了一个树状结构,最顶层的是系统初始化创建的,被称为Root Namespace,由它创建的新的PID Namespace成为它的Child namespace,原先的PID Namespace成为新创建的Parent Namespace,这种情况下不同的PID Namespace形成一个等级体系:父节点可以看到子节点中的进程,可以通过信号对子节点的进程产生影响,反过来子节点无法看到父节点PID Namespace里面的进程。
*Pid Namespace 原理与源码分析
https://zhuanlan.zhihu.com/p/335171876
idr_find()
*idr_find()函数返回了指向给定ID的指针
3.pid_task()
*PIDTYPE_PID表示当前进程的进程号
*PIDTYPE_PGID表示进程组的领头进程的进程号
参数含义:
*pid:指定pid结构,pid_type,pid 的类型
返回值:
*指定pid的task_struct结构
3.list_for_each(pos,head)
遍历链表节点中list_head域的位置
4.list_entry(ptr,type,member)
实现
1.linux5.10/kernel.sys.c
SYSCALL_DEFINE5(zwhsyscall,pid_t,pid,u64*,start_time,u64*,utime,u64*,stime,u64*,pid_list)
{
int s_num=0;
struct pid* fpid=NULL;
struct task_struct *ftask=NULL;
struct task_struct *p=NULL;
struct list_head *pos=NULL;
struct task_struct *p_=NULL;
struct list_head *pos_=NULL;
fpid=find_get_pid(pid);
ftask=pid_task(fpid,PIDTYPE_PID);
if(ftask==NULL){
return s_num;
}
copy_to_user(start_time,&ftask->start_time,sizeof(ftask->start_time));
copy_to_user(utime,&ftask->utime,sizeof(ftask->utime));
copy_to_user(stime,&ftask->stime,sizeof(ftask->stime));
copy_to_user(pid_list,&ftask->pid,sizeof(ftask->pid));
s_num=1;
list_for_each(pos,&ftask->children){
p=list_entry(pos,struct task_struct,sibling);
copy_to_user(start_time+s_num,&p->start_time,sizeof(p->start_time));
copy_to_user(utime+s_num,&p->utime,sizeof(p->utime));
copy_to_user(stime+s_num,&p->stime,sizeof(p->stime));
copy_to_user(pid_list+s_num,&p->pid,sizeof(p->pid));
s_num+=1;
list_for_each(pos_,&p->children){
p_=list_entry(pos_,struct task_struct,sibling);
copy_to_user(start_time+s_num,&p_->start_time,sizeof(p_->start_time));
copy_to_user(utime+s_num,&p_->utime,sizeof(p_->utime));
copy_to_user(stime+s_num,&p_->stime,sizeof(p_->stime));
copy_to_user(pid_list+s_num,&p_->pid,sizeof(p_->pid));
s_num+=1;
}
}
return s_num;
}
2.arch/x86/entry/syscalls/syscall_64.tbl
修改系统调用表
3.linux5.10/include/linux/syscalls.h
申明系统调用服务例程
4.编译内核
1.配置实验环境
虚拟机配置:建议虚拟机有60G左右的空间。
linux内核版本:5.10.37
*我的linux内核版本是5.10.37,在此内核版本下make编译后内核会占用40G左右
*随着linux版本越高,一般来说所需要的编译时间越长,占用空间也越大
2.解压缩内核源码文件xz -d linux-5.10.tar.xz
tar -xvf linux-...tar
3.清除残留的.config和.o文件make mrproper
4.配置内核make menuconfig
此处需要
gedit ./.config
,ctrl+F查找debian,删除相关项的值为空""
5.编译内核 生成启动映像文件
(1)执行命令之前需要做一些准备工作,如安装包等,防止我们在编译许久后突然发现自己某个包没装又需要重来一次,建议sudo su,管理员权限下安装apt-get install libssl-dev ison flex zstd build-essentialdwares
,一般提前安装这些包基本能够解决编译过程中报的错。
(2)执行命令:make
make -j12
建议:make是编译内核过程中耗时最久的一步,且容易出错,因此建议把处理线程j12
/j2
等开到最大,如何得知自己的电脑能够开到多大的线程?在任务管理器-性能中查看即可,如我的电脑的最大是16,那么最好就是开到12,比你的最大承受力稍微开低4个线程,保护电脑。
(3)如何判断编译是否成功?linux5.10/arch/x86_64/boot/bzImage
文件存在
6.编译模块make modules
7.安装内核make modules_install
make install
8.配置grub开机引导程序update-grub2
,该命令自动修改grub
9.reboot重启uname -a
查看内核版本,变化即成功;若未变化,可以在重启开机时按住shift
键,进入Ubuntu高级选项,启动你对应需要的内核版本,启动后再次使用uname -a
命令即可发现内核成功切换。
相关问题
1.已编译内核,但又修改了自己的系统调用例程,需要从make mrproper步骤重新开始吗?
R:不需要,直接make -j12即可,make命令只会重新编译修改的部分,一般几分钟就能完成二次编译。
参考
https://www.cnblogs.com/chicken-gay/p/17516238.html
https://blog.csdn.net/qq_36393978/article/details/124274364
https://blog.csdn.net/qq_44222849/article/details/105754952
https://blog.csdn.net/m0_56475481/article/details/127935829(可参考,但本人认为这篇代码有错误)
推荐一篇相关参考https://segmentfault.com/a/1190000021940881(此文写的很不错)