لینکس د کرنل او غوښتنلیکونو ډیبګ کولو لپاره لوی شمیر وسیلې لري. ډیری یې د غوښتنلیک فعالیت باندې منفي اغیزه لري او په تولید کې نشي کارول کیدی.
څو کاله وړاندې هلته وه
دمخه د غوښتنلیک ډیری اسانتیاوې شتون لري چې eBPF کاروي ، او پدې مقاله کې به موږ وګورو چې څنګه د کتابتون پراساس ستاسو د پروفایل کولو وړتیا ولیکئ
سیف ورو دی
د سیف کلستر ته یو نوی کوربه اضافه شوی. دې ته د ځینو معلوماتو له لیږدولو وروسته، موږ ولیدل چې د دې لخوا د لیکلو غوښتنو پروسس کولو سرعت د نورو سرورونو په پرتله خورا ټیټ و.
د نورو پلیټ فارمونو برخلاف ، دې کوربه bcache او نوي لینکس 4.15 کرنل کارولی. دا لومړی ځل و چې د دې ترتیب کوربه دلته کارول کیده. او په هغه وخت کې دا روښانه وه چې د ستونزې ریښه په نظرياتي توګه هر څه کیدی شي.
د کوربه پلټنه
راځئ چې د سیف-osd پروسې دننه څه پیښ شي په لټه کې پیل وکړو. د دې لپاره موږ به وکاروو
انځور موږ ته وایي چې فعالیت fdatasync() فعالیتونو ته د غوښتنې لیږلو ډیر وخت تیر کړ عمومي_جوړولو_غوښتنه(). دا پدې مانا ده چې ډیری احتمال د ستونزو لامل پخپله د osd ډیمون څخه بهر دی. دا کیدای شي د کرنل یا ډیسک وي. د iostat محصول د bcache ډیسکونو لخوا د غوښتنو پروسس کولو کې لوړ ځنډ ښودلی.
کله چې د کوربه معاینه کول، موږ وموندله چې سیسټمډ - udevd ډیمون د CPU وخت لوی مقدار مصرفوي - په څو کورونو کې شاوخوا 20٪. دا عجیب چلند دی، نو تاسو اړتیا لرئ چې معلومه کړئ ولې. څرنګه چې Systemd-udevd د uevents سره کار کوي، موږ پریکړه وکړه چې د دوی له لارې وګورو udevadm څارونکی. دا معلومه شوه چې په سیسټم کې د هر بلاک وسیلې لپاره د بدلون لوی شمیر پیښې رامینځته شوي. دا خورا غیر معمولي ده، نو موږ باید وګورو چې دا ټولې پیښې څه رامینځته کوي.
د BCC Toolkit کارول
لکه څنګه چې موږ دمخه موندلي ، دانه (او د سیسټم کال کې سیف ډیمون) ډیر وخت تیروي عمومي_جوړولو_غوښتنه(). راځئ هڅه وکړو چې د دې فعالیت سرعت اندازه کړو. IN
دا فیچر معمولا په چټکۍ سره کار کوي. ټول دا کوي چې غوښتنه د وسیلې ډرایور کتار ته انتقالوي.
Bcache یو پیچلی وسیله ده چې په حقیقت کې درې ډیسکونه لري:
- د ملاتړ وسیله (کیچ شوی ډیسک) ، پدې حالت کې دا یو ورو HDD دی؛
- د کیشینګ وسیله (کیشینګ ډیسک)، دلته دا د NVMe وسیله یوه برخه ده؛
- د bcache مجازی وسیله چې ورسره غوښتنلیک چلیږي.
موږ پوهیږو چې د غوښتنې لیږد ورو دی، مګر د کومو وسیلو لپاره؟ موږ به لږ وروسته له دې سره معامله وکړو.
موږ اوس پوهیږو چې د پیښو احتمال د ستونزو لامل کیږي. د هغه څه موندل چې واقعیا د دوی نسل لامل کیږي دومره اسانه ندي. راځئ فرض کړو چې دا یو ډول سافټویر دی چې په دوره توګه په لاره اچول کیږي. راځئ وګورو چې کوم ډول سافټویر په سیسټم کې د سکریپټ په کارولو سره چلیږي execsnoop له همدې څخه
د مثال په توګه:
/usr/share/bcc/tools/execsnoop | tee ./execdump
موږ به دلته د execsnoop بشپړ محصول ونه ښیې ، مګر زموږ لپاره د علاقې یوه کرښه داسې ښکاري:
sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'
دریم کالم د پروسې PPID (د اصلي PID) دی. د PID 5802 سره پروسه زموږ د څارنې سیسټم یوه برخه وه. کله چې د څارنې سیسټم تنظیمات چیک کول، غلط پیرامیټونه وموندل شول. د HBA اډاپټر تودوخه په هر 30 ثانیو کې اخیستل شوې ، کوم چې د اړتیا په پرتله خورا ډیر دی. وروسته له دې چې د چیک وقفه اوږد مهاله بدله کړه، موږ وموندله چې پدې کوربه کې د غوښتنې پروسس کولو ځنډ نور د نورو کوربه توب په پرتله څرګند نه و.
مګر دا لاهم روښانه نده چې ولې د bcache وسیله دومره ورو وه. موږ د ورته ترتیب سره د ازموینې پلیټ فارم چمتو کړی او هڅه یې کړې چې ستونزه په bcache کې د fio په چلولو سره بیا تولید کړو ، په دوره توګه د udevadm ټریګر چلولو لپاره د euvents رامینځته کولو لپاره.
د BCC پر بنسټ د وسیلو لیکل
راځئ هڅه وکړو چې یو ساده افادیت ولیکئ ترڅو ورو ورو تلیفونونه تعقیب او ښکاره کړئ عمومي_جوړولو_غوښتنه(). موږ د ډرایو نوم سره هم علاقه لرو د کوم لپاره چې دا فنکشن ویل شوی و.
پلان ساده دی:
- راجستر kprobe په عمومي_جوړولو_غوښتنه():
- موږ د ډیسک نوم په حافظه کې خوندي کوو، د فنکشن دلیل له لارې د لاسرسي وړ؛
- موږ د مهال ویش خوندي کوو.
- راجستر kretprobe د بیرته راستنیدو لپاره عمومي_جوړولو_غوښتنه():
- موږ اوسنی مهال ویش ترلاسه کوو؛
- موږ د خوندي شوي مهال ویش په لټه کې یو او د اوسني سره پرتله کوو؛
- که پایله له ټاکل شوي څخه لویه وي، نو موږ د خوندي شوي ډیسک نوم پیدا کوو او په ټرمینل کې یې ښکاره کوو.
Kprobes и kretprobes په الوتنه کې د فعالیت کوډ بدلولو لپاره د وقفې نقطې میکانیزم وکاروئ. تاسو لوستلی شئ
د python سکریپټ دننه د eBPF متن داسې ښکاري:
bpf_text = “”” # Here will be the bpf program code “””
د دندو تر منځ د معلوماتو د تبادلې لپاره، د eBPF پروګرامونه کاروي
struct data_t {
u64 pid;
u64 ts;
char comm[TASK_COMM_LEN];
u64 lat;
char disk[DISK_NAME_LEN];
};
BPF_HASH(p, u64, struct data_t);
BPF_PERF_OUTPUT(events);
دلته موږ د هش میز په نوم ثبت کوو pد کلیدي ډول سره u64 او د ډول ارزښت struct data_t. جدول به زموږ د BPF برنامې په شرایطو کې شتون ولري. BPF_PERF_OUTPUT میکرو یو بل جدول راجستر کوي چې نومیږي پېښې، کوم چې لپاره کارول کیږي
کله چې د فنکشن زنګ وهلو او له هغې څخه بیرته راستنیدو ترمینځ ځنډ اندازه کول ، یا مختلف فنکشنونو ته د زنګ وهلو ترمینځ ، تاسو اړتیا لرئ په پام کې ونیسئ چې ترلاسه شوي ډاټا باید په ورته شرایطو پورې اړه ولري. په بل عبارت، تاسو اړتیا لرئ د دندو احتمالي موازي لانچ په اړه په یاد ولرئ. موږ د دې وړتیا لرو چې د یوې پروسې په شرایطو کې د فنکشن زنګ وهلو او د بلې پروسې په شرایطو کې د دې فنکشن څخه بیرته راستنیدو ترمینځ ځنډ اندازه کړو ، مګر دا احتمال بې ګټې دی. یو ښه مثال به دلته وي
بیا، موږ اړتیا لرو هغه کوډ ولیکئ چې هغه وخت به پرمخ ځي کله چې د مطالعې لاندې فعالیت ویل کیږي:
void start(struct pt_regs *ctx, struct bio *bio) {
u64 pid = bpf_get_current_pid_tgid();
struct data_t data = {};
u64 ts = bpf_ktime_get_ns();
data.pid = pid;
data.ts = ts;
bpf_probe_read_str(&data.disk, sizeof(data.disk), (void*)bio->bi_disk->disk_name);
p.update(&pid, &data);
}
دلته د ویل شوي فنکشن لومړی دلیل به د دویم دلیل په توګه بدل شي
لاندې فنکشن به د بیرته راستنیدو پرمهال ویل کیږي عمومي_جوړولو_غوښتنه():
void stop(struct pt_regs *ctx) {
u64 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
struct data_t* data = p.lookup(&pid);
if (data != 0 && data->ts > 0) {
bpf_get_current_comm(&data->comm, sizeof(data->comm));
data->lat = (ts - data->ts)/1000;
if (data->lat > MIN_US) {
FACTOR
data->pid >>= 32;
events.perf_submit(ctx, data, sizeof(struct data_t));
}
p.delete(&pid);
}
}
دا فنکشن تیر ته ورته دی: موږ د پروسې PID او د مهال ویش معلوموو، مګر د نوي ډیټا جوړښت لپاره حافظه نه اختصاص کوو. پرځای یې، موږ د کیلي == اوسني PID په کارولو سره د دمخه موجود جوړښت لپاره د هش میز لټون کوو. که جوړښت وموندل شي، نو موږ د چلولو پروسې نوم ومومئ او دا یې اضافه کړئ.
هغه بائنری شفټ چې موږ یې دلته کاروو د GID تار ترلاسه کولو لپاره اړین دی. هغه. د اصلي پروسې PID چې موضوع یې په هغه شرایطو کې پیل کړې چې موږ یې کار کوو. هغه فعالیت چې موږ یې وایو
کله چې ټرمینل ته محصول ورکوو، موږ اوس مهال تار سره علاقه نه لرو، مګر موږ د اصلي پروسې سره علاقه لرو. وروسته له دې چې د پایلې ځنډ د ورکړل شوي حد سره پرتله کړو، موږ خپل جوړښت تیر کړو معلومات د میز له لارې د کارونکي ځای ته پېښې، له هغې وروسته موږ د ننوتلو څخه حذف کوو p.
په python سکریپټ کې چې دا کوډ به پورته کړي، موږ اړتیا لرو چې MIN_US او فاکتور د ځنډ حد او وخت واحدونو سره ځای په ځای کړو، کوم چې موږ به د دلیلونو څخه تیر کړو:
bpf_text = bpf_text.replace('MIN_US',str(min_usec))
if args.milliseconds:
bpf_text = bpf_text.replace('FACTOR','data->lat /= 1000;')
label = "msec"
else:
bpf_text = bpf_text.replace('FACTOR','')
label = "usec"
اوس موږ اړتیا لرو چې د BPF پروګرام له لارې چمتو کړو
b = BPF(text=bpf_text)
b.attach_kprobe(event="generic_make_request",fn_name="start")
b.attach_kretprobe(event="generic_make_request",fn_name="stop")
موږ به هم باید مشخص کړو struct data_t زموږ په سکریپټ کې، که نه نو موږ به هیڅ شی ونه لوستلو:
TASK_COMM_LEN = 16 # linux/sched.h
DISK_NAME_LEN = 32 # linux/genhd.h
class Data(ct.Structure):
_fields_ = [("pid", ct.c_ulonglong),
("ts", ct.c_ulonglong),
("comm", ct.c_char * TASK_COMM_LEN),
("lat", ct.c_ulonglong),
("disk",ct.c_char * DISK_NAME_LEN)]
وروستی ګام دا دی چې ټرمینل ته ډاټا تولید کړي:
def print_event(cpu, data, size):
global start
event = ct.cast(data, ct.POINTER(Data)).contents
if start == 0:
start = event.ts
time_s = (float(event.ts - start)) / 1000000000
print("%-18.9f %-16s %-6d %-1s %s %s" % (time_s, event.comm, event.pid, event.lat, label, event.disk))
b["events"].open_perf_buffer(print_event)
# format output
start = 0
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()
سکریپټ پخپله شتون لري
په پای کې! اوس موږ ګورو چې هغه څه چې د سټلانګ bcache وسیلې په څیر ښکاري په حقیقت کې یو سټیلینګ کال دی عمومي_جوړولو_غوښتنه() د زیرمه شوي ډیسک لپاره.
په دانه کې کیندل
د غوښتنې لیږد په جریان کې په ریښتیا څه ورو کیږي؟ موږ ګورو چې ځنډ حتی د غوښتنې محاسبې له پیل څخه دمخه پیښیږي ، د بیلګې په توګه. په دې باندې د احصایې د نورو محصولاتو لپاره د ځانګړي غوښتنې محاسبه (/proc/diskstats یا iostat) لا نه ده پیل شوې. دا په اسانۍ سره د iostat په چلولو سره تایید کیدی شي پداسې حال کې چې ستونزه بیا تولیدوي، یا
که موږ فعالیت ته وګورو عمومي_جوړولو_غوښتنه()، بیا به موږ وګورو چې مخکې له دې چې غوښتنې حساب پیل کړي ، دوه نور افعال بلل کیږي. لومړی - عمومي_میک_غوښتنه_چیکونه()، د ډیسک ترتیباتو په اړه د غوښتنې مشروعیت چیک کوي. دوهم -
ret = wait_event_interruptible(q->mq_freeze_wq,
(atomic_read(&q->mq_freeze_depth) == 0 &&
(preempt || !blk_queue_preempt_only(q))) ||
blk_queue_dying(q));
په دې کې، دانه د کتار د خلاصیدو لپاره انتظار کوي. راځئ چې ځنډ اندازه کړو blk_queue_enter():
~# /usr/share/bcc/tools/funclatency blk_queue_enter -i 1 -m
Tracing 1 functions for "blk_queue_enter"... Hit Ctrl-C to end.
msecs : count distribution
0 -> 1 : 341 |****************************************|
msecs : count distribution
0 -> 1 : 316 |****************************************|
msecs : count distribution
0 -> 1 : 255 |****************************************|
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 | |
داسې ښکاري چې موږ یو حل ته نږدې یو. هغه فنکشنونه چې د قطار منجمد / غیر منجمد کولو لپاره کارول کیږي عبارت دي له
هغه وخت چې د دې کتار پاکولو لپاره وخت نیسي د ډیسک ځنډ سره مساوي دی ځکه چې کرنل د ټولو قطار عملیاتو بشپړیدو ته انتظار باسي. یوځل چې قطار خالي وي ، د تنظیماتو بدلونونه پلي کیږي. له هغې وروسته ویل کیږي
اوس موږ د وضعیت سمولو لپاره کافي پوهیږو. د udevadm ټریګر کمانډ د دې لامل کیږي چې د بلاک آلې لپاره تنظیمات پلي شي. دا ترتیبات د udev قواعدو کې تشریح شوي. موږ موندلی شو چې کوم ترتیبات د sysfs له لارې د بدلولو هڅه کولو یا د کرنل سرچینې کوډ په کتلو سره قطار کنګل کوي. موږ کولی شو د BCC افادیت هم هڅه وکړو
~# /usr/share/bcc/tools/trace blk_freeze_queue -K -U
PID TID COMM FUNC
3809642 3809642 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
elevator_switch+0x29 [kernel]
elv_iosched_store+0x197 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
3809631 3809631 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
queue_requests_store+0xb6 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
د Udev قواعد په ندرت سره بدلیږي او معمولا دا په کنټرول شوي ډول پیښیږي. نو موږ ګورو چې حتی د دمخه ټاکل شوي ارزښتونو پلي کول د غوښتنلیک څخه ډیسک ته د غوښتنې لیږدولو کې د ځنډ لامل کیږي. البته، د udev پیښو رامینځته کول کله چې د ډیسک په ترتیب کې هیڅ بدلون شتون نلري (د مثال په توګه، وسیله نصب شوی / منحل شوی نه وي) ښه عمل ندی. په هرصورت، موږ کولی شو د کرنل سره مرسته وکړو چې غیر ضروري کار ونه کړي او د غوښتنې کتار کنګل کړي که اړین نه وي.
پایله
eBPF خورا انعطاف منونکی او ځواکمن وسیله ده. په مقاله کې موږ یو عملي قضیه وڅیړله او د هغه څه یوه کوچنۍ برخه یې وښودله چې کیدی شي. که تاسو د BCC اسانتیاو رامینځته کولو کې علاقه لرئ ، نو دا د لیدو ارزښت لري
د eBPF پراساس نور په زړه پوري ډیبګ کولو او پروفایل کولو وسیلې شتون لري. ېو د هغو څخه -
سرچینه: www.habr.com