linux 内核页表替换

深入Linux内核(内存篇)—页表映射分页 深入Linux内核(内存篇)—页表映射一、分页二、X86中的分页三、ARM中的分页四、Kernel中的分页 操作系统的核心任务是对系统资源的管理,而重中之重的是对CPU和内存的管理。为了使进程摆脱系统内存的制约,用户进程运行在虚拟内存之上,每个用户进程都拥有完整的虚拟地址空间,互不干涉。 而实现虚拟内存的关键就在于建立虚拟地址(Virtual Address,VA)与物理地址(Physical Address,PA)之间的关系,因为无论如何数据终究要存储到物理内存中才能被记录下来。 如下图所示,进程1和进程 阅读详情

linux 内核页表替换

一.linux本身关于页表查找机制的代码实现

1. 逻辑地址到页表的查找过程
static int __follow_pte_pmd(struct mm_struct *mm, unsigned long address,
                            unsigned long *start, unsigned long *end,
                            pte_t **ptepp, pmd_t **pmdpp, spinlock_t **ptlp)
{
        pgd_t *pgd;
        p4d_t *p4d;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *ptep;

	/* 进程的Page Global Directory基指针存放在mm->pgd */
        pgd = pgd_offset(mm, address);	  /* 返回address指向该进程的Page Global Directory指针 */
        if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
                goto out;

        p4d = p4d_offset(pgd, address);	  /* 在4级页面机制中,不做任何操作,直接返回pgd */
        if (p4d_none(*p4d) || unlikely(p4d_bad(*p4d)))
                goto out;

        pud = pud_offset(p4d, address);	  /* 返回address指向该进程的Page Upper Directory指针 */
        if (pud_none(*pud) || unlikely(pud_bad(*pud)))
                goto out;

        pmd = pmd_offset(pud, address);	  /* 返回address指向该进程的Page Middle Derectory指针 */
        /* ... */
        if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
                goto out;

        if (start && end) {
                *start = address & PAGE_MASK;
                *end = *start + PAGE_SIZE;
                mmu_notifier_invalidate_range_start(mm, *start, *end);
        }
        ptep = pte_offset_map_lock(mm, pmd, address, ptlp);   /* 返回address指向该进程的Page Table指针 */
	/* 判断Page Table是否保留在物理内存之中 */
        if (!pte_present(*ptep))
                goto unlock;
        *ptepp = ptep;
        *ptepp = ptep;
        return 0;
unlock:
        pte_unmap_unlock(ptep, *ptlp);
        if (start && end)
                mmu_notifier_invalidate_range_end(mm, *start, *end);
out:
        return -EINVAL;
}

2.Page Table转为物理地址

int follow_phys(struct vm_area_struct *vma,
                unsigned long address, unsigned int flags,
                unsigned long *prot, resource_size_t *phys)
{
        /* ... */
	if (follow_pte(vma->vm_mm, address, &ptep, &ptl))
                goto out;
        pte = *ptep;
	/* ... */
        *phys = (resource_size_t)pte_pfn(pte) << PAGE_SHIFT;	/* Page Table转化为物理地址 */

        ret = 0;
unlock:
        pte_unmap_unlock(ptep, ptl);
out:
        return ret;
}

关于分页机制的4级还是几级查找不是我关注的,我的目标是在
在这里插入图片描述
Page_Table到Page物理地址查找的一个替换。

phys = (resource_size_t)pte_pfn(pte) << PAGE_SHIFT; / Page Table转化为物理地址 */
这句即是Page_Table到Page的查找过程。那么其中必然就会涉及到表的查询,我们当然也可以在类似的实现中自己构造新的页表跳转。

3.页表到物理地址的转换

linux-4.14.146/arch/x86/include/asm/pgtable.h:static inline unsigned long pte_pfn(pte_t pte)

static inline unsigned long pte_pfn(pte_t pte)
{
	phys_addr_t pfn = pte_val(pte);     //宏用来把pgd_t类型转换成无符号长整型
	pfn ^= protnone_mask(pfn);
	return (pfn & PTE_PFN_MASK) >> PAGE_SHIFT;
}

/* Get a mask to xor with the page table entry to get the correct pfn. */
static inline u64 protnone_mask(u64 val)
{
	return __pte_needs_invert(val) ?  ~0ull : 0;
}

static inline bool __pte_needs_invert(u64 val)
{
	return val && !(val & _PAGE_PRESENT);
}

#define _PAGE_PRESENT	(_AT(pteval_t, 1) << _PAGE_BIT_PRESENT)

#define _PAGE_BIT_PRESENT	0	/* is present */

#ifdef __ASSEMBLY__
#define _AC(X,Y)	X
#define _AT(T,X)	X
#else
#define __AC(X,Y)	(X##Y)
#define _AC(X,Y)	__AC(X,Y)
#define _AT(T,X)	((T)(X))
#endif

#define PAGE_SHIFT		12


#define PTE_PFN_MASK		((pteval_t)PHYSICAL_PAGE_MASK)
#define PHYSICAL_PAGE_MASK	(((signed long)PAGE_MASK) & __PHYSICAL_MASK)

#ifdef CONFIG_DYNAMIC_PHYSICAL_MASK
extern phys_addr_t physical_mask;
#define __PHYSICAL_MASK		physical_mask
#else
#define __PHYSICAL_MASK		((phys_addr_t)((1ULL << __PHYSICAL_MASK_SHIFT) - 1))
#endif

phys_addr_t physical_mask = (1ULL << __PHYSICAL_MASK_SHIFT) - 1;

#ifdef CONFIG_DYNAMIC_PHYSICAL_MASK
extern phys_addr_t physical_mask;
#define __PHYSICAL_MASK		physical_mask
#else
#define __PHYSICAL_MASK		((phys_addr_t)((1ULL << __PHYSICAL_MASK_SHIFT) - 1))
#endif

#define __PHYSICAL_MASK_SHIFT	52

typedef u64 phys_addr_t;

二.重新构建查表过程


int follow_phys(struct vm_area_struct *vma,
		unsigned long address, unsigned int flags,
		unsigned long *prot, resource_size_t *phys)
{
	int ret = -EINVAL;
	pte_t *ptep, pte;
	spinlock_t *ptl;

	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
		goto out;

	//if (follow_pte(vma->vm_mm, address, &ptep, &ptl))
	//	goto out;

	if (follow_pte_pmd(vma->vm_mm,address, NULL, NULL,
						    &ptep, NULL, &ptl))//page find
		goto out;
	pte = *ptep;

	if ((flags & FOLL_WRITE) && !pte_write(pte))
		goto unlock;

	*prot = pgprot_val(pte_pgprot(pte));
	*phys = (resource_size_t)pte_pfn(pte) << PAGE_SHIFT;

	ret = 0;
unlock:
	pte_unmap_unlock(ptep, ptl);
out:
	return ret;
}

这一段大部分是内核中的代码,但有一点follow_pte没有导出,但是导出了follow_pte_pmd。这两个函数在源码里的实现是一样的。所以完全可以替换。

另一个关键是获取的struct vm_area_struct
如下:

struct vm_area_struct* get_vma(unsigned long addr){
	struct task_struct *cur_task =current;//get_current();

	struct vm_area_struct *cur_vma =  find_vma(cur_task->mm_strcut,addr);
	if (!vma || vma->vm_start > start)
	{
		printk("\n");
		return -EINVAL;
	}
	printk("\n");
	return cur_vma;
}

那么理论上我们通过页表查询的方式获取到了物理地址

	resource_size_t phys;
	resource_size_t phys_addr;
	unsigned long long* target = kmalloc(0x200, GFP_KERNEL);
	unsigned long long phytarget = virt_to_phys(target);
	__u64 phytargets = phytarget;


	struct vm_area_struct * cur_vma = find_vma(target);

	int ret = follow_phys(cur_vma,target,FOLL_WRITE, &prot, &phys);
	if(ret){
		printk("find physic error\n");
	}

这里有个FOLL_WRITE位,内核中定义如下:

#define FOLL_WRITE	0x01	/* check pte is writable */
#define FOLL_TOUCH	0x02	/* mark page accessed */
#define FOLL_GET	0x04	/* do get_page on page */
#define FOLL_DUMP	0x08	/* give error on hole if it would be zero */
#define FOLL_FORCE	0x10	/* get_user_pages read/write w/o permission */
#define FOLL_NOWAIT	0x20	/* if a disk transfer is needed, start the IO
				 * and return without waiting upon it */
#define FOLL_POPULATE	0x40	/* fault in page */
#define FOLL_SPLIT	0x80	/* don't return transhuge pages, split them */
#define FOLL_HWPOISON	0x100	/* check page is hwpoisoned */
#define FOLL_NUMA	0x200	/* force NUMA hinting page fault */
#define FOLL_MIGRATION	0x400	/* wait for page to replace migration entry */
#define FOLL_TRIED	0x800	/* a retry, previous pass started an IO */
#define FOLL_MLOCK	0x1000	/* lock present pages */

可见这里应该是可以更改的。

三.内存描述属性

内核中的地址块因为不是连续的,所以存在多个vma数据块。而且这些块也是通过红黑树来组织的。所以理论上vma----mm-----pte_t的查询路径应该是可行的,但是我在4.14.146版本上进行测试的时候。kmalloc的虚拟地址无法找到对应的vma。find_vma总是NULL。

这里有点不太懂。

好在找到了另一种方式。

 unsigned long init_mm_addr = kallsyms_lookup_name("init_mm");
        struct mm_struct *my_mm;
        my_mm = (struct mm_struct *)init_mm_addr;

		printk(KERN_NOTICE "init_mm_addr %p\n",init_mm_addr);


        pgd = pgd_offset(my_mm, address);
        if (pgd_none(*pgd) || pgd_bad(*pgd))
            goto out;

其他的页表查询方式跟之前的一样。

四.修改页表属性

经过多次尝试,终于在内核中以模块的方式实现了查询页表到物理地址的整个过程,这个过程的意义在于,对于页表属性的更改和页表替换必须依赖以上条件。
arm 64 上有两个函数set_pte_bit,clear_pte_bit。从名字看,我们对页表描述符的修改可以依赖以上两个函数。但是好像x86上没有这两个函数,不过不要紧,我们可以自己实现。

static inline pte_t set_pte_bit(pte_t pte, pgprot_t prot)
{
	pte_val(pte) |= pgprot_val(prot);
	return pte;
}

#define pgprot_val(x)	((x).pgprot)

typedef struct pgprot { pgprotval_t pgprot; } pgprot_t;

typedef unsigned long	pgprotval_t;

#define pte_val(x)	native_pte_val(x)
#define __pte(x)	native_make_pte(x)

static inline pteval_t native_pte_val(pte_t pte)
{
	return pte.pte;
}

typedef struct { pteval_t pte; } pte_t;

typedef unsigned long	pteval_t;
#define pte_pgprot(x) __pgprot(pte_flags(x))

static inline pteval_t pte_flags(pte_t pte)
{
	return native_pte_val(pte) & PTE_FLAGS_MASK;
}

#define __pgprot(x)	((pgprot_t) { (x) } )

static inline pteval_t native_pte_val(pte_t pte)
{
	return pte.pte;
}

五、页表替换

这里走了点弯路,因为kmalloc分配的都是巨页,所以开始的时候老是想怎么能分配一个标准页来着。
问了下总管,说是要看下分配器的内容,开始没找到,后来发现了个直接的调用,用来分配一个物理页。
vpage = get_zeroed_page(GFP_ATOMIC);

当时还有第二种做法,就是全局变量默认都是标准页。

不过想着全局变量不太灵活,所以还是直接分配吧。

我这里以ptep为代表的说明。
这个指针代表页表描述符。主要包含了两个信息。

  • 页帧号(经过位移转换可以得到对应物理页的的物理地址)
  • 页属性(用来控制页的操控属性,读写执行等。)

所以我这里写了两个函数
cp_page(unsigned long * target,unsigned long address)
– lookup_address()
ch_physic(unsigned long destaddr,unsigned long srcaddr)
– lookup_address()
–lookup_address()
这里进行了三次虚拟地址查表的过程,理论上可以减少到两个,因为如果考虑到效率问题。不过替换操作发生不是很频繁,应该可以忽略,暂且这样。

#define PTE_PHY_PAGE  0x0000fffffffff000 
#define PTE_PHY_PAGE_CLEAR 0xffff000000000fff 

		pte=*destptep;
		printk("ch pte %016lx \n",pte );
		pte.pte = pte.pte & PTE_PHY_PAGE_CLEAR;
		printk("ch pte clear phypage %016lx \n",pte );

		pten=*srcptep;
		printk("ch pten %016lx \n",pten);
		pten.pte = pten.pte & PTE_PHY_PAGE;
		printk("ch pten phyget %016lx \n",pten);

		pte.pte = pte.pte + pten.pte ;
		printk("ch pte result %016lx \n",pte );

		destptep->pte=pte.pte;

替换过程如上。

全球独家视频:特工队 - 动态修改计算机系统调用 我们最近录制一期特工队节目,全球独家的视频节目,全程2.5小时,这个视频节目是围绕这样一个实验来进行的。我们在这期视频节目中重点介绍: ARM64体系结构中的内存管理 ARM64的页表管理 ARM64 的Linux内核如何walk through页表 ARM64的Linux内核系统调用是如何实现的? 如何面对宕机?本实验讲会面临4次不同的宕机。 详细分析宕机的log 利用kdump来手撕宕机问题 笨叔保证,在全球的视频网站和培训机构上 阅读详情

相关推荐

direct IO 内核实现分析及揭示一个坑——基于3.10.0-693.11.1

linux的读写系统调用提供了一个O_DIRECT标记,可以让尝试绕过缓存,直接对磁盘进行读写(为啥是尝试绕过?当直接落盘失败时还要通过缓存去落盘)。为了能实现直接落盘,使用direct IO限制多多,文件偏移得对齐到磁盘block,内存地址得对齐到磁盘block,读写size也得对齐到磁盘block。但direct IO的实现还是有个小缺陷。这个缺陷我在fuse上的分析已经讲清楚了,对于缺陷原理...

Liaosp的博客 3233

DirtyCow Linux权限提升漏洞分析

DirtyCow Linux权限提升漏洞分析 如上图,这个漏洞的特点就是线程竞争导致可以读写的权限被扩大了。其实具体说可能有点绕,就是foll_write标志在row特性执行后去掉了,但是再次执行时却没有检查页表项的有效性,导致我们可以利用另一个线程清空页表。清空的页表由于没有了foll_write权限要求,再次被分配时就不执行row执行机制,导致原本不应该本写的地址拥有了写的权限。 ...

inquisiter 2254

重大软院操作系统实验四:虚拟内存深入探索

虚拟内存是现代计算机系统中一种重要的资源管理技术,允许程序运行时可以使用比实际物理内存更多的地址空间。它通过将部分程序代码和数据暂时存储在硬盘上,以缓解物理内存空间的限制,从而提高多任务处理能力。Linux内核内存管理子系统负责所有内存相关活动,如内存分配、回收、映射和保护。内核采用页式虚拟内存管理,将物理内存划分为固定大小的页,进程的虚拟地址空间也相应地划分为页,并通过页表映射到物理页。Linux内核的虚拟内存管理是现代操作系统不可或缺的一部分。

weixin_31720909的博客 725

linux内核读取物理地址,如何从Linux内核模块中的逻辑地址获取物理地址?

Ilya Matveyc..6好吧,它可能看起来像那样(从虚拟地址跟随PTE):void follow_pte(struct mm_struct * mm, unsigned long address, pte_t * entry){pgd_t * pgd = pgd_offset(mm, address);printk("follow_pte() for %lx\n", address);ent...

weixin_42500625的博客 731

Linux内核调度与内存管理实战:从习题推演到/proc/sys调试

操作系统中的进程调度、内存管理与I/O调度并非抽象理论,而是可测量、可复现的系统行为。理解CFS调度器的sched_latency_ns周期、页表项映射与mincore缺页统计、blktrace磁盘调度延迟,是定位高延迟服务的根本能力。本文基于经典操作系统习题,将PV操作映射到futex系统调用,把LRU页面替换对应到/proc/PID/pagemap字段,将电梯算法还原为/sys/block/sda/queue/scheduler切换实践。面向Linux运维工程师、内核开发者及性能优化从业者,提供带单位(

weixin_33883104的博客 215

手写Linux 0.11内核:从引导到文件系统的八大实验实战指南

操作系统是计算机系统的核心软件,理解其底层机制对后端开发与系统工程师至关重要。单纯背诵概念难以建立真正的工程认知,而通过阅读真实内核源码并动手改造,能够深入掌握进程管理、内存映射与文件系统等核心原理。Linux 0.11作为一个结构清晰、代码量仅两万行的早期内核,是学习内核机制的理想素材。本文围绕一套经典的实验课程,系统梳理了从环境搭建、系统引导到系统调用、进程切换、信号量实现、调度算法替换、页表映射及文件系统改造的完整路径,并针对编译链兼容性、调试技巧和常见陷阱给出了实用建议。这类贴近真实内核的工程实践,

weixin_33883104的博客 333

linux 内核 物理地址,如何从Linux内核模块中的逻辑地址获取物理地址?

好吧,它可能看起来像那样(从虚拟地址跟随PTE):void follow_pte(struct mm_struct * mm, unsigned long address, pte_t * entry){pgd_t * pgd = pgd_offset(mm, address);printk("follow_pte() for %lx\n", address);entry->pte = 0;...

weixin_42217306的博客 233

附录B:Standard Delay Format(SDF)(上)

本附录将介绍标准延迟标注格式,并说明了如何在仿真中执行反标。 延迟格式描述了设计网表的单元延迟和互连走线延迟,无论设计是用两种主要硬件描述语言(VHDL或Verilog HDL)中的哪一种所描述的。 本章还会介绍仿真的反标(backannotation),STA的反标其实是一个简单直接的过程,其中DUA中的时序弧将由SDF所指定的延迟进行标注。

To the beyond and infinity! 5411

linux 脏牛漏洞,【漏洞分析】11月4日:深入解读脏牛Linux本地提权漏洞(CVE-2016-5195)...

稿费:600RMB(不服你也来投稿啊!)投稿方式:发送邮件至linwei#360.cn,或登陆0x00 概述该漏洞是Linux的一个本地提权漏洞,发现者是Phil Oester,影响>=2.6.22的所有Linux内核版本,修复时间是2016年10月18号。该漏洞的原因是get_user_page内核函数在处理Copy-on-Write(以下使用COW表示)的过程中,可能产出竞态条件造成CO...

weixin_35285401的博客 663

GIS坐标系转换以及空间校正和地理配准

许多小伙伴都对坐标系,投影这类问题迷迷糊糊,我之前也一样,现在总结一下。 坐标系的转换1.为什么要进行坐标系的转换?2. 不同坐标系的介绍1.地理坐标系我国常见的地理坐标(1).北京54坐标系(2).西安80坐标系(3).WGS-84坐标系(4).大地2000坐标系2.投影坐标系我国常用的投影坐标系(1) UTM投影(2) 高斯-克吕格投影(3) 兰伯特投影(4) 阿尔伯斯投影投影坐标系的选择3. 坐标系的转换1.已知坐标系之间的转换1.1 两坐标系的参考椭球相同1.2 两坐标系的参考椭球不一致,但是GIS

m0_47258611的博客 8913

MIPI RFFE 信号完整性与硬件设计

MIPI RFFE信号完整性设计关键点 摘要:MIPI RFFE作为射频前端控制总线,其信号完整性设计需平衡速率、驱动能力与抗干扰性。核心挑战在于确保接收端在52Mb/s速率下保持足够的电压和时序裕量。关键设计考量包括: 驱动能力优化:过强导致过冲/振铃和射频杂散,过弱则恶化时序裕量,需与总线负载匹配。 波形质量指标:严格控制上升/下降时间(兼顾EMC和时序要求),抑制过冲/振铃幅度,管理VIO电源纹波和耦合噪声。

sinat_15150363的博客 239

kvm 监控内存,替换页表(linux版的win VT晶核)(这个整复杂了,不用小内核也可以实现,留着吧,主要记录了bootLoad的启动过程)

kvm 监控内存,替换页表等问题 一、如何利用kvm 监控整个linux系统 # 进入 KVM 代码目录 cd /root/kernel-src/kvm-2.6.32/arch/x86/kvm # 开始编译 make -C /lib/modules/`uname -r`/build M=`pwd` clean make -C /lib/modules/`uname -r`/build M=`pwd` modules # 拷贝编译结果出来,并使用 cp *.ko /root/kvm/tools/.

inquisiter 785

3D打印机控制系统设计:基于MATLAB的3D打印机运动学建模、轨迹规划和控制

控制实现:在控制实现中,需要将轨迹规划的结果转化为实际的控制指令,控制3D打印机按照规划的轨迹进行运动。3D打印机的运动学建模:3D打印机的运动学建模是指将3D打印机的机械结构、传动系统和运动控制部件等抽象成数学模型,以便于进行控制系统的设计和分析。轨迹规划:轨迹规划是指根据3D模型的几何形状和打印机的运动学模型,计算出打印机需要执行的运动轨迹,以便于实现所需的打印效果。控制实现:控制实现是指将轨迹规划的结果转化为控制指令,控制3D打印机按照规划的轨迹进行运动。

走向CTO的路上... 2473

GNU/Linux - Linux kernel memory access

在检查是否有能力写入用户缓冲区(通过 access_ok)后,内部函数 __copy_to_user 将被调用,该函数又会调用 __copy_from_user_inatomic (在 ./linux/arch/x86/include/asm/uaccessXX.h 中,其中 _XX 是 32 或 64,取决于体系结构)。请注意,每个进程都可以将共享内存区域映射到各自地址空间中的不同地址。请注意,如图所示,一个进程可能有一个很大的地址空间,但它是稀疏的,这意味着地址空间的小区域(页)通过页表指向物理内存。

guoqx的专栏 1862

深度解析:C# Modbus TCP协议解析与异常处理(工控上位机必备)

协议解析要精准:掌握MBAP头和PDU的结构,尤其是常用功能码的请求/响应格式,避免因字节处理错误导致通信失败;异常处理要全面:覆盖通信层、协议层、数据层的所有异常类型,针对工业场景制定重试、重连、滤波等策略;资源管理要规范:使用释放资源,采用连接池复用连接,避免端口泄露;工业场景要适配:结合工控现场的特点(网络波动、电磁干扰、设备忙),优化通信效率和数据处理逻辑。通过以上解析和策略,可确保C#上位机与工业设备的Modbus TCP通信具备高稳定性、高可靠性、高容错性,满足工控领域的严苛要求。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 997

【AUTOSAR-E2E】-2.2-配置E2E:基于E2EPW配置一个TX发送的Signal Group(使用E2E Profile 2)

《共13页精讲》:此法也适用于E2E Profile 4/5/6/7/11/22 目录 1 检查CAN Matrix Arxml/DBC是否满足E2E需求 2 在DaVinci Developer中为Port添加E2E保护 3 在DaVinci Configurator settings设置 4 如何使用应用层的E2E保护 5 结尾 <--返回「Autosar_BSW高阶配置」总目录--> <--返回「Autosar_BSW高阶配置」专栏主页--> &lt.

「汽车电子助手」的博客 4219

linux打印调用栈 函数名_Linux内核如何替换内核函数并调用原始函数

替换一个已经在内存中的函数,使得执行流流入我们自己的逻辑,然后再调用原始的函数,这是一个很古老的话题了。比如有个函数叫做funcion,而你希望统计一下调用function的次数,最直接的方法就是 如果有谁调用function的时候,调到下面这个就好了 :void new_function() { count++; return function(); }网上很多文章给出了实现这个思路的Tri...

weixin_39716088的博客 101

微信小程序 - 最新详细实现模板消息订阅通知功能,给用户微信发送“服务通知“模板消息并点击进入跳转到小程序页面路由,订阅消息通知详细后台申请流程及示例代码,解决授权申请弹框被拒绝后无法调起、常见报错!

微信小程序,vue2,vue3,模板消息,订阅通知,服务通知,小程序模板通知,订阅微信模板消息功能,唤起模板消息订阅弹框并发送服务通知,消息订阅与模板消息发送,一次性订阅,长期永久订阅,申请模板id,详细示例代码教程,配置流程,消息模板,模板ID,用户收不到消息,订阅了通知但微信不来服务通知,报错,小程序平台模板消息订阅功能,唤起订阅模板消息弹框,订阅模板消息发送服务通知功能,如何做模板消息订阅,一直收不到通知,用户通过弹窗订阅,订阅消息,无法打开申请授权弹框,用户拒绝/关闭后,如何引导用户再开启,拒绝后怎

🏆 前端领域优质创作者 5329

Linux--进程(进程虚拟地址空间、页表、进程控制、实现简易shell)

进程虚拟地址空间、页表、进程控制、实现简易shell

大学在读,感谢关注 1566

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

第九章 虚拟内存 第七节 案例研究:Intel Core Linux 内存系统

1、Core i7 地址翻译 1. 翻译概览 Core i7采用四级页表层次结构。 每个进程有它自己私有的页表层次结构。 当一个Linux进程在运行时, 虽然Core i7体系结构允许页表换进换出,但是与已分配了的页相关联的页表都是驻留在内存中的。 CR3控制寄存器指向第一级页表 (L1)的起始位置。 CR3的值是每个进程上下文的一部分, 每次上下文切换时, CR3的值都会被恢复。 下图展示了其地址翻译概况。 2. PTE细节 1. 第一二三级页表 P=1时,要求地址字段包含一个40位的物理页号,它指向

刘胖仔学后端 1133

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

内存管理

虚拟内存、内核空间和用户空间 Linux简化了分段机制,使得虚拟地址与线性地址总是一致。在32位平台上,线性地址的大小位4GB,也即是虚拟空间的大小。 Linux内核将这4GB的空间分为两部分。 内核空间: 最高的1GB(0xC0000000–0xFFFFFFFF),供内核使用。 用户空间(地址空间): 较低的3GB(0x00000000–0xBFFFFFFF),供进程使用。 从具体进程的角度来看,每个进程可以拥有4GB的虚拟地址空间,因为每个进程可以通过系统调用进入内核,共享内核空间。 用户空间不是被进程

qq_44712437的博客 291

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Linux 进程:地址空间、页表、进程控制、程序替换

/ 先写一个自定义程序(test.c)i<argc;// 编译:gcc test.c -o test// 主程序(main.c)// 替换成自定义程序exit(1);wait(NULL);return 0;进程地址空间:是虚拟的,每个进程独立,分为内核 / 用户空间,用户空间按代码段 / 数据段 / 堆 / 栈等布局。页表:映射虚拟地址和物理内存,父子进程共享只读页,写时复制(COW)是核心优化。进程控制。

2302_81335080的博客 389

实践二之实验报告

一、基本模块的实现: 1.进程遍历打印输出 2.简单地编写一个新的系统调用(替换空的系统调用号) 基本模块学到的知识点: 1.相关指令   make oldconfig配置内核   make编译内核   make modules_instal编译安装内核模块   make install引导新编译的内核   uname –a查看内核版本   lsmod查看加载的模块...

weixin_30369087的博客 147

【写时复制(精华,从源码分析总结)】

Linux下的fork()函数利用了写时拷贝技术,子进程复制了父进程的页表,进程“读”操作的时候也能访问到同一个数据,那是因为他们映射到的物理内存上都是一块相同的空间。但是当有一个进程要进行“写”操作时,系统会在物理内存中复制出一块内存,然后将数据写进去,进行“写”操作的进程通过虚拟地址访问时就不是访问之前的那个数据,而是访问进行“写”操作后复制过来并写入数据的那块数据----写时拷贝技术:两个进程一开始指向同一块空间,等有进程进行“写”操作时候,再给进行“写”操作的进程重新开辟空间,目的就是提高子进程创建

Orwell_VII的博客 873

Linux进程篇(十一):从底层原理到 exec* 家族:彻底搞懂 Linux 进程程序替换

在 Linux 中,用 fork()创建子进程后,子进程往往会执行与父进程相同的代码(或者是通过 if-else分流执行父进程代码的一部分)。如果我们想让子进程执行一个全新的程序,就需要用到进程程序替换。进程程序替换是通过exec*系列系统调用/库函数实现的。当进程调用这类函数时,该进程的用户空间代码和数据完全被新程序替换,并从新程序的初始化代码开始执行。参数含义path:要执行程序的绝对路径或相对路径(回答了“我要执行谁”)。arg:命令行参数列表。

2501_93641778的博客 332
上一篇: linux 内核双机调试
下一篇: arm64 内核 inline hook
inquisiter
博客等级 码龄10年 374粉丝 228原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值