Xin lỗi trước nếu bài đăng này hơi dày đặc / lộn xộn, nhưng tôi gặp khó khăn trong việc xây dựng nó tốt hơn ... Về cơ bản, tôi muốn nghiên cứu những gì xảy ra khi ghi vào đĩa cứng và tôi muốn biết:
- Sự hiểu biết của tôi dưới đây có đúng không - và nếu không, tôi sẽ sai ở đâu?
- Có công cụ nào tốt hơn để "thu thập" dữ liệu nhật ký, về tất cả các khía cạnh xảy ra trên PC, trong quá trình ghi đĩa không?
Chi tiết hơn - đầu tiên, HĐH tôi đang sử dụng là:
$ uname -a
Linux mypc 2.6.38-16-generic #67-Ubuntu SMP Thu Sep 6 18:00:43 UTC 2012 i686 i686 i386 GNU/Linux
Vì vậy, tôi có một cách đơn giản sau (ví dụ: các kiểm tra thông thường về lỗi hoạt động bị bỏ qua) chương trình C không gian người dùng , wtest.c
:
#include <stdio.h>
#include <fcntl.h> // O_CREAT, O_WRONLY, S_IRUSR
int main(void) {
char filename[] = "/tmp/wtest.txt";
char buffer[] = "abcd";
int fd;
mode_t perms = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
fd = open(filename, O_RDWR|O_CREAT, perms);
write(fd,buffer,4);
close(fd);
return 0;
}
Tôi xây dựng cái này với gcc -g -O0 -o wtest wtest.c
. Bây giờ, vì tôi đang cố gắng viết thư /tmp
, tôi lưu ý rằng đó là một thư mục dưới thư mục gốc /
- vì vậy tôi kiểm tra mount
:
$ mount
/dev/sda5 on / type ext4 (rw,errors=remount-ro,commit=0)
...
/dev/sda6 on /media/disk1 type ext4 (rw,uhelper=hal,commit=0)
/dev/sda7 on /media/disk2 type ext3 (rw,nosuid,nodev,uhelper=udisks,commit=0,commit=0,commit=0,commit=0,commit=0,commit=0)
...
Vì vậy, hệ thống tập tin gốc của tôi /
là một phân vùng của /dev/sda
thiết bị (và tôi cũng đang sử dụng các phân vùng khác dưới dạng đĩa / mount "độc lập"). Để tìm trình điều khiển cho thiết bị này, tôi sử dụng hwinfo
:
$ hwinfo --disk
...
19: IDE 00.0: 10600 Disk
...
SysFS ID: /class/block/sda
SysFS BusID: 0:0:0:0
...
Hardware Class: disk
Model: "FUJITSU MHY225RB"
...
Driver: "ata_piix", "sd"
Driver Modules: "ata_piix"
Device File: /dev/sda
...
Device Number: block 8:0-8:15
...
Vì vậy, /dev/sda
đĩa cứng rõ ràng được xử lý bởi trình điều khiển ata_piix
(và sd
).
$ grep 'ata_piix\| sd' <(gunzip </var/log/syslog.2.gz)
Jan 20 09:28:31 mypc kernel: [ 1.963846] ata_piix 0000:00:1f.2: version 2.13
Jan 20 09:28:31 mypc kernel: [ 1.963901] ata_piix 0000:00:1f.2: PCI INT B -> GSI 19 (level, low) -> IRQ 19
Jan 20 09:28:31 mypc kernel: [ 1.963912] ata_piix 0000:00:1f.2: MAP [ P0 P2 P1 P3 ]
Jan 20 09:28:31 mypc kernel: [ 2.116038] ata_piix 0000:00:1f.2: setting latency timer to 64
Jan 20 09:28:31 mypc kernel: [ 2.116817] scsi0 : ata_piix
Jan 20 09:28:31 mypc kernel: [ 2.117068] scsi1 : ata_piix
Jan 20 09:28:31 mypc kernel: [ 2.529065] sd 0:0:0:0: [sda] 488397168 512-byte logical blocks: (250 GB/232 GiB)
Jan 20 09:28:31 mypc kernel: [ 2.529104] sd 0:0:0:0: Attached scsi generic sg0 type 0
Jan 20 09:28:31 mypc kernel: [ 2.529309] sd 0:0:0:0: [sda] Write Protect is off
Jan 20 09:28:31 mypc kernel: [ 2.529319] sd 0:0:0:0: [sda] Mode Sense: 00 3a 00 00
Jan 20 09:28:31 mypc kernel: [ 2.529423] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
Jan 20 09:28:31 mypc kernel: [ 2.674783] sda: sda1 sda2 < sda5 sda6 sda7 sda8 sda9 sda10 >
Jan 20 09:28:31 mypc kernel: [ 2.676075] sd 0:0:0:0: [sda] Attached SCSI disk
Jan 20 09:28:31 mypc kernel: [ 4.145312] sd 2:0:0:0: Attached scsi generic sg1 type 0
Jan 20 09:28:31 mypc kernel: [ 4.150596] sd 2:0:0:0: [sdb] Attached SCSI removable disk
Tôi phải rút từ syslog cũ vì tôi đình chỉ rất nhiều, nhưng ở trên có vẻ như đoạn trích thích hợp từ syslog khi khởi động, nơi trình điều khiển ata_piix
(và sd
) lần đầu tiên khởi động.
Điểm khó hiểu đầu tiên của tôi là tôi không thể quan sát các trình điều khiển ata_piix
hoặc sd
trình điều khiển khác:
$ lsmod | grep 'ata_piix\| sd'
$
$ modinfo sd
ERROR: modinfo: could not find module sd
$ modinfo ata_piix
ERROR: modinfo: could not find module ata_piix
Vì vậy, câu hỏi đầu tiên của tôi là - tại sao tôi không thể quan sát ata_piix
mô-đun ở đây, chỉ trong nhật ký thời gian khởi động? Có phải vì ata_piix
(và sd
) được xây dựng dưới dạng trình điều khiển tích hợp trong nhân (nguyên khối), trái ngược với việc được xây dựng dưới dạng .ko
mô-đun hạt nhân (có thể tải) ?
Đúng vậy - bây giờ, tôi đang cố gắng quan sát những gì xảy ra khi chạy chương trình với trình ftrace
theo dõi chức năng tích hợp sẵn của Linux.
sudo bash -c '
KDBGPATH="/sys/kernel/debug/tracing"
echo function_graph > $KDBGPATH/current_tracer
echo funcgraph-abstime > $KDBGPATH/trace_options
echo funcgraph-proc > $KDBGPATH/trace_options
echo 0 > $KDBGPATH/tracing_on
echo > $KDBGPATH/trace
echo 1 > $KDBGPATH/tracing_on ; ./wtest ; echo 0 > $KDBGPATH/tracing_on
cat $KDBGPATH/trace > wtest.ftrace
'
... và đây là một đoạn của ftrace
nhật ký liên quan đến write
:
4604,352690 | 0) wtest-31632 | | sys_write () { 4604,352690 | 0) wtest-31632 | 0,750 chúng tôi | fget_light (); 4604,352692 | 0) wtest-31632 | | vfs_write () { 4604,352693 | 0) wtest-31632 | | rw_verify_area () { 4604,352693 | 0) wtest-31632 | | security_file_ allow () { 4604,352694 | 0) wtest-31632 | | apparmor_file_ allow () { 4604,352695 | 0) wtest-31632 | 0.811 chúng tôi | chung_file_perm (); 4604,352696 | 0) wtest-31632 | 2.198 chúng tôi | } 4604,352697 | 0) wtest-31632 | 3.573 chúng tôi | } 4604,352697 | 0) wtest-31632 | 4.979 chúng tôi | } 4604,352698 | 0) wtest-31632 | | do_sync_write () { 4604,352699 | 0) wtest-31632 | | ext4_file_write () { 4604,352700 | 0) wtest-31632 | | generic_file_aio_write () { 4604,352701 | 0) wtest-31632 | | mutex_lock () { 4604,352701 | 0) wtest-31632 | 0,666 chúng tôi | _cond_resched (); 4604.352703 | 0) wtest-31632 | 1.994 chúng tôi | } 4604.352704 | 0) wtest-31632 | | __generic_file_aio_write () { ... 4604,352728 | 0) wtest-31632 | | file_update_time () { ... 4604,352732 | 0) wtest-31632 | 0,756 chúng tôi | mnt_want_write_file (); 4604,352734 | 0) wtest-31632 | | __mark_inode_denty () { ... 4604,352750 | 0) wtest-31632 | | ext4_mark_inode_denty () { 4604,352750 | 0) wtest-31632 | 0,679 chúng tôi | _cond_resched (); 4604,352752 | 0) wtest-31632 | | ext4_reserve_inode_write () { ... 4604,352777 | 0) wtest-31632 | | __ext4_journal_get_write_access () { ... 4604,352795 | 0) wtest-31632 | | ext4_mark_iloc_denty () { ... 4604.352806 | 0) wtest-31632 | | __ext4_journal_stop () { ... 4604,352821 | 0) wtest-31632 | 0,684 chúng tôi | mnt_drop_write (); 4604,352822 | 0) wtest-31632 | + 93,541 chúng tôi | } 4604,352823 | 0) wtest-31632 | | generic_file_buffered_write () { 4604,352824 | 0) wtest-31632 | 0,654 chúng tôi | iov_iter_advance (); 4604,352825 | 0) wtest-31632 | | generic_perform_write () { 4604,352826 | 0) wtest-31632 | 0,709 chúng tôi | iov_iter_fault_in_readable (); 4604,352828 | 0) wtest-31632 | | ext4_da_write_begin () { 4604,352829 | 0) wtest-31632 | | ext4_journal_start_sb () { ... 4604,352847 | 0) wtest-31632 | 1.453 chúng tôi | __block_write_begin (); 4604,352849 | 0) wtest-31632 | + 21.128 chúng tôi | } 4604,352849 | 0) wtest-31632 | | iov_iter_copy_from_user_atomic () { 4604,352850 | 0) wtest-31632 | | __kmap_atomic () { ... 4604,352863 | 0) wtest-31632 | 0,672 chúng tôi | mark_page_accessed (); 4604,352864 | 0) wtest-31632 | | ext4_da_write_end () { 4604,352865 | 0) wtest-31632 | | generic_write_end () { 4604,352866 | 0) wtest-31632 | | block_write_end () { ... 4604,352893 | 0) wtest-31632 | | __ext4_journal_stop () { ... 4604.352909 | 0) wtest-31632 | 0,655 chúng tôi | mutex_unlock (); 4604,352911 | 0) wtest-31632 | 0,727 chúng tôi | generic_write_sync (); 4604,352912 | 0) wtest-31632 | ! 212.259 chúng tôi | } 4604,352913 | 0) wtest-31632 | ! 213.845 chúng tôi | } 4604,352914 | 0) wtest-31632 | ! 215.286 chúng tôi | } 4604,352914 | 0) wtest-31632 | 0,685 chúng tôi | __fsnotify_parent (); 4604,352916 | 0) wtest-31632 | | fsnotify () { 4604,352916 | 0) wtest-31632 | 0,907 chúng tôi | __srcu_read_lock (); 4604,352918 | 0) wtest-31632 | 0,685 chúng tôi | __srcu_read_unlock (); 4604,352920 | 0) wtest-31632 | 3.958 chúng tôi | } 4604,352920 | 0) wtest-31632 | ! 228,409 chúng tôi | } 4604,352921 | 0) wtest-31632 | ! 231.334 chúng tôi | }
Đây là điểm nhầm lẫn thứ hai của tôi - tôi có thể quan sát không gian người dùng write()
dẫn đến không gian kernel sys_write()
, như mong đợi; và trong sys_write()
, tôi quan sát các hàm liên quan đến bảo mật (ví dụ apparmor_file_permission()
), các hàm ghi "chung" (ví dụ generic_file_aio_write()
), ext4
các hàm liên quan đến hệ thống tệp (ví dụ ext4_journal_start_sb()
) - nhưng tôi không quan sát thấy bất cứ điều gì liên quan đến trình điều khiển ata_piix
(hoặc sd
)?!
Trang Truy tìm và lược tả - Dự án Yocto đề xuất sử dụng công cụ blk
theo dõi ftrace
để có thêm thông tin về hoạt động của thiết bị khối, nhưng nó không báo cáo gì cho tôi với ví dụ này. Ngoài ra, Trình điều khiển hệ thống tập tin Linux - Annon Inglorion (tutorfs) đề xuất rằng các hệ thống tập tin cũng (có thể) được triển khai như các mô-đun / trình điều khiển hạt nhân và tôi cũng đoán đó là trường hợp ext4
tương tự.
Cuối cùng, tôi có thể đã thề rằng trước đó tôi đã quan sát tên trình điều khiển trong dấu ngoặc vuông bên cạnh chức năng được hiển thị bởi người function_graph
theo dõi, nhưng tôi đoán rằng tôi đã trộn lẫn mọi thứ - nó có thể xuất hiện như thế trong dấu vết ngăn xếp (trở lại), nhưng không trong biểu đồ hàm. Hơn nữa, tôi có thể kiểm tra /proc/kallsyms
:
$ grep 'piix\| sd\|psmouse' /proc/kallsyms
...
00000000 d sd_ctl_dir
00000000 d sd_ctl_root
00000000 d sdev_class
00000000 d sdev_attr_queue_depth_rw
00000000 d sdev_attr_queue_ramp_up_period
00000000 d sdev_attr_queue_type_rw
00000000 d sd_disk_class
...
00000000 t piix_init_sata_map
00000000 t piix_init_sidpr
00000000 t piix_init_one
00000000 t pci_fixup_piix4_acpi
...
00000000 t psmouse_show_int_attr [psmouse]
00000000 t psmouse_protocol_by_type [psmouse]
00000000 r psmouse_protocols [psmouse]
00000000 t psmouse_get_maxproto [psmouse]
...
... và kiểm tra với nguồn Linux / trình điều khiển / ata / ata_piix.c , xác nhận rằng ví dụ piix_init_sata_map
thực sự là một hàm trong ata_piix
. Mà có lẽ nên nói với tôi rằng: các mô-đun được biên dịch trong kernel (để chúng trở thành một phần của hạt nhân nguyên khối) "làm mất" thông tin về mô-đun chúng đến từ đâu; tuy nhiên, các mô-đun có thể tải được xây dựng dưới dạng .ko
các đối tượng hạt nhân riêng biệt , bảo toàn thông tin đó (ví dụ như [psmouse]
được hiển thị ở trên trong dấu ngoặc vuông). Do đó, cũng ftrace
chỉ có thể hiển thị thông tin "mô-đun khởi tạo", chỉ cho các chức năng đến từ các mô-đun hạt nhân có thể tải. Điều này có đúng không?
Trên đây được xem xét, đây là sự hiểu biết mà tôi có về quá trình hiện tại:
- Khi khởi động,
ata_piix
trình điều khiển thiết lập ánh xạ bộ nhớ DMA (?) Giữa/dev/sda
và đĩa cứng- bởi vì điều này, tất cả các truy cập trong tương lai sẽ
/dev/sda
thôngata_piix
suốt với kernel (nghĩa là không thể theo dõi được) - vì tất cả các kernel sẽ thấy, chỉ đọc / ghi vào các vị trí bộ nhớ (không nhất thiết phải gọi đến các hàm kernel có thể theo dõi cụ thể), mà không được báo cáo bởifunction_graph
tracer
- bởi vì điều này, tất cả các truy cập trong tương lai sẽ
- Khi khởi động,
sd
trình điều khiển sẽ tiếp tục "phân tích" các phân vùng/dev/sda
, làm cho chúng khả dụng và có thể xử lý ánh xạ bộ nhớ giữa các phân vùng <-> thiết bị đĩa- một lần nữa, điều này sẽ làm cho các hoạt động truy cập thông qua
sd
trong suốt đến kernel
- một lần nữa, điều này sẽ làm cho các hoạt động truy cập thông qua
- Vì cả hai
ata_piix
vàsd
được biên dịch trong kernel, ngay cả khi một số chức năng của chúng cuối cùng bị bắt giữftrace
, chúng tôi không thể có được thông tin về mô-đun mà các chức năng đó sẽ đến từ đâu (ngoài tương quan "thủ công" với các tệp nguồn) - Sau đó,
mount
thiết lập mối quan hệ / ràng buộc giữa một phân vùng và trình điều khiển hệ thống tệp tương ứng (trong trường hợp nàyext4
)- kể từ thời điểm này, tất cả các truy cập vào hệ thống tập tin được gắn kết sẽ được xử lý bởi các
ext4
hàm - có thể truy nguyên theo kernel; nhưng nhưext4
được biên dịch trong kernel, trình theo dõi không thể cung cấp cho chúng ta thông tin mô-đun khởi tạo
- kể từ thời điểm này, tất cả các truy cập vào hệ thống tập tin được gắn kết sẽ được xử lý bởi các
- Vì vậy, ghi "chung" được quan sát, được gọi thông qua các
ext4
hàm, cuối cùng sẽ truy cập các vị trí bộ nhớ, có ánh xạ được thiết lập bởiata_piix
- nhưng ngoài điều đó,ata_piix
không can thiệp trực tiếp vào việc truyền dữ liệu (có thể được xử lý bởi DMA (bên ngoài bộ xử lý (s), và do đó minh bạch với nó).
Sự hiểu biết này có đúng không?
Một số câu hỏi con liên quan:
- Trong thiết lập của tôi ở trên, tôi có thể xác định trình điều khiển thiết bị PCI (
ata_piix
) và trình điều khiển hệ thống tập tin (ext4
); nhưng có các trình điều khiển ký tự hoặc khối được sử dụng ở đâu đó trên đường dẫn thực thi "ghi" và nếu có thì chúng là gì? - Những trình điều khiển nào sẽ xử lý bộ nhớ đệm (vì vậy các hoạt động đĩa không cần thiết được bỏ qua hoặc tối ưu hóa?)
- Tôi biết từ trước đó
/dev/shm
là một hệ thống tập tin trong RAM;mount | grep shm
cho tôi báo cáo :none on /dev/shm type tmpfs (rw,nosuid,nodev)
. Điều đó có nghĩa là - trái ngược với/dev/sda
-shm
hệ thống tập tin đơn giản là thiếu ánh xạ (DMA) từ các phần tử "của chính nó" đến các địa chỉ bus đối với một thiết bị; và do đó tất cả các truy cập thông quatmpfs
trình điều khiển hệ thống tập tin kết thúc trong RAM thực tế?