日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
10張圖22段代碼,萬(wàn)字長(zhǎng)文帶你搞懂虛擬內(nèi)存模型和Malloc內(nèi)部原理

10張圖22段代碼,萬(wàn)字長(zhǎng)文帶你搞懂虛擬內(nèi)存模型和Malloc內(nèi)部原理

作者:程序喵大人 2020-11-11 08:25:45

云計(jì)算

虛擬化

存儲(chǔ)軟件 我們會(huì)通過(guò)/proc文件系統(tǒng)找到正在運(yùn)行的進(jìn)程的字符串所在的虛擬內(nèi)存地址,并通過(guò)更改此內(nèi)存地址的內(nèi)容來(lái)更改字符串內(nèi)容,使你更深入了解虛擬內(nèi)存這個(gè)概念!

成都網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁(yè)設(shè)計(jì)、成都網(wǎng)站建設(shè)公司、微信開發(fā)、小程序制作、集團(tuán)成都定制網(wǎng)站等服務(wù)項(xiàng)目。核心團(tuán)隊(duì)均擁有互聯(lián)網(wǎng)行業(yè)多年經(jīng)驗(yàn),服務(wù)眾多知名企業(yè)客戶;涵蓋的客戶類型包括:社區(qū)文化墻等眾多領(lǐng)域,積累了大量豐富的經(jīng)驗(yàn),同時(shí)也獲得了客戶的一致贊賞!

本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。

攤牌了,不裝了,其實(shí)我是程序喵辛苦工作一天還要回家編輯公眾號(hào)到大半夜的老婆,希望各位大哥能踴躍轉(zhuǎn)發(fā),完成我一千閱讀量的KPI(夢(mèng)想),謝謝!

咳咳,有點(diǎn)跑題,以下是程序喵的廢話,麻煩給個(gè)面子劃到最后點(diǎn)擊在看或者贊,證明我比程序喵人氣高,謝謝!

通過(guò)/proc文件系統(tǒng)探究虛擬內(nèi)存

我們會(huì)通過(guò)/proc文件系統(tǒng)找到正在運(yùn)行的進(jìn)程的字符串所在的虛擬內(nèi)存地址,并通過(guò)更改此內(nèi)存地址的內(nèi)容來(lái)更改字符串內(nèi)容,使你更深入了解虛擬內(nèi)存這個(gè)概念!這之前先介紹下虛擬內(nèi)存的定義!

虛擬內(nèi)存

虛擬內(nèi)存是一種實(shí)現(xiàn)在計(jì)算機(jī)軟硬件之間的內(nèi)存管理技術(shù),它將程序使用到的內(nèi)存地址(虛擬地址)映射到計(jì)算機(jī)內(nèi)存中的物理地址,虛擬內(nèi)存使得應(yīng)用程序從繁瑣的管理內(nèi)存空間任務(wù)中解放出來(lái),提高了內(nèi)存隔離帶來(lái)的安全性,虛擬內(nèi)存地址通常是連續(xù)的地址空間,由操作系統(tǒng)的內(nèi)存管理模塊控制,在觸發(fā)缺頁(yè)中斷時(shí)利用分頁(yè)技術(shù)將實(shí)際的物理內(nèi)存分配給虛擬內(nèi)存,而且64位機(jī)器虛擬內(nèi)存的空間大小遠(yuǎn)超出實(shí)際物理內(nèi)存的大小,使得進(jìn)程可以使用比物理內(nèi)存大小更多的內(nèi)存空間。

在深入研究虛擬內(nèi)存前,有幾個(gè)關(guān)鍵點(diǎn):

  • 每個(gè)進(jìn)程都有它自己的虛擬內(nèi)存
  • 虛擬內(nèi)存的大小取決于系統(tǒng)的體系結(jié)構(gòu)
  • 不同操作管理有著不同的管理虛擬內(nèi)存的方式,但大多數(shù)操作系統(tǒng)的虛擬內(nèi)存結(jié)構(gòu)如下圖:

virtual_memory.png

上圖并不是特別詳細(xì)的內(nèi)存管理圖,高地址其實(shí)還有內(nèi)核空間等等,但這不是這篇文章的主題。從圖中可以看到高地址存儲(chǔ)著命令行參數(shù)和環(huán)境變量,之后是??臻g、堆空間和可執(zhí)行程序,其中??臻g向下延申,堆空間向上增長(zhǎng),堆空間需要使用malloc分配,是動(dòng)態(tài)分配的內(nèi)存的一部分。

首先通過(guò)一個(gè)簡(jiǎn)單的C程序探究虛擬內(nèi)存。

  
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. /**
  5.  * main - 使用strdup創(chuàng)建一個(gè)字符串的拷貝,strdup內(nèi)部會(huì)使用malloc分配空間,
  6.  * 返回新空間的地址,這段地址空間需要外部自行使用free釋放
  7.  *
  8.  * Return: EXIT_FAILURE if malloc failed. Otherwise EXIT_SUCCESS
  9.  */
  10. int main(void)
  11. {
  12.     char *s;
  13.     s = strdup("test_memory");
  14.     if (s == NULL)
  15.     {
  16.         fprintf(stderr, "Can't allocate mem with malloc\n");
  17.         return (EXIT_FAILURE);
  18.     }
  19.     printf("%p\n", (void *)s);
  20.     return (EXIT_SUCCESS);
  21. }
  22. 編譯運(yùn)行:gcc -Wall -Wextra -pedantic -Werror main.c -o test; ./test
  23. 輸出:0x88f010

我的機(jī)器是64位機(jī)器,進(jìn)程的虛擬內(nèi)存高地址為0xffffffffffffffff, 低地址為0x0,而0x88f010遠(yuǎn)小于0xffffffffffffffff,因此大概可以推斷出被復(fù)制的字符串的地址(堆地址)是在內(nèi)存低地址附近,具體可以通過(guò)/proc文件系統(tǒng)驗(yàn)證.

ls /proc目錄可以看到好多文件,這里主要關(guān)注/proc/[pid]/mem和/proc/[pid]/maps

mem & maps

  
 
 
 
  1. man proc
  2. /proc/[pid]/mem
  3.     This file can be used to access the pages of a process's memory through open(2), read(2), and lseek(2).
  4. /proc/[pid]/maps
  5.     A  file containing the currently mapped memory regions and their access permissions.
  6.           See mmap(2) for some further information about memory mappings.
  7.               The format of the file is:
  8.        address           perms offset  dev   inode       pathname
  9.        00400000-00452000 r-xp 00000000 08:02 173521      /usr/bin/dbus-daemon
  10.        00651000-00652000 r--p 00051000 08:02 173521      /usr/bin/dbus-daemon
  11.        00652000-00655000 rw-p 00052000 08:02 173521      /usr/bin/dbus-daemon
  12.        00e03000-00e24000 rw-p 00000000 00:00 0           [heap]
  13.        00e24000-011f7000 rw-p 00000000 00:00 0           [heap]
  14.        ...
  15.        35b1800000-35b1820000 r-xp 00000000 08:02 135522  /usr/lib64/ld-2.15.so
  16.        35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522  /usr/lib64/ld-2.15.so
  17.        35b1a20000-35b1a21000 rw-p 00020000 08:02 135522  /usr/lib64/ld-2.15.so
  18.        35b1a21000-35b1a22000 rw-p 00000000 00:00 0
  19.        35b1c00000-35b1dac000 r-xp 00000000 08:02 135870  /usr/lib64/libc-2.15.so
  20.        35b1dac000-35b1fac000 ---p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so
  21.        35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so
  22.        35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870  /usr/lib64/libc-2.15.so
  23.        ...
  24.        f2c6ff8c000-7f2c7078c000 rw-p 00000000 00:00 0    [stack:986]
  25.        ...
  26.        7fffb2c0d000-7fffb2c2e000 rw-p 00000000 00:00 0   [stack]
  27.        7fffb2d48000-7fffb2d49000 r-xp 00000000 00:00 0   [vdso]
  28.               The address field is the address space in the process that the mapping occupies.
  29.           The perms field is a set of permissions:
  30.                    r = read
  31.                    w = write
  32.                    x = execute
  33.                    s = shared
  34.                    p = private (copy on write)
  35.               The offset field is the offset into the file/whatever;
  36.           dev is the device (major:minor); inode is the inode on that device.   0  indicates
  37.               that no inode is associated with the memory region,
  38.           as would be the case with BSS (uninitialized data).
  39.               The  pathname field will usually be the file that is backing the mapping.
  40.           For ELF files, you can easily coordinate with the offset field
  41.               by looking at the Offset field in the ELF program headers (readelf -l).
  42.               There are additional helpful pseudo-paths:
  43.                    [stack]
  44.                           The initial process's (also known as the main thread's) stack.
  45.                    [stack:] (since Linux 3.4)
  46.                           A thread's stack (where the  is a thread ID).
  47.               It corresponds to the /proc/[pid]/task/[tid]/ path.
  48.                    [vdso] The virtual dynamically linked shared object.
  49.                    [heap] The process's heap.
  50.               If the pathname field is blank, this is an anonymous mapping as obtained via the mmap(2) function.
  51.           There is no easy  way  to  coordinate
  52.               this back to a process's source, short of running it through gdb(1), strace(1), or similar.
  53.               Under Linux 2.0 there is no field giving pathname.

通過(guò)mem文件可以訪問(wèn)和修改整個(gè)進(jìn)程的內(nèi)存頁(yè),通過(guò)maps可以看到進(jìn)程當(dāng)前已映射的內(nèi)存區(qū)域,有地址和訪問(wèn)權(quán)限偏移量等,從maps中可以看到堆空間是在低地址而??臻g是在高地址. 從maps中可以看到heap的訪問(wèn)權(quán)限是rw,即可寫,所以可以通過(guò)堆地址找到上個(gè)示例程序中字符串的地址,并通過(guò)修改mem文件對(duì)應(yīng)地址的內(nèi)容,就可以修改字符串的內(nèi)容啦,程序:

  
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. #include 
  5. /**              
  6.  * main - uses strdup to create a new string, loops forever-ever
  7.  *                
  8.  * Return: EXIT_FAILURE if malloc failed. Other never returns
  9.  */
  10. int main(void)
  11. {
  12.      char *s;
  13.      unsigned long int i;
  14.      s = strdup("test_memory");
  15.      if (s == NULL)
  16.      {
  17.           fprintf(stderr, "Can't allocate mem with malloc\n");
  18.           return (EXIT_FAILURE);
  19.      }
  20.      i = 0;
  21.      while (s)
  22.      {
  23.           printf("[%lu] %s (%p)\n", i, s, (void *)s);
  24.           sleep(1);
  25.           i++;
  26.      }
  27.      return (EXIT_SUCCESS);
  28. }
  29. 編譯運(yùn)行:gcc -Wall -Wextra -pedantic -Werror main.c -o loop; ./loop
  30. 輸出:
  31. [0] test_memory (0x21dc010)
  32. [1] test_memory (0x21dc010)
  33. [2] test_memory (0x21dc010)
  34. [3] test_memory (0x21dc010)
  35. [4] test_memory (0x21dc010)
  36. [5] test_memory (0x21dc010)
  37. [6] test_memory (0x21dc010)
  38. ...

這里可以寫一個(gè)腳本通過(guò)/proc文件系統(tǒng)找到字符串所在位置并修改其內(nèi)容,相應(yīng)的輸出也會(huì)更改。

首先找到進(jìn)程的進(jìn)程號(hào)

  
 
 
 
  1. ps aux | grep ./loop | grep -v grep
  2. zjucad    2542  0.0  0.0   4352   636 pts/3    S+   12:28   0:00 ./loop

2542即為loop程序的進(jìn)程號(hào),cat /proc/2542/maps得到

  
 
 
 
  1. 00400000-00401000 r-xp 00000000 08:01 811716                             /home/zjucad/wangzhiqiang/loop
  2. 00600000-00601000 r--p 00000000 08:01 811716                             /home/zjucad/wangzhiqiang/loop
  3. 00601000-00602000 rw-p 00001000 08:01 811716                             /home/zjucad/wangzhiqiang/loop
  4. 021dc000-021fd000 rw-p 00000000 00:00 0                                  [heap]
  5. 7f2adae2a000-7f2adafea000 r-xp 00000000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so
  6. 7f2adafea000-7f2adb1ea000 ---p 001c0000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so
  7. 7f2adb1ea000-7f2adb1ee000 r--p 001c0000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so
  8. 7f2adb1ee000-7f2adb1f0000 rw-p 001c4000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so
  9. 7f2adb1f0000-7f2adb1f4000 rw-p 00000000 00:00 0
  10. 7f2adb1f4000-7f2adb21a000 r-xp 00000000 08:01 8661310                    /lib/x86_64-linux-gnu/ld-2.23.so
  11. 7f2adb3fa000-7f2adb3fd000 rw-p 00000000 00:00 0
  12. 7f2adb419000-7f2adb41a000 r--p 00025000 08:01 8661310                    /lib/x86_64-linux-gnu/ld-2.23.so
  13. 7f2adb41a000-7f2adb41b000 rw-p 00026000 08:01 8661310                    /lib/x86_64-linux-gnu/ld-2.23.so
  14. 7f2adb41b000-7f2adb41c000 rw-p 00000000 00:00 0
  15. 7ffd51bb3000-7ffd51bd4000 rw-p 00000000 00:00 0                          [stack]
  16. 7ffd51bdd000-7ffd51be0000 r--p 00000000 00:00 0                          [vvar]
  17. 7ffd51be0000-7ffd51be2000 r-xp 00000000 00:00 0                          [vdso]
  18. ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

看見堆地址范圍021dc000-021fd000,并且可讀可寫,而且021dc000<0x21dc010<021fd000,這就可以確認(rèn)字符串的地址在堆中,在堆中的索引是0x10(至于為什么是0x10,后面會(huì)講到),這時(shí)可以通過(guò)mem文件到0x21dc010地址修改內(nèi)容,字符串輸出的內(nèi)容也會(huì)隨之更改,這里通過(guò)python腳本實(shí)現(xiàn)此功能。

  
 
 
 
  1. #!/usr/bin/env python3
  2. '''             
  3. Locates and replaces the first occurrence of a string in the heap
  4. of a process    
  5. Usage: ./read_write_heap.py PID search_string replace_by_string
  6. Where:           
  7. - PID is the pid of the target process
  8. - search_string is the ASCII string you are looking to overwrite
  9. - replace_by_string is the ASCII string you want to replace
  10.   search_string with
  11. '''
  12. import sys
  13. def print_usage_and_exit():
  14.     print('Usage: {} pid search write'.format(sys.argv[0]))
  15.     sys.exit(1)
  16. # check usage  
  17. if len(sys.argv) != 4:
  18.     print_usage_and_exit()
  19. # get the pid from args
  20. pid = int(sys.argv[1])
  21. if pid <= 0:
  22.     print_usage_and_exit()
  23. search_string = str(sys.argv[2])
  24. if search_string  == "":
  25.     print_usage_and_exit()
  26. write_string = str(sys.argv[3])
  27. if search_string  == "":
  28.     print_usage_and_exit()
  29. # open the maps and mem files of the process
  30. maps_filename = "/proc/{}/maps".format(pid)
  31. print("[*] maps: {}".format(maps_filename))
  32. mem_filename = "/proc/{}/mem".format(pid)
  33. print("[*] mem: {}".format(mem_filename))
  34. # try opening the maps file
  35. try:
  36.     maps_file = open('/proc/{}/maps'.format(pid), 'r')
  37. except IOError as e:
  38.     print("[ERROR] Can not open file {}:".format(maps_filename))
  39.     print("        I/O error({}): {}".format(e.errno, e.strerror))
  40.     sys.exit(1)
  41. for line in maps_file:
  42.     sline = line.split(' ')
  43.     # check if we found the heap
  44.     if sline[-1][:-1] != "[heap]":
  45.         continue
  46.     print("[*] Found [heap]:")
  47.     # parse line
  48.     addr = sline[0]
  49.     perm = sline[1]
  50.     offset = sline[2]
  51.     device = sline[3]
  52.     inode = sline[4]
  53.     pathname = sline[-1][:-1]
  54.     print("\tpathname = {}".format(pathname))
  55.     print("\taddresses = {}".format(addr))
  56.     print("\tpermisions = {}".format(perm))
  57.     print("\toffset = {}".format(offset))
  58.     print("\tinode = {}".format(inode))
  59.     # check if there is read and write permission
  60.     if perm[0] != 'r' or perm[1] != 'w':
  61.         print("[*] {} does not have read/write permission".format(pathname))
  62.         maps_file.close()
  63.         exit(0)
  64.     # get start and end of the heap in the virtual memory
  65.     addr = addr.split("-")
  66.     if len(addr) != 2: # never trust anyone, not even your OS :)
  67.         print("[*] Wrong addr format")
  68.         maps_file.close()
  69.         exit(1)
  70.     addr_start = int(addr[0], 16)
  71.     addr_end = int(addr[1], 16)
  72.     print("\tAddr start [{:x}] | end [{:x}]".format(addr_start, addr_end))
  73.     # open and read mem
  74.     try:
  75.         mem_file = open(mem_filename, 'rb+')
  76.     except IOError as e:
  77.         print("[ERROR] Can not open file {}:".format(mem_filename))
  78.         print("        I/O error({}): {}".format(e.errno, e.strerror))
  79.         maps_file.close()
  80.         exit(1)
  81.     # read heap  
  82.     mem_file.seek(addr_start)
  83.     heap = mem_file.read(addr_end - addr_start)
  84.     # find string
  85.     try:
  86.         i = heap.index(bytes(search_string, "ASCII"))
  87.     except Exception:
  88.         print("Can't find '{}'".format(search_string))
  89.         maps_file.close()
  90.         mem_file.close()
  91.         exit(0)
  92.     print("[*] Found '{}' at {:x}".format(search_string, i))
  93.     # write the new string
  94.     print("[*] Writing '{}' at {:x}".format(write_string, addr_start + i))
  95.     mem_file.seek(addr_start + i)
  96.     mem_file.write(bytes(write_string, "ASCII"))
  97.     # close files
  98.     maps_file.close()
  99.     mem_file.close()
  100.     # there is only one heap in our example
  101.     break

運(yùn)行這個(gè)Python腳本

  
 
 
 
  1. zjucad@zjucad-ONDA-H110-MINI-V3-01:~/wangzhiqiang$ sudo ./loop.py 2542 test_memory test_hello
  2. [*] maps: /proc/2542/maps
  3. [*] mem: /proc/2542/mem
  4. [*] Found [heap]:
  5.         pathname = [heap]
  6.         addresses = 021dc000-021fd000
  7.         permisions = rw-p
  8.         offset = 00000000
  9.         inode = 0
  10.         Addr start [21dc000] | end [21fd000]
  11. [*] Found 'test_memory' at 10
  12. [*] Writing 'test_hello' at 21dc010

同時(shí)字符串輸出的內(nèi)容也已更改

  
 
 
 
  1. [633] test_memory (0x21dc010)
  2. [634] test_memory (0x21dc010)
  3. [635] test_memory (0x21dc010)
  4. [636] test_memory (0x21dc010)
  5. [637] test_memory (0x21dc010)
  6. [638] test_memory (0x21dc010)
  7. [639] test_memory (0x21dc010)
  8. [640] test_helloy (0x21dc010)
  9. [641] test_helloy (0x21dc010)
  10. [642] test_helloy (0x21dc010)
  11. [643] test_helloy (0x21dc010)
  12. [644] test_helloy (0x21dc010)
  13. [645] test_helloy (0x21dc010)

實(shí)驗(yàn)成功。

通過(guò)實(shí)踐畫出虛擬內(nèi)存空間分布圖

再列出內(nèi)存空間分布圖

基本上每個(gè)人或多或少都了解虛擬內(nèi)存的空間分布,那如何驗(yàn)證它呢,下面會(huì)提到。

堆棧空間

首先驗(yàn)證??臻g的位置,我們都知道C中局部變量是存儲(chǔ)在??臻g的,malloc分配的內(nèi)存是存儲(chǔ)在堆空間,所以可以通過(guò)打印出局部變量地址和malloc的返回內(nèi)存地址的方式來(lái)驗(yàn)證堆??臻g在整個(gè)虛擬空間中的位置。

  
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. /**
  5.  * main - print locations of various elements
  6.  *
  7.  * Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
  8.  */
  9. int main(void)
  10. {
  11.     int a;
  12.     void *p;
  13.     printf("Address of a: %p\n", (void *)&a);
  14.     p = malloc(98);
  15.     if (p == NULL)
  16.     {
  17.         fprintf(stderr, "Can't malloc\n");
  18.         return (EXIT_FAILURE);
  19.     }
  20.     printf("Allocated space in the heap: %p\n", p);
  21.     return (EXIT_SUCCESS);
  22. }
  23. 編譯運(yùn)行:gcc -Wall -Wextra -pedantic -Werror main.c -o test; ./test
  24. 輸出:
  25. Address of a: 0x7ffedde9c7fc
  26. Allocated space in the heap: 0x55ca5b360670

通過(guò)結(jié)果可以看出堆地址空間在棧地址空間下面,整理如圖:

可執(zhí)行程序

可執(zhí)行程序也在虛擬內(nèi)存中,可以通過(guò)打印main函數(shù)的地址,并與堆棧地址相比較,即可知道可執(zhí)行程序地址相對(duì)于堆棧地址的分布。

  
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. /**
  5.  * main - print locations of various elements
  6.  *
  7.  * Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
  8.  */
  9. int main(void)
  10. {
  11.     int a;
  12.     void *p;
  13.     printf("Address of a: %p\n", (void *)&a);
  14.     p = malloc(98);
  15.     if (p == NULL)
  16.     {
  17.         fprintf(stderr, "Can't malloc\n");
  18.         return (EXIT_FAILURE);
  19.     }
  20.     printf("Allocated space in the heap: %p\n", p);
  21.     printf("Address of function main: %p\n", (void *)main);
  22.     return (EXIT_SUCCESS);
  23. }
  24. 編譯運(yùn)行:gcc main.c -o test; ./test
  25. 輸出:
  26. Address of a: 0x7ffed846de2c
  27. Allocated space in the heap: 0x561b9ee8c670
  28. Address of function main: 0x561b9deb378a

由于main(0x561b9deb378a) < heap(0x561b9ee8c670) < (0x7ffed846de2c),可以畫出分布圖如下:

virtual_memory_stack_heap_executable.png

命令行參數(shù)和環(huán)境變量

程序入口main函數(shù)可以攜帶參數(shù):

  • 第一個(gè)參數(shù)(argc): 命令行參數(shù)的個(gè)數(shù)
  • 第二個(gè)參數(shù)(argv): 指向命令行參數(shù)數(shù)組的指針
  • 第三個(gè)參數(shù)(env): 指向環(huán)境變量數(shù)組的指針

通過(guò)程序可以看見這些元素在虛擬內(nèi)存中的位置:

  
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. /**
  5.  * main - print locations of various elements
  6.  *
  7.  * Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
  8.  */
  9. int main(int ac, char **av, char **env)
  10. {
  11.         int a;
  12.         void *p;
  13.         int i;
  14.         printf("Address of a: %p\n", (void *)&a);
  15.         p = malloc(98);
  16.         if (p == NULL)
  17.         {
  18.                 fprintf(stderr, "Can't malloc\n");
  19.                 return (EXIT_FAILURE);
  20.         }
  21.         printf("Allocated space in the heap: %p\n", p);
  22.         printf("Address of function main: %p\n", (void *)main);
  23.         printf("First bytes of the main function:\n\t");
  24.         for (i = 0; i < 15; i++)
  25.         {
  26.                 printf("%02x ", ((unsigned char *)main)[i]);
  27.         }
  28.         printf("\n");
  29.         printf("Address of the array of arguments: %p\n", (void *)av);
  30.         printf("Addresses of the arguments:\n\t");
  31.         for (i = 0; i < ac; i++)
  32.         {
  33.                 printf("[%s]:%p ", av[i], av[i]);
  34.         }
  35.         printf("\n");
  36.         printf("Address of the array of environment variables: %p\n", (void *)env);
  37.     printf("Address of the first environment variable: %p\n", (void *)(env[0]));
  38.         return (EXIT_SUCCESS);
  39. }
  40. 編譯運(yùn)行:gcc main.c -o test; ./test nihao hello
  41. 輸出:
  42. Address of a: 0x7ffcc154a748
  43. Allocated space in the heap: 0x559bd1bee670
  44. Address of function main: 0x559bd09807ca
  45. First bytes of the main function:
  46.         55 48 89 e5 48 83 ec 40 89 7d dc 48 89 75 d0
  47. Address of the array of arguments: 0x7ffcc154a848
  48. Addresses of the arguments:
  49.         [./test]:0x7ffcc154b94f [nihao]:0x7ffcc154b956 [hello]:0x7ffcc154b95c
  50. Address of the array of environment variables: 0x7ffcc154a868
  51. Address of the first environment variable: 0x7ffcc154b962

結(jié)果如下:

main(0x559bd09807ca) < heap(0x559bd1bee670) < stack(0x7ffcc154a748) < argv(0x7ffcc154a848) < env(0x7ffcc154a868) < arguments(0x7ffcc154b94f->0x7ffcc154b95c + 6)(6為hello+1('\0')) < env first(0x7ffcc154b962)

可以看出所有的命令行參數(shù)都是相鄰的,并且緊接著就是環(huán)境變量。

argv和env數(shù)組地址是相鄰的嗎

上例中argv有4個(gè)元素,命令行中有三個(gè)參數(shù),還有一個(gè)NULL指向標(biāo)記數(shù)組的末尾,每個(gè)指針是8字節(jié),8*4=32, argv(0x7ffcc154a848) + 32(0x20) = env(0x7ffcc154a868),所以argv和env數(shù)組指針是相鄰的.

命令行參數(shù)地址緊隨環(huán)境變量地址之后嗎

首先需要獲取環(huán)境變量數(shù)組的大小,環(huán)境變量數(shù)組是以NULL結(jié)束的,所以可以遍歷env數(shù)組,檢查是否為NULL,獲取數(shù)組大小,代碼如下:

  
 
 
 
  1. #include 
  2. #include 
  3. #include 
  4. /**                                                                                                      
  5.  * main - print locations of various elements                                                            
  6.  *                                                                                                       
  7.  * Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS                                      
  8.  */
  9. int main(int ac, char **av, char **env)
  10. {
  11.      int a;
  12.      void *p;
  13.      int i;
  14.      int size;
  15.      printf("Address of a: %p\n", (void *)&a);
  16.      p = malloc(98);
  17.      if (p == NULL)
  18.      {
  19.           fprintf(stderr, "Can't malloc\n");
  20.           return (EXIT_FAILURE);
  21.      }
  22.      printf("Allocated space in the heap: %p\n", p);
  23.      printf("Address of function main: %p\n", (void *)main);
  24.      printf("First bytes of the main function:\n\t");
  25.      for (i&n
    分享標(biāo)題:10張圖22段代碼,萬(wàn)字長(zhǎng)文帶你搞懂虛擬內(nèi)存模型和Malloc內(nèi)部原理
    瀏覽地址:http://m.5511xx.com/article/copdeeo.html