0
点赞
收藏
分享

微信扫一扫

Linux内核进程,线程,进程组,会话组织模型


Linux 内核创世与创生

Linux宇宙诞生之时,创建了三个重要进程,三个进程的PID分别为0,1,2.首先,由于初始从GRUB或者UBOOT跳入时刻没有进程存在,bringup阶段首先捏造了PID为0的swapper进程,对应task_struct是静态的,也就是init_task,有了第一个进程之后就好办了,如同女娲创世造人一般,剩下的进程都是按照init_task swapper进程的样子clone的(通过内核fork).swapper进程最后演化为内核idle进程,它是如此特殊,以至于用任何工具,或者/proc文件系统,都看不到它的影子,但是它是实实在在存在的。

swaper完成了自己的任务,创建了kernel thread init进程和kthreadd进程,就去养老了。把后面的工作留给了后继者。其中kernel_thread init进程完成了内核各类驱动的初始化,你看到的modue_init发起的内核驱动初始化调用,多半都是由于kernel init进程发起的,kenrel_thread最终不满足于内核,在执行的最后阶段,完成了内核阶段的使命之后,init终于破土而出,成为了用户空间的第一个进程,化身了另一个用户宇宙的创世者。而kthreadd进程则继续坚守在内核中,负责创造一个个默默无闻,兢兢业业内核态工作线程,你写驱动用到的workqueue,软中断,RCU等等等等线程,其父亲都是kthreadd进程。子又生孙,孙又生子;子又有子,子又有孙; 子子孙孙无穷匮也。

内核的创世很伟大,仅仅通过三个进程,便为我们创建了一个丰富的,可交互的世界。

Linux内核进程,线程,进程组,会话组织模型_linux

进程组与会话

进程组

什么是进程组?

  • 进程组:一组协同工作或关联进程的集合,每个进程组有ID(PGID)
  • 每个进程属于一个进程组,每一个进程组有一个进程组长,该进程组长ID(PID)与进程组ID(PGID)相同
  • 一个信号可以发送给进程组的所有进程、让所有进程终止、暂停或继续运行.

会话

什么是会话?

    会话是一个或多个进程组的集合

  •         当用户登录系统时,登录进程会为这个用户创建一个新的会话(session)
  •         shell进程(如bash)作为会话的第一个进程,称为会话进程(session leader)
  •         会话的PID(SID):等于会话首进程的PID
  •         会话会分配给用户一个控制终端(只能有一个),用于处理用户的输入输出
  •         一个会话包括了该登录用户的所有活动
  •         会话中的进程由一个前台进程组和N个后台进程组构成

kernel hack module code:

source file:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/pid.h>

MODULE_AUTHOR("zlcao");
MODULE_LICENSE("GPL");

int seqfile_debug_mode = 0;
module_param(seqfile_debug_mode, int, 0664);

// 开始输出任务列表
// my_seq_ops_start()的返回值,会传递给my_seq_ops_next()的v参数
static void *my_seq_ops_start(struct seq_file *m, loff_t *pos)
{
loff_t index = *pos;
struct task_struct *task;

printk("%s line %d, index %lld.count %ld, size %ld here.\n", __func__, __LINE__, index, m->count, m->size);

if(seqfile_debug_mode == 0) {
// 如果缓冲区不足, seq_file可能会重新调用start()函数,
// 并且传入的pos是之前已经遍历到的位置,
// 这里需要根据pos重新计算开始的位置
for_each_process(task) {
if (index-- == 0) {
return task;
}
}
} else if(seqfile_debug_mode == 1) {
return NULL + (*pos == 0);
} else if(seqfile_debug_mode == 2) {
return NULL + (*pos == 0);
} else if(seqfile_debug_mode == 3) {
return NULL + (*pos == 0);
} else {
return NULL + (*pos == 0);
}

return NULL;
}

// 继续遍历, 直到my_seq_ops_next()放回NULL或者错误
static void *my_seq_ops_next(struct seq_file *m, void *v, loff_t *pos)
{
struct task_struct *task = NULL;

if(seqfile_debug_mode == 0) {
task = next_task((struct task_struct *)v);

// 这里加不加好像都没有作用
++ *pos;

// 返回NULL, 遍历结束
if(task == &init_task) {
return NULL;
}
} else if(seqfile_debug_mode == 1) {
++ *pos;
} else if(seqfile_debug_mode == 2) {
++ *pos;
} else if(seqfile_debug_mode == 3) {
++ *pos;
} else {
++ *pos;
}

return task;
}

// 遍历完成/出错时seq_file会调用stop()函数
static void my_seq_ops_stop(struct seq_file *m, void *v)
{

}

// 此函数将数据写入`seq_file`内部的缓冲区
// `seq_file`会在合适的时候把缓冲区的数据拷贝到应用层
// 参数@V是start/next函数的返回值
static int my_seq_ops_show(struct seq_file *m, void *v)
{
struct task_struct * task = NULL;
struct task_struct * p = NULL;
struct file *file = m->private;

if(seqfile_debug_mode == 0) {
seq_puts(m, " file=");
seq_file_path(m, file, "\n");
seq_putc(m, ' ');

task = (struct task_struct *)v;
seq_printf(m, "PID=%u, task: %s, index=%lld, read_pos=%lld\n", task->tgid, task->comm, m->index, m->read_pos);
} else if(seqfile_debug_mode == 1) {
struct task_struct *g, *p;
static int oldcount = 0;
static int entercount = 0;
char *str;

printk("%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);
seq_printf(m, "%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);

rcu_read_lock();
for_each_process_thread(g, p) {
if(list_empty(&p->tasks)) {
str = "empty";
} else {
str = "not empty";
}
seq_printf(m, "process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. tasks->prev = %p, tasks->next = %p, p->tasks=%p, %s.\n",
g->comm, task_pid_nr(g), task_cpu(g), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(g), get_nr_threads(p), p->tasks.prev, p->tasks.next, &p->tasks, str);
if(oldcount == 0 || oldcount != m->size) {
printk("%s line %d, m->count %ld, m->size %ld.\n", __func__, __LINE__, m->count, m->size);
oldcount = m->size;
}
}
rcu_read_unlock();
} else if(seqfile_debug_mode == 2) {
for_each_process(task) {
struct pid *pgrp = task_pgrp(task);

seq_printf(m, "Group Header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_PGID, p);
}
} else if (seqfile_debug_mode == 3) {
for_each_process(task) {
struct pid *session = task_session(task);

seq_printf(m, "session header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(session, PIDTYPE_SID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_SID, p);
}
} else if(seqfile_debug_mode == 4) {
struct task_struct *thread, *child;
for_each_process(task) {
seq_printf(m, "process %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
for_each_thread(task, thread) {
list_for_each_entry(child, &thread->children, sibling) {
seq_printf(m, " thread %s(%d,cpu%d) child %s(%d,cpu%d),threadnum %d, %d.\n",
thread->comm, task_pid_nr(thread), task_cpu(thread), \
child->comm, task_pid_nr(child), task_cpu(child), \
get_nr_threads(thread), get_nr_threads(child));
}
}
}
} else {
printk("%s line %d,cant be here, seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
}

return 0;
}

static const struct seq_operations my_seq_ops = {
.start = my_seq_ops_start,
.next = my_seq_ops_next,
.stop = my_seq_ops_stop,
.show = my_seq_ops_show,
};

static int proc_seq_open(struct inode *inode, struct file *file)
{
int ret;
struct seq_file *m;

ret = seq_open(file, &my_seq_ops);
if(!ret) {
m = file->private_data;
m->private = file;
}

return ret;
}

static ssize_t proc_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos)
{
char debug_string[16];
int debug_no;

memset(debug_string, 0x00, sizeof(debug_string));
if (count >= sizeof(debug_string)) {
printk("%s line %d, fata error, write count exceed max buffer size.\n", __func__, __LINE__);
return -EINVAL;
}

if (copy_from_user(debug_string, buffer, count)) {
printk("%s line %d, fata error, copy from user failure.\n", __func__, __LINE__);
return -EFAULT;
}

if (sscanf(debug_string, "%d", &debug_no) <= 0) {
printk("%s line %d, fata error, read debugno failure.\n", __func__, __LINE__);
return -EFAULT;
}

seqfile_debug_mode = debug_no;

//printk("%s line %d, debug_no %d.\n", __func__, __LINE__, debug_no);

return count;
}

static ssize_t proc_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
ssize_t ret;

printk("%s line %d enter, fuck size %lld size %ld.\n", __func__, __LINE__, *ppos, size);

ret = seq_read(file, buf, size, ppos);

printk("%s line %d exit, fuck size %lld size %ld,ret = %ld.\n", __func__, __LINE__, *ppos, size, ret);

return ret;
}

static struct file_operations seq_proc_ops = {
.owner = THIS_MODULE,
.open = proc_seq_open,
.release = seq_release,
.read = proc_seq_read,
.write = proc_seq_write,
.llseek = seq_lseek,
.unlocked_ioctl = NULL,
};


static struct proc_dir_entry * entry;
static int proc_hook_init(void)
{
printk("%s line %d, init. seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
entry = proc_create("dumptask", 0644, NULL, &seq_proc_ops);
//entry = proc_create_seq("dumptask", 0644, NULL, &my_seq_ops);
return 0;
}

static void proc_hook_exit(void)
{
proc_remove(entry);

printk("%s line %d, exit.\n", __func__, __LINE__);

return;
}

module_init(proc_hook_init);
module_exit(proc_hook_exit);

Makefile

ifneq ($(KERNELRELEASE),)
obj-m:=seqfile.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.mod .*.cmd *.order
endif

install, the module support 3 dump method.

sudo insmod seqfile.ko seqfile_debug_mode=0

Linux内核进程,线程,进程组,会话组织模型_进程组_02

cat /proc/dumpstack

Linux内核进程,线程,进程组,会话组织模型_linux_03

sudo insmod seqfile.ko seqfile_debug_mode=1

Linux内核进程,线程,进程组,会话组织模型_运维_04

sudo insmod seqfile.ko seqfile_debug_mode=2

Linux内核进程,线程,进程组,会话组织模型_linux_05

conclusion:

系统中所有的进程都组织在init_task的tasks链表下面,每个进程的线程组织在每个进程task_sturct->signal的链表下,如下图所示

Linux内核进程,线程,进程组,会话组织模型_#include_06

加入子进程的real_parent逻辑关系后,为下图所示:

Linux内核进程,线程,进程组,会话组织模型_运维_07

the related implention in code:

Linux内核进程,线程,进程组,会话组织模型_服务器_08

the task struct structure member tasks are used to link all the system process together from init_task, but actually, the  task struct member ->tasks in thread are not used. the should be null, but after get the value verbose by above module, you will find the are not empty in thread as belows:

Linux内核进程,线程,进程组,会话组织模型_服务器_09

but why? may be this is inherent from the parent,but not used in child process, so it would be inherent from the parent, this not mean the are used. to prove this, we init the task_struct->task in copy_process function:

Linux内核进程,线程,进程组,会话组织模型_#include_10

recompile the kernel and launch the test, you will find all the thread is empty,only the process group leader which link to init_task as process are not empty,说明上面所制的图是符合实际情况的。

Linux内核进程,线程,进程组,会话组织模型_linux_11

获取进程内线程列表的方式:

上面介绍了通过struct task_struct->signal->thread_head成员获取一个进程线程列表的方式,其实除了这种方式,还有另一个方式,看代码:

Linux内核进程,线程,进程组,会话组织模型_#include_12

 如同singal成员的作用,group_leader似乎更加符合进程和线程的定位。并且内核中提供的处理对应链表的宏:

Linux内核进程,线程,进程组,会话组织模型_linux_13

修改代码,增加新的case seqfile_debug_mode=5.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/pid.h>

MODULE_AUTHOR("zlcao");
MODULE_LICENSE("GPL");

int seqfile_debug_mode = 0;
module_param(seqfile_debug_mode, int, 0664);

// 开始输出任务列表
// my_seq_ops_start()的返回值,会传递给my_seq_ops_next()的v参数
static void *my_seq_ops_start(struct seq_file *m, loff_t *pos)
{
loff_t index = *pos;
struct task_struct *task;

printk("%s line %d, index %lld.count %ld, size %ld here.\n", __func__, __LINE__, index, m->count, m->size);

if(seqfile_debug_mode == 0) {
// 如果缓冲区不足, seq_file可能会重新调用start()函数,
// 并且传入的pos是之前已经遍历到的位置,
// 这里需要根据pos重新计算开始的位置
for_each_process(task) {
if (index-- == 0) {
return task;
}
}
} else {
return NULL + (*pos == 0);
}

return NULL;
}

// 继续遍历, 直到my_seq_ops_next()放回NULL或者错误
static void *my_seq_ops_next(struct seq_file *m, void *v, loff_t *pos)
{
struct task_struct *task = NULL;

if(seqfile_debug_mode == 0) {
task = next_task((struct task_struct *)v);

// 这里加不加好像都没有作用
++ *pos;

// 返回NULL, 遍历结束
if(task == &init_task) {
return NULL;
}
} else {
++ *pos;
}

return task;
}

// 遍历完成/出错时seq_file会调用stop()函数
static void my_seq_ops_stop(struct seq_file *m, void *v)
{

}

// 此函数将数据写入`seq_file`内部的缓冲区
// `seq_file`会在合适的时候把缓冲区的数据拷贝到应用层
// 参数@V是start/next函数的返回值
static int my_seq_ops_show(struct seq_file *m, void *v)
{
struct task_struct * task = NULL;
struct task_struct * p = NULL;
struct file *file = m->private;

if(seqfile_debug_mode == 0) {
seq_puts(m, " file=");
seq_file_path(m, file, "\n");
seq_putc(m, ' ');

task = (struct task_struct *)v;
seq_printf(m, "PID=%u, task: %s, index=%lld, read_pos=%lld\n", task->tgid, task->comm, m->index, m->read_pos);
} else if(seqfile_debug_mode == 1) {
struct task_struct *g, *p;
static int oldcount = 0;
static int entercount = 0;
char *str;

printk("%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);
seq_printf(m, "%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);

rcu_read_lock();
for_each_process_thread(g, p) {
if(list_empty(&p->tasks)) {
str = "empty";
} else {
str = "not empty";
}
seq_printf(m, "process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. tasks->prev = %p, tasks->next = %p, p->tasks=%p, %s.\n",
g->comm, task_pid_nr(g), task_cpu(g), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(g), get_nr_threads(p), p->tasks.prev, p->tasks.next, &p->tasks, str);
if(oldcount == 0 || oldcount != m->size) {
printk("%s line %d, m->count %ld, m->size %ld.\n", __func__, __LINE__, m->count, m->size);
oldcount = m->size;
}
}
rcu_read_unlock();
} else if(seqfile_debug_mode == 2) {
for_each_process(task) {
struct pid *pgrp = task_pgrp(task);

seq_printf(m, "Group Header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_PGID, p);
}
} else if (seqfile_debug_mode == 3) {
for_each_process(task) {
struct pid *session = task_session(task);

seq_printf(m, "session header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(session, PIDTYPE_SID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_SID, p);
}
} else if(seqfile_debug_mode == 4) {
struct task_struct *thread, *child;
for_each_process(task) {
seq_printf(m, "process %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
for_each_thread(task, thread) {
list_for_each_entry(child, &thread->children, sibling) {
seq_printf(m, " thread %s(%d,cpu%d) child %s(%d,cpu%d),threadnum %d, %d.\n",
thread->comm, task_pid_nr(thread), task_cpu(thread), \
child->comm, task_pid_nr(child), task_cpu(child), \
get_nr_threads(thread), get_nr_threads(child));
}
}
}
} else if(seqfile_debug_mode == 5) {
struct task_struct *g, *t;
do_each_thread (g, t) {
seq_printf(m, "Process %s(%d cpu%d), thread %s(%d cpu%d), threadnum %d.\n", g->comm, task_pid_nr(g), task_cpu(g), t->comm, task_pid_nr(t), task_cpu(t), get_nr_threads(g));
} while_each_thread (g, t);
} else {
printk("%s line %d,cant be here, seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
}

return 0;
}

static const struct seq_operations my_seq_ops = {
.start = my_seq_ops_start,
.next = my_seq_ops_next,
.stop = my_seq_ops_stop,
.show = my_seq_ops_show,
};

static int proc_seq_open(struct inode *inode, struct file *file)
{
int ret;
struct seq_file *m;

ret = seq_open(file, &my_seq_ops);
if(!ret) {
m = file->private_data;
m->private = file;
}

return ret;
}

static ssize_t proc_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos)
{
char debug_string[16];
int debug_no;

memset(debug_string, 0x00, sizeof(debug_string));
if (count >= sizeof(debug_string)) {
printk("%s line %d, fata error, write count exceed max buffer size.\n", __func__, __LINE__);
return -EINVAL;
}

if (copy_from_user(debug_string, buffer, count)) {
printk("%s line %d, fata error, copy from user failure.\n", __func__, __LINE__);
return -EFAULT;
}

if (sscanf(debug_string, "%d", &debug_no) <= 0) {
printk("%s line %d, fata error, read debugno failure.\n", __func__, __LINE__);
return -EFAULT;
}

seqfile_debug_mode = debug_no;

//printk("%s line %d, debug_no %d.\n", __func__, __LINE__, debug_no);

return count;
}

static ssize_t proc_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
ssize_t ret;

printk("%s line %d enter, fuck size %lld size %ld.\n", __func__, __LINE__, *ppos, size);

ret = seq_read(file, buf, size, ppos);

printk("%s line %d exit, fuck size %lld size %ld,ret = %ld.\n", __func__, __LINE__, *ppos, size, ret);

return ret;
}

static struct file_operations seq_proc_ops = {
.owner = THIS_MODULE,
.open = proc_seq_open,
.release = seq_release,
.read = proc_seq_read,
.write = proc_seq_write,
.llseek = seq_lseek,
.unlocked_ioctl = NULL,
};


static struct proc_dir_entry * entry;
static int proc_hook_init(void)
{
printk("%s line %d, init. seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
entry = proc_create("dumptask", 0644, NULL, &seq_proc_ops);
//entry = proc_create_seq("dumptask", 0644, NULL, &my_seq_ops);
return 0;
}

static void proc_hook_exit(void)
{
proc_remove(entry);

printk("%s line %d, exit.\n", __func__, __LINE__);

return;
}

module_init(proc_hook_init);
module_exit(proc_hook_exit);

Linux内核进程,线程,进程组,会话组织模型_#include_14

Linux内核进程,线程,进程组,会话组织模型_linux_15

what is pgid and tgid.

From the literal, pgid is process group id, but tgid is thread group id.so you know the difference.

Linux内核进程,线程,进程组,会话组织模型_运维_16

thread_group leader:

Linux内核进程,线程,进程组,会话组织模型_linux_17

Linux内核进程,线程,进程组,会话组织模型_服务器_18

any process launched in shell would call setpgrp to set it self as a process group leader before execution the actual elf target.

Linux内核进程,线程,进程组,会话组织模型_linux_19

Linux内核进程,线程,进程组,会话组织模型_进程组_20

demo about pgrp:

=========================================================================

about namespace

namespace is based on the PID management of kernel,can be illustrated as below:

Linux内核进程,线程,进程组,会话组织模型_#include_21

在用户空间可以通过一个正整数来唯一标识一个进程(我们称这个正整数为pid number)。在引入容器之后,事情稍微复杂一点,pid这个正整数只能是唯一标识容器内的进程。也就是说,如果有容器1和容器2存在于系统中,那么可以同时存在两个pid等于a的进程,分别位于容器1和容器2。当然,进程也可以不在容器里,例如进程x和进程y,它们就类似传统的linux系统中的进程。当然,你也可以认为进程x和进程y位于一个系统级别的顶层容器0,其中包括进程x和进程y以及两个容器。同样的概念,容器2中也可以嵌套一个容器,从而形成了一个container hierarchy。

容器(linux container)是一个OS级别的虚拟化方法,基本上是属于纯软件的方法来实现虚拟化,开销小,量级轻,当然也有自己的局限。linux container主要应用了内核中的cgroup和namespace隔离技术,当然这些内容不是我们这份文档关心的,我们这里主要关心pid namespace。

当一个进程运行在linux OS之上的时候,它拥有了很多的系统资源,例如pid、user ID、网络设备、协议栈、IP以及端口号、filesystem hierarchy。对于传统的linux,这些资源都是全局性的,一个进程umount了某一个文件系统挂载点,改变了自己的filesystem hierarchy视图,那么所有进程看到的文件系统目录结构都变化了(umount操作被所有进程感知到了)。有没有可能把这些资源隔离开呢?这就是namespace的概念,而PID namespace就是用来隔离pid的地址空间的。

进程是感知不到pid namespace的,它只是知道能够通过getpid获取自己的ID,并不知道自己实际上被关在一个pid namespace的牢笼。从这个角度看,用户空间是简单而幸福的,内核空间就没有这么幸运了,我们需要使用复杂的数据结构来抽象这些形成层次结构的PID。

最后顺便说一句,上面的描述是针对pid而言的,实际上,tid、pgid和sid都是一样的概念,原来直接使用这些ID就可以唯一标识一个实体,现在我们需要用(pid namespace,ID)来唯一标识一个实体。

每个容器都有一个在自身看来PID为1的进程。

Linux内核进程,线程,进程组,会话组织模型_linux_22

比如:

$ sudo docker container top b03e403e73a0
UID PID PPID C STIME TTY TIME CMD
root 16800 16777 0 08:36 ? 00:00:00 bash
root 16887 16800 0 08:36 ? 00:00:00 top
$ sudo docker exec b03e403e73a0 ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 00:36 pts/0 00:00:00 bash
root 17 1 0 00:36 pts/0 00:00:00 top
root 30 0 0 00:43 ? 00:00:00 ps -ef

dkms auto probe and install module

如果要问 Linux 内核模块如何发布、安装。脑回路的第一反应肯定是 make && insmod。上述方法可以满足嵌入式场景,因为嵌入式产品的软件是整体发布,包括:内核、模块、软件等交付件。但是在 PC/服务器 领域,各个组件都是互相独立的,如果一个模块基于内核 A 编译并发布,那用户更改内核后,之前发布的内核模块就不能用了。所以,DELL 发布了 DKMS,全称 Dynamic Kernel Module System。可以做到内核变更后自动编译模块,适配新内核。we try this.

install dkms:

$ sudo apt install dkms

create dkms.conf file in module dir.

字段含义还比较清晰,设置模块名,编译方法。最重要的就是 ​​AUTOINSTALL​​ 字段,表示内核变更时要重新编译模块。

PACKAGE_NAME="seqfile"
PACKAGE_VERSION="1.0.0"
CLEAN="make clean"
MAKE[0]="make all"
BUILT_MODULE_NAME[0]="seqfile"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"

Linux内核进程,线程,进程组,会话组织模型_#include_23

after this, the module dir should be like this,目录符合 DKMS 规范

Linux内核进程,线程,进程组,会话组织模型_服务器_24

install dir

DKMS 模块代码目录位于 ​​/usr/src/modulename-version​​​,比如:​​/usr/src/seqfile-1.0.0​​,表示 seqfile 模块的 1.0.0 版本。then we copy the module dir to /usr/src director

Linux内核进程,线程,进程组,会话组织模型_服务器_25

modify the makefile,we did not use the kbuild module first stage.

#ifneq ($(KERNELRELEASE),)
obj-m:=seqfile.o
#else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.mod .*.cmd *.order
#endif

add the seqfile to dkms management

$ sudo dkms add -m seqfile -v 1.0.0

Creating symlink /var/lib/dkms/seqfile/1.0.0/source ->
/usr/src/seqfile-1.0.0

DKMS: add completed.
$ dkms status
seqfile, 1.0.0: added
virtualbox, 5.2.42, 5.4.0-131-generic, x86_64: installed
virtualbox, 5.2.42, 5.4.0-132-generic, x86_64: installed

Linux内核进程,线程,进程组,会话组织模型_运维_26

compile:

the default target is "all", we run

$ sudo dkms build -m seqfile -v 1.0.0

Linux内核进程,线程,进程组,会话组织模型_#include_27

install:

$ sudo dkms install -m seqfile -v 1.0.0

Linux内核进程,线程,进程组,会话组织模型_服务器_28

the result

Linux内核进程,线程,进程组,会话组织模型_进程组_29

remove

$ sudo dkms remove -m seqfile -v 1.0.0 -k 5.4.0-132-generic

Linux内核进程,线程,进程组,会话组织模型_进程组_30

验证部分

1.进程和子进程的SESSION ID以及PARENT

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/pid.h>

MODULE_AUTHOR("zlcao");
MODULE_LICENSE("GPL");

int seqfile_debug_mode = 0;
module_param(seqfile_debug_mode, int, 0664);

// 开始输出任务列表
// my_seq_ops_start()的返回值,会传递给my_seq_ops_next()的v参数
static void *my_seq_ops_start(struct seq_file *m, loff_t *pos)
{
loff_t index = *pos;
struct task_struct *task;

printk("%s line %d, index %lld.count %ld, size %ld here.\n", __func__, __LINE__, index, m->count, m->size);

if(seqfile_debug_mode == 0) {
// 如果缓冲区不足, seq_file可能会重新调用start()函数,
// 并且传入的pos是之前已经遍历到的位置,
// 这里需要根据pos重新计算开始的位置
for_each_process(task) {
if (index-- == 0) {
return task;
}
}
} else {
return NULL + (*pos == 0);
}

return NULL;
}

// 继续遍历, 直到my_seq_ops_next()放回NULL或者错误
static void *my_seq_ops_next(struct seq_file *m, void *v, loff_t *pos)
{
struct task_struct *task = NULL;

if(seqfile_debug_mode == 0) {
task = next_task((struct task_struct *)v);

// 这里加不加好像都没有作用
++ *pos;

// 返回NULL, 遍历结束
if(task == &init_task) {
return NULL;
}
} else {
++ *pos;
}

return task;
}

// 遍历完成/出错时seq_file会调用stop()函数
static void my_seq_ops_stop(struct seq_file *m, void *v)
{

}

// 此函数将数据写入`seq_file`内部的缓冲区
// `seq_file`会在合适的时候把缓冲区的数据拷贝到应用层
// 参数@V是start/next函数的返回值
static int my_seq_ops_show(struct seq_file *m, void *v)
{
struct task_struct * task = NULL;
struct task_struct * p = NULL;
struct file *file = m->private;

if(seqfile_debug_mode == 0) {
seq_puts(m, " file=");
seq_file_path(m, file, "\n");
seq_putc(m, ' ');

task = (struct task_struct *)v;
seq_printf(m, "PID=%u, task: %s, index=%lld, read_pos=%lld\n", task->tgid, task->comm, m->index, m->read_pos);
} else if(seqfile_debug_mode == 1) {
struct task_struct *g, *p;
static int oldcount = 0;
static int entercount = 0;
char *str;

printk("%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);
seq_printf(m, "%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);

rcu_read_lock();
for_each_process_thread(g, p) {
struct task_struct *session = pid_task(task_session(g), PIDTYPE_PID);
struct task_struct *thread = pid_task(task_session(p), PIDTYPE_PID);

if(list_empty(&p->tasks)) {
str = "empty";
} else {
str = "not empty";
}
seq_printf(m, "process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. tasks->prev = %p, tasks->next = %p, p->tasks=%p, %s, process parent %s(%d), thread parent%s(%d)",
g->comm, task_pid_nr(g), task_cpu(g), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(g), get_nr_threads(p), p->tasks.prev, p->tasks.next, &p->tasks, str, g->real_parent->comm, \
task_pid_nr(g->real_parent), p->real_parent->comm, task_pid_nr(p->real_parent));
if(thread)
{
seq_printf(m, "thread session %s(%d).", thread->comm, task_pid_nr(thread));
}
if(session)
{
seq_printf(m, "process session %s(%d).\n", session->comm, task_pid_nr(session));
}
else
seq_printf(m, "\n");

if(oldcount == 0 || oldcount != m->size) {
printk("%s line %d, m->count %ld, m->size %ld.\n", __func__, __LINE__, m->count, m->size);
oldcount = m->size;
}
}
rcu_read_unlock();
} else if(seqfile_debug_mode == 2) {
for_each_process(task) {
struct pid *pgrp = task_pgrp(task);

seq_printf(m, "Group Header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_PGID, p);
}
} else if (seqfile_debug_mode == 3) {
for_each_process(task) {
struct pid *session = task_session(task);

seq_printf(m, "session header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(session, PIDTYPE_SID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_SID, p);
}
} else if(seqfile_debug_mode == 4) {
struct task_struct *thread, *child;
for_each_process(task) {
seq_printf(m, "process %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
for_each_thread(task, thread) {
list_for_each_entry(child, &thread->children, sibling) {
seq_printf(m, " thread %s(%d,cpu%d) child %s(%d,cpu%d),threadnum %d, %d.\n",
thread->comm, task_pid_nr(thread), task_cpu(thread), \
child->comm, task_pid_nr(child), task_cpu(child), \
get_nr_threads(thread), get_nr_threads(child));
}
}
}
} else if(seqfile_debug_mode == 5) {
struct task_struct *g, *t;
do_each_thread (g, t) {
seq_printf(m, "Process %s(%d cpu%d), thread %s(%d cpu%d), threadnum %d.\n", g->comm, task_pid_nr(g), task_cpu(g), t->comm, task_pid_nr(t), task_cpu(t), get_nr_threads(g));
} while_each_thread (g, t);
} else if(seqfile_debug_mode == 6) {
for_each_process(task) {
struct pid *pid = task_pid(task);

seq_printf(m, "Process %s(%d,cpu%d) pid %d, tgid %d:\n", task->comm, task_pid_nr(task), task_cpu(task), task_pid_vnr(task), task_tgid_vnr(task));
do_each_pid_task(pid, PIDTYPE_TGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. pid %d, tgid %d\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), task_pid_vnr(p), task_tgid_vnr(p));
} while_each_pid_task(pid, PIDTYPE_TGID, p);
}
} else if(seqfile_debug_mode == 7) {
for_each_process(task) {
struct pid *pid = task_pid(task);

seq_printf(m, "Process %s(%d,cpu%d) pid %d, tgid %d:\n", task->comm, task_pid_nr(task), task_cpu(task), task_pid_vnr(task), task_tgid_vnr(task));
do_each_pid_task(pid, PIDTYPE_PID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. pid %d, tgid %d\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), task_pid_vnr(p), task_tgid_vnr(p));
} while_each_pid_task(pid, PIDTYPE_PID, p);
}
} else {
printk("%s line %d,cant be here, seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
}

return 0;
}

static const struct seq_operations my_seq_ops = {
.start = my_seq_ops_start,
.next = my_seq_ops_next,
.stop = my_seq_ops_stop,
.show = my_seq_ops_show,
};

static int proc_seq_open(struct inode *inode, struct file *file)
{
int ret;
struct seq_file *m;

ret = seq_open(file, &my_seq_ops);
if(!ret) {
m = file->private_data;
m->private = file;
}

return ret;
}

static ssize_t proc_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos)
{
char debug_string[16];
int debug_no;

memset(debug_string, 0x00, sizeof(debug_string));
if (count >= sizeof(debug_string)) {
printk("%s line %d, fata error, write count exceed max buffer size.\n", __func__, __LINE__);
return -EINVAL;
}

if (copy_from_user(debug_string, buffer, count)) {
printk("%s line %d, fata error, copy from user failure.\n", __func__, __LINE__);
return -EFAULT;
}

if (sscanf(debug_string, "%d", &debug_no) <= 0) {
printk("%s line %d, fata error, read debugno failure.\n", __func__, __LINE__);
return -EFAULT;
}

seqfile_debug_mode = debug_no;

//printk("%s line %d, debug_no %d.\n", __func__, __LINE__, debug_no);

return count;
}

static ssize_t proc_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
ssize_t ret;

printk("%s line %d enter, fuck size %lld size %ld.\n", __func__, __LINE__, *ppos, size);

ret = seq_read(file, buf, size, ppos);

printk("%s line %d exit, fuck size %lld size %ld,ret = %ld.\n", __func__, __LINE__, *ppos, size, ret);

return ret;
}

static struct file_operations seq_proc_ops = {
.owner = THIS_MODULE,
.open = proc_seq_open,
.release = seq_release,
.read = proc_seq_read,
.write = proc_seq_write,
.llseek = seq_lseek,
.unlocked_ioctl = NULL,
};


static struct proc_dir_entry * entry;
static int proc_hook_init(void)#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/pid.h>

MODULE_AUTHOR("zlcao");
MODULE_LICENSE("GPL");

int seqfile_debug_mode = 0;
module_param(seqfile_debug_mode, int, 0664);

// 开始输出任务列表
// my_seq_ops_start()的返回值,会传递给my_seq_ops_next()的v参数
static void *my_seq_ops_start(struct seq_file *m, loff_t *pos)
{
loff_t index = *pos;
struct task_struct *task;

printk("%s line %d, index %lld.count %ld, size %ld here.\n", __func__, __LINE__, index, m->count, m->size);

if(seqfile_debug_mode == 0) {
// 如果缓冲区不足, seq_file可能会重新调用start()函数,
// 并且传入的pos是之前已经遍历到的位置,
// 这里需要根据pos重新计算开始的位置
for_each_process(task) {
if (index-- == 0) {
return task;
}
}
} else {
return NULL + (*pos == 0);
}

return NULL;
}

// 继续遍历, 直到my_seq_ops_next()放回NULL或者错误
static void *my_seq_ops_next(struct seq_file *m, void *v, loff_t *pos)
{
struct task_struct *task = NULL;

if(seqfile_debug_mode == 0) {
task = next_task((struct task_struct *)v);

// 这里加不加好像都没有作用
++ *pos;

// 返回NULL, 遍历结束
if(task == &init_task) {
return NULL;
}
} else {
++ *pos;
}

return task;
}

// 遍历完成/出错时seq_file会调用stop()函数
static void my_seq_ops_stop(struct seq_file *m, void *v)
{

}

// 此函数将数据写入`seq_file`内部的缓冲区
// `seq_file`会在合适的时候把缓冲区的数据拷贝到应用层
// 参数@V是start/next函数的返回值
static int my_seq_ops_show(struct seq_file *m, void *v)
{
struct task_struct * task = NULL;
struct task_struct * p = NULL;
struct file *file = m->private;

if(seqfile_debug_mode == 0) {
seq_puts(m, " file=");
seq_file_path(m, file, "\n");
seq_putc(m, ' ');

task = (struct task_struct *)v;
seq_printf(m, "PID=%u, task: %s, index=%lld, read_pos=%lld\n", task->tgid, task->comm, m->index, m->read_pos);
} else if(seqfile_debug_mode == 1) {
struct task_struct *g, *p;
static int oldcount = 0;
static int entercount = 0;
char *str;

printk("%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);
seq_printf(m, "%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);

rcu_read_lock();
for_each_process_thread(g, p) {
struct task_struct *session = pid_task(task_session(g), PIDTYPE_PID);
struct task_struct *thread = pid_task(task_session(p), PIDTYPE_PID);

if(list_empty(&p->tasks)) {
str = "empty";
} else {
str = "not empty";
}
seq_printf(m, "process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. tasks->prev = %p, tasks->next = %p, p->tasks=%p, %s, process parent %s(%d), thread parent%s(%d)",
g->comm, task_pid_nr(g), task_cpu(g), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(g), get_nr_threads(p), p->tasks.prev, p->tasks.next, &p->tasks, str, g->real_parent->comm, \
task_pid_nr(g->real_parent), p->real_parent->comm, task_pid_nr(p->real_parent));
if(thread)
{
seq_printf(m, "thread session %s(%d).", thread->comm, task_pid_nr(thread));
}
if(session)
{
seq_printf(m, "process session %s(%d).\n", session->comm, task_pid_nr(session));
}
else
seq_printf(m, "\n");

if(oldcount == 0 || oldcount != m->size) {
printk("%s line %d, m->count %ld, m->size %ld.\n", __func__, __LINE__, m->count, m->size);
oldcount = m->size;
}
}
rcu_read_unlock();
} else if(seqfile_debug_mode == 2) {
for_each_process(task) {
struct pid *pgrp = task_pgrp(task);

seq_printf(m, "Group Header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_PGID, p);
}
} else if (seqfile_debug_mode == 3) {
for_each_process(task) {
struct pid *session = task_session(task);

seq_printf(m, "session header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(session, PIDTYPE_SID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_SID, p);
}
} else if(seqfile_debug_mode == 4) {
struct task_struct *thread, *child;
for_each_process(task) {
seq_printf(m, "process %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
for_each_thread(task, thread) {
list_for_each_entry(child, &thread->children, sibling) {
seq_printf(m, " thread %s(%d,cpu%d) child %s(%d,cpu%d),threadnum %d, %d.\n",
thread->comm, task_pid_nr(thread), task_cpu(thread), \
child->comm, task_pid_nr(child), task_cpu(child), \
get_nr_threads(thread), get_nr_threads(child));
}
}
}
} else if(seqfile_debug_mode == 5) {
struct task_struct *g, *t;
do_each_thread (g, t) {
seq_printf(m, "Process %s(%d cpu%d), thread %s(%d cpu%d), threadnum %d.\n", g->comm, task_pid_nr(g), task_cpu(g), t->comm, task_pid_nr(t), task_cpu(t), get_nr_threads(g));
} while_each_thread (g, t);
} else if(seqfile_debug_mode == 6) {
for_each_process(task) {
struct pid *pid = task_pid(task);

seq_printf(m, "Process %s(%d,cpu%d) pid %d, tgid %d:\n", task->comm, task_pid_nr(task), task_cpu(task), task_pid_vnr(task), task_tgid_vnr(task));
do_each_pid_task(pid, PIDTYPE_TGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. pid %d, tgid %d\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), task_pid_vnr(p), task_tgid_vnr(p));
} while_each_pid_task(pid, PIDTYPE_TGID, p);
}
} else if(seqfile_debug_mode == 7) {
for_each_process(task) {
struct pid *pid = task_pid(task);

seq_printf(m, "Process %s(%d,cpu%d) pid %d, tgid %d:\n", task->comm, task_pid_nr(task), task_cpu(task), task_pid_vnr(task), task_tgid_vnr(task));
do_each_pid_task(pid, PIDTYPE_PID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. pid %d, tgid %d\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), task_pid_vnr(p), task_tgid_vnr(p));
} while_each_pid_task(pid, PIDTYPE_PID, p);
}
} else {
printk("%s line %d,cant be here, seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
}

return 0;
}

static const struct seq_operations my_seq_ops = {
.start = my_seq_ops_start,
.next = my_seq_ops_next,
.stop = my_seq_ops_stop,
.show = my_seq_ops_show,
};

static int proc_seq_open(struct inode *inode, struct file *file)
{
int ret;
struct seq_file *m;

ret = seq_open(file, &my_seq_ops);
if(!ret) {
m = file->private_data;
m->private = file;
}

return ret;
}

static ssize_t proc_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos)
{
char debug_string[16];
int debug_no;

memset(debug_string, 0x00, sizeof(debug_string));
if (count >= sizeof(debug_string)) {
printk("%s line %d, fata error, write count exceed max buffer size.\n", __func__, __LINE__);
return -EINVAL;
}

if (copy_from_user(debug_string, buffer, count)) {
printk("%s line %d, fata error, copy from user failure.\n", __func__, __LINE__);
return -EFAULT;
}

if (sscanf(debug_string, "%d", &debug_no) <= 0) {
printk("%s line %d, fata error, read debugno failure.\n", __func__, __LINE__);
return -EFAULT;
}

seqfile_debug_mode = debug_no;

//printk("%s line %d, debug_no %d.\n", __func__, __LINE__, debug_no);

return count;
}

static ssize_t proc_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
ssize_t ret;

printk("%s line %d enter, fuck size %lld size %ld.\n", __func__, __LINE__, *ppos, size);

ret = seq_read(file, buf, size, ppos);

printk("%s line %d exit, fuck size %lld size %ld,ret = %ld.\n", __func__, __LINE__, *ppos, size, ret);

return ret;
}

static struct file_operations seq_proc_ops = {
.owner = THIS_MODULE,
.open = proc_seq_open,
.release = seq_release,
.read = proc_seq_read,
.write = proc_seq_write,
.llseek = seq_lseek,
.unlocked_ioctl = NULL,
};


static struct proc_dir_entry * entry;
static int proc_hook_init(void)
{
printk("%s line %d, init. seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
entry = proc_create("dumptask", 0644, NULL, &seq_proc_ops);
//entry = proc_create_seq("dumptask", 0644, NULL, &my_seq_ops);
return 0;
}

static void proc_hook_exit(void)
{
proc_remove(entry);

printk("%s line %d, exit.\n", __func__, __LINE__);

return;
}

module_init(proc_hook_init);
module_exit(proc_hook_exit);
{
printk("%s line %d, init. seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
entry = proc_create("dumptask", 0644, NULL, &seq_proc_ops);
//entry = proc_create_seq("dumptask", 0644, NULL, &my_seq_ops);
return 0;
}

static void proc_hook_exit(void)
{
proc_remove(entry);

printk("%s line %d, exit.\n", __func__, __LINE__);

return;
}

module_init(proc_hook_init);
module_exit(proc_hook_exit);

Linux内核进程,线程,进程组,会话组织模型_服务器_31

End!

举报

相关推荐

0 条评论