[20260917]oracle共享内存段Key值的计算4.txt

[20260917]oracle共享内存段Key值的计算4.txt

--//前几天遇到斜线引起的问题,突然想了解共享内存段Key值的计算,肯定是某种hash算法,开始使用ora_hash函数盲猜,根本对不上。
--//做一个小小的分析尝试,中间遇到许多问题,比如sqlplus登录连接数据库,导致是那个函数调用shmget,在这里浪费了许多时间,
--//另外写blog分析。

1.环境:
SYS@book> @ ver2
==============================
PORT_STRING                   : x86_64/Linux 2.4.xx
VERSION                       : 21.0.0.0.0
BANNER                        : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
BANNER_FULL                   : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
BANNER_LEGACY                 : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
CON_ID                        : 0
PL/SQL procedure successfully completed.

$ ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 0          oracle     600        10485760   56
0x00000000 1          oracle     600        1140850688 56
0x00000000 2          oracle     600        8388608    56
0xafa94c20 3          oracle     600        2097152    56
--//当前key=0xafa94c20 = 2947107872.
--//2097152/1024/1024 = 2M.

2.分析:
--//^J在linux下按ctrl+v ctrl+j输入,相当于\n.
$ strace -Ttt -f -e ipc sqlplus -s -l / as sysdba <<<"host sleep 3^Jquit"
strace: Process 4359 attached
[pid  4359] 09:43:04.444115 shmget(0xafa94c20, 0, 000) = 3 <0.000124>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--//带入key值计算0xafa94c20返回shmid=3.说明在之前已经计算获得其hash值.
[pid  4359] 09:43:04.444475 shmctl(3, IPC_STAT, {shm_perm={uid=54321, gid=54321, mode=0600, key=2947107872, cuid=54321, cgid=54321}, shm_segsz=2097152, shm_cpid=2744, shm_lpid=4352, shm_nattch=56, shm_atime=1789609343, shm_dtime=1789609343, shm_ctime=1789606883}) = 0 <0.000092>
[pid  4359] 09:43:04.444764 shmat(3, NULL, 0) = 0x7f9a08c00000 <0.000107>
[pid  4359] 09:43:04.445083 shmdt(0x7f9a08c00000) = 0 <0.000113>
[pid  4359] 09:43:04.445367 shmget(0xafa94c21, 0, 000) = -1 ENOENT (No such file or directory) <0.000085>
[pid  4359] 09:43:04.445670 shmget(0xafa94c22, 0, 000) = -1 ENOENT (No such file or directory) <0.000112>
[pid  4359] 09:43:04.446015 shmget(0xafa94c23, 0, 000) = -1 ENOENT (No such file or directory) <0.000124>
[pid  4359] 09:43:04.446383 shmat(3, 0xa6000000, 0) = 0xa6000000 <0.000102>
[pid  4359] 09:43:04.446656 shmat(1, 0x61000000, 0) = 0x61000000 <0.000104>
[pid  4359] 09:43:04.446943 shmat(2, 0xa5000000, 0) = 0xa5000000 <0.000133>
[pid  4359] 09:43:04.447234 shmat(0, 0x60000000, 0) = 0x60000000 <0.000104>
[pid  4359] 09:43:04.476375 shmget(0xa93a2388, 0, 000) = -1 ENOENT (No such file or directory) <0.000117>
[pid  4359] 09:43:04.476661 shmget(0xa93a2389, 0, 000) = -1 ENOENT (No such file or directory) <0.000152>
[pid  4359] 09:43:04.476996 shmget(0xa93a238a, 0, 000) = -1 ENOENT (No such file or directory) <0.000100>
[pid  4359] 09:43:04.477238 shmget(0xa93a238b, 0, 000) = -1 ENOENT (No such file or directory) <0.000121>
[pid  4359] 09:43:04.495974 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x2} ---
strace: Process 4360 attached
--//以下host sleep 3执行.
[pid  4360] 09:43:07.862971 +++ exited with 0 +++
[pid  4358] 09:43:07.863132 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4360, si_uid=54321, si_status=0, si_utime=0, si_stime=0} ---

[pid  4358] 09:43:07.876305 +++ exited with 0 +++
09:43:07.884981 shmdt(0x61000000)       = 0 <0.000223>
09:43:07.885412 shmdt(0xa5000000)       = 0 <0.000139>
09:43:07.885733 shmdt(0x60000000)       = 0 <0.000202>
09:43:07.886122 shmdt(0xa6000000)       = 0 <0.000149>
09:43:07.897475 +++ exited with 0 +++
--//补充说明:不清楚为什么又做了shmget(0xafa94c21, 0, 000),shmget(0xafa94c22, 0, 000),shmget(0xafa94c23, 0, 000)三次尝
--//试失败,hash冲突吗?
--//然后使用shmat attach 共享内存段。后面做了4次
shmget(0xa93a2388, 0, 000) = -1 ENOENT (No such file or directory) <0.000117>
shmget(0xa93a2389, 0, 000) = -1 ENOENT (No such file or directory) <0.000152>
shmget(0xa93a238a, 0, 000) = -1 ENOENT (No such file or directory) <0.000100>
shmget(0xa93a238b, 0, 000) = -1 ENOENT (No such file or directory) <0.000121>
--//看后面跟踪的结果参与计算的是/u01/app/oracle/product/21.0.0/dbhome_1book.pga_#$,从内容看似乎与pga相关。难道pga也可以
--//使用类似共享内存段吗?可惜启动没有建立。

--//建立bpftrace脚本记录调用shmget的调用堆栈。注:开始使用gdb跟踪,一直无法知道执行的调用栈。
$ cat shmget.bt
tracepoint:syscalls:sys_enter_shmget
/ pid == $1 || $1 == 0 /
{
    @stacks[ustack, comm] = count();
}

END
{
   printf("\n=== Top Stacks (sample count) ===\n");
   print(@stacks);
   clear(@stacks);
}

--//执行1次登录数据库操作,完成按ctrl+c退出.
# bpftrace shmget.bt 0
Attached 2 probes
^C
=== Top Stacks (sample count) ===
@stacks[
        shmget+14
        skgmlocate+261
        skgmattach_primaryseg+234
        skgmattach_nondeferareas+313
        skgmattach+36
        ksmnfy+11816
        kscnfy+1212
        opiino+106
        opiodr+1256
        opidrv+1067
        sou2o+165
        opimai_real+400
        ssthrdmain+412
        main+292
        __libc_start_call_main+128
, oracle]: 4
@stacks[
        shmget+14
        skgmlocate+261
        skgmattach_primaryseg+234
        skgmattach_nondeferareas+313
        ksm_attach_sga+300
        ksmlsge_phaseone+449
        opimai_init+284
        opimai_real+356
        ssthrdmain+412
        main+292
        __libc_start_call_main+128
, oracle]: 4

--//一共执行8次,与前面strace跟踪可以对上.可以看出整个调用栈使用的函数.
--//注:实际上少了一个函数sskgmget.应该是skgmlocate调用sskgmget,sskgmget调用shmget.
--//后面+的数字是偏移量,反汇编代码很容易确定.

(gdb) disassemble skgmlocate
Dump of assembler code for function skgmlocate:
   0x0000000005790b80 <+0>:     xchg   %ax,%ax
   0x0000000005790b82 <+2>:     push   %rbp
   0x0000000005790b83 <+3>:     mov    %rsp,%rbp
   0x0000000005790b86 <+6>:     push   %r12
   0x0000000005790b88 <+8>:     push   %r13
   0x0000000005790b8a <+10>:    push   %r14
   0x0000000005790b8c <+12>:    push   %r15
   0x0000000005790b8e <+14>:    push   %rbx
...
   0x0000000005790c78 <+248>:   mov    -0x40(%rbp),%rdx
   0x0000000005790c7c <+252>:   mov    -0x50(%rbp),%r9
   0x0000000005790c80 <+256>:   call   0x57a67c0 <sskgmget>
   0x0000000005790c85 <+261>:   mov    %eax,%r9d
--//基本可以确定调用sskgmget调用shmget函数。

--//查看调用栈的函数skgmattach_primaryseg,反汇编skgmattach_primaryseg.

(gdb) disassemble skgmattach_primaryseg
Dump of assembler code for function skgmattach_primaryseg:
   0x00000000057905e0 <+0>:     xchg   %ax,%ax
   0x00000000057905e2 <+2>:     push   %rbp
   0x00000000057905e3 <+3>:     mov    %rsp,%rbp
   0x00000000057905e6 <+6>:     push   %r12
   0x00000000057905e8 <+8>:     push   %r13
   0x00000000057905ea <+10>:    push   %r14
   0x00000000057905ec <+12>:    push   %r15
   0x00000000057905ee <+14>:    push   %rbx
   0x00000000057905ef <+15>:    sub    $0x128,%rsp
   0x00000000057905f6 <+22>:    mov    %rdx,%r12
   0x00000000057905f9 <+25>:    mov    %rdi,%rbx
   0x00000000057905fc <+28>:    mov    %r12,%rdi
   0x00000000057905ff <+31>:    mov    %rsi,%r15
   0x0000000005790602 <+34>:    xor    %eax,%eax
   0x0000000005790604 <+36>:    mov    %rcx,-0x38(%rbp)
   0x0000000005790608 <+40>:    mov    0x100(%r12),%rsi
   0x0000000005790610 <+48>:    movl   $0xffffffff,-0x30(%rbp)
   0x0000000005790617 <+55>:    mov    %rax,-0x48(%rbp)
   0x000000000579061b <+59>:    movl   $0x0,-0x2c(%rbp)
   0x0000000005790622 <+66>:    mov    %rax,-0x40(%rbp)
   0x0000000005790626 <+70>:    call   0x57909b0 <skgmhash>
...

--//可以大致猜测计算使用skgmhash函数.

3. 继续:
 $ cat shm.bt
//uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:sskgmget
//uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:skgmlocate
//uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:skgmattach_primaryseg
uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:skgmhash
/ $1 == 0 || pid == $1 /
{
//      printf("[%s] %s pid=%d arg0=%016lx arg1=%016lx arg2=%016lx arg3=%016lx\n", strftime("%H:%M:%S", nsecs), func,pid,arg0, arg1,arg2,arg3);
//      printf("[%s] %s pid=%d arg0=%s arg1=%s arg2=%s arg3=%s\n", strftime("%H:%M:%S", nsecs), func,pid,buf(arg0,64),buf(arg1,64),buf(arg2,64),buf(arg3,64));
//      printf("[%s] %s pid=%d arg2=%s\n", strftime("%H:%M:%S", nsecs), func,pid,buf(arg2,52));
        printf("[%s] %s pid=%d ptr0=%016lx hash_text=%s length=%d\n", strftime("%H:%M:%S", nsecs), func,pid,arg0, buf(arg0,arg1),arg1);
}

uretprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:skgmhash
/ $1 == 0 || pid == $1 /
{
    printf("[%s] %s pid=%d return hash_value=%016lx %lu\n", strftime("%H:%M:%S", nsecs), func,pid, retval, retval);
}

--//执行1次登录数据库操作,完成按ctrl+c退出.
# bpftrace shm.bt 0
Attached 2 probes
[10:00:50] skgmhash pid=4605 ptr0=00007ffe767ecb30 hash_text=/u01/app/oracle/product/21.0.0/dbhome_1book length=43
[10:00:50] skgmhash pid=4605 return hash_value=00000000afa94c20 2947107872
[10:00:50] skgmhash pid=4605 ptr0=00007ffe767ead30 hash_text=/u01/app/oracle/product/21.0.0/dbhome_1book.pga_#$ length=50
[10:00:50] skgmhash pid=4605 return hash_value=00000000a93a2388 2839159688
^C

--//验证我的判断,共享内存段key值的计算使用skgmhash函数,返回值也可以对上。至于使用什么hash算法,超出我的能力.

--//如果换成如下环境变量再次登录:
$ export ORACLE_HOME=//u01/app/oracle/product/21.0.0/dbhome_1/
--//注:两头都有斜线/

# bpftrace shm.bt 0
Attached 2 probes
[10:05:40] skgmhash pid=4690 ptr0=00007ffe1c4e1a30 hash_text=//u01/app/oracle/product/21.0.0/dbhome_1book length=44
[10:05:40] skgmhash pid=4690 return hash_value=00000000af89bb44 2945039172
[10:05:40] skgmhash pid=4690 ptr0=00007ffe1c4dfc30 hash_text=//u01/app/oracle/product/21.0.0/dbhome_1book.pga_#$ length=51
[10:05:40] skgmhash pid=4690 return hash_value=000000002ecfdd0c 785374476
[10:05:40] skgmhash pid=4690 ptr0=00007ffe1c4e0330 hash_text=//u01/app/oracle/product/21.0.0/dbhome_1book length=44
[10:05:40] skgmhash pid=4690 return hash_value=00000000af89bb44 2945039172
[10:05:40] skgmhash pid=4690 ptr0=00007ffe1c4d46e0 hash_text=//u01/app/oracle/product/21.0.0/dbhome_1book length=44
[10:05:40] skgmhash pid=4690 return hash_value=00000000af89bb44 2945039172
^C

--//注意看hash_text的值,长度变成44多了一个字符,这样自然连接空实例。
--//拼接的字符串dbhome_1 book之间没有/,以前测试也验证从11.2.0.4版本开始环境变量ORACLE_HOME结尾有没有/,不会受到这个因素的影响.
posted @ 2026-09-21 21:23  lfree  阅读(3)  评论(0)    收藏  举报