在进程中打印其他进程的数据

在《打印指定进程中的数据》中实现了,在内核中通过日志打印了指定进程,指定地址的字符串数据。这次实现,在一个进程中,打印另一个进程的字符串数据。

第一个测试程序代码如下:(first.c)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

char* p = "this is first process";
int main()
{
    printf("pid = %d\n", getpid());
    printf("%d - %p: %s\n", p, p, p);
    getchar();
    return 0;
}

第二个测试程序代码如下:(second.c)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

char* p = "this is second process";
int main()
{
    printf("pid = %d\n", getpid());
    printf("%d: %s\n", p, p);
    getchar();

    printf("input a address:\n");
    int a = 0;
    scanf("%d", &a);
    printf("%d - %x\n", a, a);
    a += 4096; // 字符串在本进程中的实际地址
    printf("%d - %x\n", a, a);


    // 打印字符串
    printf("%s\n", (char*)a);
    return 0;
}

要实现在第二个进程中,把第一个进程的字符串“this is first process”打印出来,操作流程是: 

一 启动两个测试程序

第一个程序(后面称first)启动后打印日志如下:

pid = 4642
4195892 - 0x400634: this is first process

第二个程序(后面称second)启动后打印日志如下:

pid = 4643
4196164: this is second process

二 执行内核代码

cat /proc/4642/maps,可以查看first的地址空间,前三项如下:

00400000-00401000 r-xp 00000000 08:05 2135623         /home/wang/...
00600000-00601000 rw-p 00000000 08:05 2135623        /home/wang/...
02564000-02585000 rw-p 00000000 00:00 0                   [heap] 

 第一个空间为数据段的地址空间,字符串位于此空间中,大小为一个页框的大小,即4K。second的地址空间也类似,只有一页大小的数据段空间。

/proc/4642/maps中的每一行,在内核中用vm_area_struct结构表示。结构中的vm_start表示地址空间的开始位置,vm_end表示地址空间的结束位置。

要在第二个进程中打印第一个进程的字符串,需要将字符串拷贝到第二个进程中。因此需要增加第二个进程的地址空间,即找到字符串地址所在的地址空间的vm_area_struct,将vm_end后移。但是增加的内存空间,并没有分配实际的物理内存。

现在看下程序分页情况(两个程序都只有一页的数据段,所以以first为例进行讲解):

字符串的地址为0x400634,此字符串存放在一个页框中,字符串地址的低12位(即0x634)为字符串在该页框中的偏移量。PTE和PMD的大小都是4096,在x86_64想系统中,一个地址占8字节,因此一个PMD或PTE可以存放512个地址。上图PAGE的地址就存放在PTE索引为0的位置上。图中的PTE又位于PMD索引为2的位置上。 

second数据段增加一页的地址空间后,将first字符串所在的页框地址,放在second程序数据段对应PTE索引为1的位置。所以second.c代码中需要对输入的地址加4096.

a += 4096; // 字符串在本进程中的实际地址

以上是内核代码执行的逻辑。 

三 打印字符串

在第二个进程中按“回车”键,输入字符串在第一个进程中的地址(即4195892),按“回车”键。

第二个进程最终的执行结构如下:

pid = 4643
4196164: this is second process

input a address:
4195892
4195892 - 400634
4199988 - 401634
this is first process
 

 上面最后一行,已标红,就是第一个进程中的字符串。

最后附上内核部分代码:

static int hello_open(struct inode* inode, struct file*filep)
{
	printk("test begin\n");

	struct task_struct * tsk;

	int f_pid = 4642; // 第一个进程的pid
	int s_pid = 4643; // 第二个进程的pid

	pte_t* f_pte = NULL;
	pte_t* s_pte = NULL;

	for_each_process(tsk) {
		if (f_pid == tsk->pid || s_pid == tsk->pid)
		{
			// 目标字符串的地址
			unsigned long addr = 4195892;
			struct mm_struct *mm = tsk->mm;

			pgd_t *pgd = pgd_offset(mm, addr);

			// p4d和pgd是一样的?
			p4d_t *p4d = p4d_offset(pgd, addr);

			pud_t *pud = pud_offset(p4d, addr);
			
			pmd_t *pmd = pmd_offset(pud, addr);

			pte_t *pte = pte_offset_kernel(pmd, addr); // 页表的线性地址

			if (f_pid == tsk->pid)
			{
				f_pte = pte;
			}
			else if (s_pid == tsk->pid)
			{
				s_pte = pte;
			}

			struct vm_area_struct *vm_area = find_vma(mm, addr);
			if (NULL != vm_area){
				printk("vm_area_struct vm_start = %d\n", vm_area->vm_start);
				printk("vm_area_struct vm_end = %d\n", vm_area->vm_end);
				printk("vm_area_struct vm_page_prot = %d\n", vm_area->vm_page_prot);
				printk("vm_area_struct vm_flags = %d\n", vm_area->vm_flags);

				if (s_pid == tsk->pid)
				{
					// 增加第二个进程的地址空间
					vm_area->vm_end += 4096;
				}
			}
		}
	}

	if (NULL != f_pte && NULL != s_pte)
	{
        // 将第一个进程字符串所在的页,添加到第二个进程中
		set_pte(&s_pte[1], *f_pte);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值