2012-01-23 18 views
18

Tôi đang cố điều hướng các bảng trang cho một quy trình trong linux. Trong một mô-đun hạt nhân tôi nhận ra các chức năng sau:Bảng trang đi bộ của quy trình trong Linux

static struct page *walk_page_table(unsigned long addr) 
{ 
    pgd_t *pgd; 
    pte_t *ptep, pte; 
    pud_t *pud; 
    pmd_t *pmd; 

    struct page *page = NULL; 
    struct mm_struct *mm = current->mm; 

    pgd = pgd_offset(mm, addr); 
    if (pgd_none(*pgd) || pgd_bad(*pgd)) 
     goto out; 
    printk(KERN_NOTICE "Valid pgd"); 

    pud = pud_offset(pgd, addr); 
    if (pud_none(*pud) || pud_bad(*pud)) 
     goto out; 
    printk(KERN_NOTICE "Valid pud"); 

    pmd = pmd_offset(pud, addr); 
    if (pmd_none(*pmd) || pmd_bad(*pmd)) 
     goto out; 
    printk(KERN_NOTICE "Valid pmd"); 

    ptep = pte_offset_map(pmd, addr); 
    if (!ptep) 
     goto out; 
    pte = *ptep; 

    page = pte_page(pte); 
    if (page) 
     printk(KERN_INFO "page frame struct is @ %p", page); 

out: 
    return page; 
} 

Chức năng này được gọi là từ ioctladdr là một địa chỉ ảo trong không gian địa chỉ tiến trình:

static int my_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long addr) 
{ 
    struct page *page = walk_page_table(addr); 
    ... 
    return 0; 
} 

Điều lạ là gọi ioctl trong một quá trình không gian người dùng, điều này segfaults ... nhưng có vẻ như là cách tôi đang tìm mục nhập bảng trang là chính xác vì với dmesg tôi có được ví dụ cho mỗi ioctl gọi:

[ 1721.437104] Valid pgd 
[ 1721.437108] Valid pud 
[ 1721.437108] Valid pmd 
[ 1721.437110] page frame struct is @ c17d9b80 

Vậy tại sao quá trình không thể hoàn thành chính xác cuộc gọi 'ioctl'? Có lẽ tôi phải khóa một cái gì đó trước khi điều hướng các bảng trang?

Tôi đang làm việc với hạt nhân 2.6.35-22 và ba bảng trang cấp.

Cảm ơn tất cả!

+0

Có thể ioctl syscall trả về thành công và mã được phân đoạn sau đó không? –

+0

Không vì syscall ioctl là lệnh cuối cùng trong 'main' trước' return'.Nếu tôi nhận xét 'ioctl' thì quá trình này không phân biệt. – MirkoBanchi

+0

Tại sao bạn ẩn phần mà bạn sử dụng địa chỉ của 'struct page'? Bạn có chắc chắn rằng segfaults của bạn không đến từ đây? Bạn đã thử gỡ lỗi này trên qemu chưa? –

Trả lời

11
pte_unmap(ptep); 

bị thiếu ngay trước nhãn. Hãy thử thay đổi mã theo cách này:

... 
    page = pte_page(pte); 
    if (page) 
     printk(KERN_INFO "page frame struct is @ %p", page); 

    pte_unmap(ptep); 

out: 
+0

Cảm ơn bạn. Tôi đã chắc chắn rằng hạt nhân đã được biên dịch mà không có CONFIG_HIGHPTE thay vào đó xác định đã được thiết lập để 'pte_offset_map' đã làm một' kmap'. – MirkoBanchi

+0

Cảm ơn! Tôi tiếp tục gặp sự cố với các thông báo như " được trả về với sự mất cân bằng trước". Cuối cùng truy tìm preempt_count() tăng lên pte_offset_map()! Thêm pte_unmap làm giảm nó và tất cả tốt. – kaiwan

5

Nhìn vào /proc/<pid>/smaps hệ thống tập tin, bạn sẽ nhìn thấy bộ nhớ userspace:

cat smaps 
bfa60000-bfa81000 rw-p 00000000 00:00 0   [stack] 
Size:    136 kB 
Rss:     44 kB 

và làm thế nào nó được in là thông qua fs/proc/task_mmu.c (từ nguồn kernel):

http://lxr.linux.no/linux+v3.0.4/fs/proc/task_mmu.c

if (vma->vm_mm && !is_vm_hugetlb_page(vma)) 
       walk_page_range(vma->vm_start, vma->vm_end, &smaps_walk); 
       show_map_vma(m, vma.....); 
     seq_printf(m, 
        "Size:   %8lu kB\n" 
        "Rss:   %8lu kB\n" 
        "Pss:   %8lu kB\n" 

Và chức năng của bạn hơi giống như hàm walk_page_range(). Nhìn vào walk_page_range(), bạn có thể thấy rằng cấu trúc smaps_walk là không được phép thay đổi trong khi nó đang đi bộ:

http://lxr.linux.no/linux+v3.0.4/mm/pagewalk.c#L153 

For eg: 

       } 
201    if (walk->pgd_entry) 
202      err = walk->pgd_entry(pgd, addr, next, walk); 
203    if (!err && 
204     (walk->pud_entry || walk->pmd_entry || walk->pte_entry 

Nếu đi bộ đã thay đổi, sau đó tất cả các kiểm tra trên có thể có được không phù hợp.

Tất cả những chỉ có nghĩa là bạn phải khóa mmap_sem khi đi bộ bảng trang:

if (!down_read_trylock(&mm->mmap_sem)) { 
      /* 
      * Activate page so shrink_inactive_list is unlikely to unmap 
      * its ptes while lock is dropped, so swapoff can make progress. 
      */ 
      activate_page(page); 
      unlock_page(page); 
      down_read(&mm->mmap_sem); 
      lock_page(page); 
    } 

và sau đó theo sau mở khóa:

up_read(&mm->mmap_sem); 

Và dĩ nhiên, khi bạn phát hành printk () của pagetable bên trong module kernel của bạn, module kernel đang chạy trong process context của quá trình insmod của bạn (chỉ cần printk "comm" và bạn có thể thấy "insmod") có nghĩa là mmap_sem là khóa, nó cũng có nghĩa là quá trình KHÔNG chạy, và do đó không có đầu ra bàn điều khiển cho đến khi quá trình hoàn thành (tất cả các đầu ra printk() chỉ vào bộ nhớ).

Âm thanh hợp lý?

+0

Cảm ơn bạn Peter, tôi đã cố gắng giữ 'mmap_sem' trước khi lệnh đầu tiên đọc bảng trang nhưng không hoạt động ... cùng lỗi phân đoạn. Tuy nhiên khi tôi gọi 'walk_page_table' tôi không ở trong ngữ cảnh của quá trình' insmod': tôi gọi nó bên trong 'my_ioctl' vì vậy tôi đang ở trong ngữ cảnh của quá trình gọi' ioctl' syscall. Điều này có thể tạo sự khác biệt không? – MirkoBanchi

+0

Vâng, nó tạo nên sự khác biệt. Bởi vì quá trình khác nhau có khác nhau cho mỗi quá trình pagetable - nếu u đi bộ phần phi hạt nhân. Nhưng tất cả các quá trình chia sẻ cùng một pagetable khi nói đến các địa chỉ hạt nhân. –

Các vấn đề liên quan