本文的基础知识:由于前半部分内容是转的,且不知道原文出处,没法给出原文地址,大家自行百度

const的实现机制

const究竟是如何实现的呢?对于声明为const的内置类型,例如int,short,long等等,编译器会如何实现const的本意?那么对于非内置类型是否也是与内置数据类型一样处理呢,例如对于结构体类型则会怎样处理呢?下面通过几个小例子来说明这些问题:
C语言const示例:

const int i=10;
int *p=(int *)(&i);
*p=20;
printf("i=%d *p=%d \n",i,*p);

猜一猜输出结果是什么? i=20 *p=20
C++语言const示例1:

const int i=10;
int *p=const_cast<int *>(&i);
*p=20;

cout<<"i="<<i<<"*p="<<*p<<endl;

输出结果是 i=10 *p=20
C++语言const示例2:

struct test{
int j;
char tmp;
test()
{
j=30;
tmp='a';
}
};
int main(int argc, char* argv[])
{
const struct test t1;
int *q=(int *)(&t1.j);
*q=40;
cout<<"j="<<t1.j<<"*q="<<*q<<endl;
return 0;
}

输出结果是 j=40 *q=40

示例结果分析
看到上面三组输出结果,我们可以分析两个问题:

问题1:C语言和C++语言中的const究竟表示什么?

问题2:const的实现机制究竟是怎样的?

问题1,对于const int类型的变量i,C语言中通过指针p修改了值后,i变成了20;而在C++中,通过指针p修改了值后,i仍然是10。
问题2,C++语言中 const struct test的元素j通过指针q被改变了,为何const int 与 const struct test的反应机制不同?

针对问题1,我们知道C语言中const表示只读的变量,既然把const看成是变量,那么其在内存中就会有存储他的空间,并且可以通过指针间接的改变该内存空间的值,当通过指针p改变该内存中的值后,再获取i的值的时候,会访问该空间,得到的是被改变后的值。而C++把const看做常量,编译器会使用常数直接替换掉对i的引用,例如cout<<i; 会理解成cout<<10; 并不会去访问i的内存地址去取数据,这里有点像是C语言里的宏#define i 10。因此C++里i会输出10,而*p会输出20.

针对问题2,C++语言中只是对于内置数据类型做常数替换,而对于像结构体这样的非内置数据类型则不会。因为结构体类型不是内置数据类型,编译器不知道如何直接替换,因此必须要访问内存去取数据,而访问内存去取数据必然会取到被指针q改变后的值,因此会造成与C++中const int类型完全不一样的处理模式。

-------------------------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------------------

有了这个背景知识,我们来看一下如何替换一个内核函数:

const struct file_operations xfs_file_operations = {
    .llseek     = xfs_file_llseek,
    .read       = do_sync_read,
    .write      = do_sync_write,
    .aio_read   = xfs_file_aio_read,
    .aio_write  = xfs_file_aio_write,
    .splice_read    = xfs_file_splice_read,
    .splice_write   = xfs_file_splice_write,
    .unlocked_ioctl = xfs_file_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = xfs_file_compat_ioctl,
#endif
    .mmap       = xfs_file_mmap,
    .open       = xfs_file_open,
    .release    = xfs_file_release,
    .fsync      = xfs_file_fsync,
    .fallocate  = xfs_file_fallocate,
};
 
p_xfs_file_operations =(struct file_operations*)kallsyms_lookup_name("xfs_file_operations");
pte_xfs_splice = lookup_address((unsigned long)p_xfs_file_operations, &level);
// change PTE to allow writing
set_pte_atomic(pte_xfs_splice, pte_mkwrite(*pte_xfs_splice)); orig_xfs_file_splice_read = p_xfs_file_operations->splice_read;
p_xfs_file_operations->splice_read = caq_xfs_file_splice_read;
set_pte_atomic(pte_xfs_splice, pte_clear_flags(*pte_xfs_splice, _PAGE_RW));
该变量是一个const变量,假设我们要替换splice_read    成员,如果直接修改,肯定不行,转成指针,就ok了,这样,我就把splice_read  成功地改为了自己的函数。

如果如果没有 set_pte_atomic(pte_xfs_splice, pte_mkwrite(*pte_xfs_splice)); 这行代码会怎么样?

答案是,如果当时这个页没有写权限,肯定会出crash,如果有的话,这行代码就没用,但是

set_pte_atomic(pte_xfs_splice, pte_clear_flags(*pte_xfs_splice, _PAGE_RW));

  这行代码就会有问题,因为后面可能有人去写这个页的时候就会crash。出现crash,打印一般如下:

<1>[51178.495137] BUG: unable to handle kernel paging request at ffffffffa0671ab8
<1>[51178.495150] IP: [<ffffffffa06cb312>] replace_sendfile+0x2c2/0x300 [newsendfile]
<4>[51178.495163] PGD 1a0b067 PUD 1a0f063 PMD 391bf00067 PTE 800000391bea7161
<0>[51178.495173] Oops: 0003 [#1] SMP
<4>[51178.495179] CPU 0
<4>[51178.495181] Modules linked in: newsendfile(EN+) datalink(EN) xfs w83627dhg(EN) tipc(EX) ossmod(EN) witdriver(EN) bonding ip6table_filter ip6_tables iptable_filter ip_tables ebtable_nat ebtables x_tables af_packet ipmi_devintf ipmi_si ipmi_msghandler edd cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq mperf fuse loop dm_mod vhost_net macvtap macvlan ipv6 ipv6_lib tun kvm_intel kvm pcspkr ses enclosure usbhid hid i40e(EX) sg igb i2c_i801 iTCO_wdt iTCO_vendor_support dca mei mptctl ptp mptbase pps_core rtc_cmos acpi_power_meter container button ext3 jbd mbcache ttm drm_kms_helper drm i2c_algo_bit sysimgblt sysfillrect i2c_core syscopyarea ehci_hcd usbcore usb_common sd_mod crc_t10dif processor thermal_sys hwmon scsi_dh_hp_sw scsi_dh_alua scsi_dh_rdac scsi_dh_emc scsi_dh mpt3sas(EX) configfs scsi_transport_sas raid_class scsi_mod
<4>[51178.495291] Supported: No, Unsupported modules are loaded
<4>[51178.495295]
<4>[51178.495300] Pid: 24329, comm: insmod Tainted: G ENX 3.0.101-0.47.90-default #1 ZTE Grantley/S1008
<4>[51178.495309] RIP: 0010:[<ffffffffa06cb312>] [<ffffffffa06cb312>] replace_sendfile+0x2c2/0x300 [newsendfile]
<4>[51178.495321] RSP: 0018:ffff88190f2b7ee8 EFLAGS: 00010286
<4>[51178.495325] RAX: ffffffffa0661b10 RBX: ffffffff81bd5160 RCX: ffff880001a0bff0
<4>[51178.495330] RDX: ffffffffa0671a00 RSI: ffffffffa06cf4d4 RDI: ffffffffa06cd150
<4>[51178.495335] RBP: ffffffffa069e880 R08: 000000391bf00000 R09: ffff880000000000
<4>[51178.495340] R10: 00000000000926bc R11: 00000000ffffffff R12: ffffffffa014c000
<4>[51178.495345] R13: 0000000000000000 R14: 00007ffc45684787 R15: 00007fcc25041010
<4>[51178.495351] FS: 00007fcc25104700(0000) GS:ffff88207fc00000(0000) knlGS:0000000000000000
<4>[51178.495357] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[51178.495361] CR2: ffffffffa0671ab8 CR3: 0000001f177bc000 CR4: 00000000001407f0
<4>[51178.495366] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
<4>[51178.495371] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
<4>[51178.495377] Process insmod (pid: 24329, threadinfo ffff88190f2b6000, task ffff881d217ac540)
<0>[51178.495381] Stack:
<4>[51178.495384] 0000000000000001 0000000000002db3 ffffffff81da1ac0 0000000000000000
<4>[51178.495396] 00000000000720e1 ffffffffa014c29a 00000000000720e1 ffffffffa06cf1a0
<4>[51178.495405] 00000000000720e1 ffffffff810001cb 00007fcc25041010 ffffffffa06cf1a0
<0>[51178.495414] Call Trace:
<4>[51178.495444] [<ffffffffa014c29a>] newsendfile_init+0x29a/0x1000 [newsendfile]
<4>[51178.495458] [<ffffffff810001cb>] do_one_initcall+0x3b/0x180
<4>[51178.495471] [<ffffffff810a303f>] sys_init_module+0xcf/0x240
<4>[51178.495482] [<ffffffff8146f5f2>] system_call_fastpath+0x16/0x1b
<4>[51178.495495] [<00007fcc24c4ad7a>] 0x7fcc24c4ad79

  根据crash文件,我们获取以下对应函数的起始地址:

objdump -D /home/caq/newsendfile.ko |grep replace_sendfile |head -10
0000000000000050 <replace_sendfile>:
64: e8 00 00 00 00 callq 69 <replace_sendfile+0x19>
70: e8 00 00 00 00 callq 75 <replace_sendfile+0x25>
7c: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # 83 <replace_sendfile+0x33>
83: e8 00 00 00 00 callq 88 <replace_sendfile+0x38>
8f: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # 96 <replace_sendfile+0x46>
96: e8 00 00 00 00 callq 9b <replace_sendfile+0x4b>
a2: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # a9 <replace_sendfile+0x59>
a9: e8 00 00 00 00 callq ae <replace_sendfile+0x5e>
b5: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # bc <replace_sendfile+0x6c>

  (0x50+0x2c2)=0x312,

addr2line -e /home/caq/newsendfile.ko 0x312
/home/caq/newsendfile.c:3007
3001         p_xfs_file_operations                 =(struct file_operations*)kallsyms_lookup_name("xfs_file_operations");
3002 pte_xfs_splice = lookup_address((unsigned long)p_xfs_file_operations, &level);
3003 // change PTE to allow writing
3004 //set_pte_atomic(pte_xfs_splice, pte_mkwrite(*pte_xfs_splice));
3005
3006 orig_xfs_file_splice_read = p_xfs_file_operations->splice_read;
3007 p_xfs_file_operations->splice_read = caq_xfs_file_splice_read;

而对应的行号3007的就是:   p_xfs_file_operations->splice_read = caq_xfs_file_splice_read;没有写权限的时候,去写这个page,就出crash了。可以明显看到,3006行是去读这个page,没事。

linux内核中的const成员是否可以修改?的更多相关文章

  1. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  2. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  3. Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  4. Linux内核中流量控制

    linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...

  5. 【转】 Linux内核中读写文件数据的方法--不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8193954 Linux内核中读写文件数据的方法  有时候需要在Linuxkernel--大 ...

  6. Linux内核中的算法和数据结构

    算法和数据结构纷繁复杂,但是对于Linux Kernel开发人员来说重点了解Linux内核中使用到的算法和数据结构很有必要. 在一个国外问答平台stackexchange.com的Theoretica ...

  7. Linux内核中常用的数据结构和算法(转)

    知乎链接:https://zhuanlan.zhihu.com/p/58087261 Linux内核代码中广泛使用了数据结构和算法,其中最常用的两个是链表和红黑树. 链表 Linux内核代码大量使用了 ...

  8. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  9. Linux内核中的软中断、tasklet和工作队列具体解释

    [TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...

随机推荐

  1. [UE4]蓝图:重写父类时调用父类方法

    右键重写的方法选择“Add call to parent function” 一定要善用这个功能,实现原有父类功能的同时实现子类特别的功能.

  2. [UE4]不精准射击 Random Unit Vector in Cone in Radians

  3. [UE4]蓝图替换节点、引用快捷方式

  4. vue 父组件主动获取子组件的数据和方法 子组件主动获取父组件的数据和方法

    Header.vue <template> <div> <h2>我是头部组件</h2> <button @click="getParen ...

  5. vue vue-resource 请求数据

    main.js import Vue from 'vue'; import App from './App.vue'; /*使用vue-resource请求数据的步骤 1.需要安装vue-resour ...

  6. UI 性能因素考虑

    浏览器的最大并发连接数一般在4到6之间,首先了解影响加载的性能因素: (1)下载的文件太大 (2)发出的请求太多 (3)请求相应不及时 针对这些因素,一般会考虑减少请求次数: (1)对静态文件设置缓存 ...

  7. WebApp专家评委打分的两种进入模式

    A模式: 当前PC端的前期设置如下: [管理员允许时,只针对管理员指定选手] 选项选中.在现场时,管理员点击 状态未知 或下方红框所示按钮 发出打分允许指令时, 专家评委使用WebApp进入专家打分区 ...

  8. xinetd网络

    简单Web服务器 基本的HTTP协议 请求服务器数据 GET /文件或目录 HTTP/1.1 协议头部分(可选) /r/n(协议头结束) 服务器应答浏览器 HTTP/1.1 200 OK conten ...

  9. Thinkphp3.2+PHPQRCode 二维码生成示例

    下载phpqrcode 整合到Thinkphp框架 在“ThinkPHP\Library\Vendor\”下新建目录phpqrcode,将压缩包内容解压到该文件夹下. 下载地址:http://www. ...

  10. window系统更新导致很多服务出错

    window7,win10,window server各版本系统中,经常会出现下载完成更新补丁后要求重启更新,此时很可能会出现很多服务失效的莫名其妙的问题,比如数据库连不上,IIS某功能不好使等等问题 ...