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

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. Java基础知识_毕向东_Java基础视频教程笔记(26 反射)

    Java反射机制: 是在运行状态中,对于任意一个类(class)文件,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性.这种动态获取的信息以及动态调用对象的方法的功 ...

  2. 第5章 IP地址和子网划分(3)_子网划分

    6.子网划分 6.1 地址浪费 (1)IPv4公网地址资源日益紧张,为减少浪费,使IP地址能够充分利用,就要用到子网划分技术. (2)传统上一个C类地址,如212.2.3.0/24,其可用的地址范围为 ...

  3. SSH2 No Session found for current thread原因

    Hibernate4 与 spring3 集成之后, 如果在取得session 的地方使用了getCurrentSession, 可能会报一个错:“No Session found for curre ...

  4. CSS便捷开发小工具汇总

    1.Prefix free 可以帮助开发者省去编写各种CSS3属性前缀的工作,只需要在页面中引入prefixfree.js即可. 2. Normalize 是一个CSS Reset工具, 相比传统的R ...

  5. 《Linux 性能及调优指南》1.1 Linux进程管理

    https://blog.csdn.net/ljianhui/article/details/46718835 本文为IBM RedBook的Linux Performanceand Tuning G ...

  6. SQL各种语(持续更新)

    --通过分组查询,并查询各个组下面的数据数量 SELECT cord,COUNT(*) AS s FROM View_QualityPolicy GROUP BY cord ORDER BY s DE ...

  7. 硬盘读取不了-->>完美解决

    说明:电脑装了两个硬盘,一个固态一个机械,装完系统之后读取不到机械硬盘的数据,网上很多人都说格式化之后分盘,但是!!!里面的数据咋办?千万别自己倒腾转换格式什么的,一不小心数据真的丢了,那就再也找不回 ...

  8. android 开发 对话框Dialog详解

    转载请注明出处:红亮的专栏:http://blog.csdn.net/liang5630/article/details/44098899 Android中的对话框形式大致可分为五种:分别是一般对话框 ...

  9. Jquery在表格中搜索关键字

    <!DOCTYPE html><html><head> <title>ddd</title></head><body> ...

  10. [UGUI]渲染层级关系

    参考链接: http://blog.csdn.net/meegomeego/article/details/42060389 Unity中的渲染顺序自上而下大致可以分为三层: 1.Camera层.可以 ...