كيفية إنشاء تفريغ أساسي في Linux بسبب خطأ تجزئة؟
سؤال
لدي عملية في Linux تتعرض لخطأ تجزئة.كيف يمكنني إخباره بإنشاء تفريغ أساسي عندما يفشل؟
المحلول
هذا يعتمد على القشرة التي تستخدمها.إذا كنت تستخدم bash، فإن أمر ulimit يتحكم في العديد من الإعدادات المتعلقة بتنفيذ البرنامج، مثل ما إذا كان يجب عليك تفريغ النواة.إذا كتبت
ulimit -c unlimited
فهذا سيخبر باش أن برامجه يمكنها تفريغ النوى من أي حجم.يمكنك تحديد حجم مثل 52M بدلاً من غير محدود إذا كنت تريد ذلك، ولكن من الناحية العملية لا ينبغي أن يكون هذا ضروريًا نظرًا لأن حجم الملفات الأساسية ربما لن يمثل مشكلة بالنسبة لك أبدًا.
في tcsh، ستكتب
limit coredumpsize unlimited
نصائح أخرى
كما هو موضح أعلاه، فإن السؤال الحقيقي الذي يتم طرحه هنا هو كيفية تمكين عمليات التفريغ الأساسية على نظام لم يتم تمكينها فيه.تمت الإجابة على هذا السؤال هنا.
إذا أتيت إلى هنا على أمل أن تتعلم كيفية إنشاء تفريغ أساسي لعملية معلقة، فالإجابة هي
gcore <pid>
إذا لم يكن gcore متاحًا على نظامك
kill -ABRT <pid>
لا تستخدم kill -SEGV لأن ذلك غالبًا ما يستدعي معالج الإشارة مما يزيد من صعوبة تشخيص العملية المتوقفة
ما فعلته في النهاية هو إرفاق gdb بالعملية قبل أن تتعطل، وبعد ذلك عندما حصلت على segfault قمت بتنفيذ الأمر generate-core-file
يأمر.هذا الجيل القسري من تفريغ الأساسية.
للتحقق من مكان إنشاء عمليات التفريغ الأساسية، قم بتشغيل:
sysctl kernel.core_pattern
أو:
cat /proc/sys/kernel/core_pattern
أين %e
هو اسم العملية و %t
وقت النظام.يمكنك تغييره في /etc/sysctl.conf
وإعادة التحميل بواسطة sysctl -p
.
إذا لم يتم إنشاء الملفات الأساسية (اختبرها عن طريق: sleep 10 &
و killall -SIGSEGV sleep
)، تحقق من الحدود عن طريق: ulimit -a
.
إذا كان حجم ملفك الأساسي محدودًا، فقم بتشغيل:
ulimit -c unlimited
لجعلها غير محدودة.
ثم اختبر مرة أخرى، إذا نجح تفريغ النواة، فسترى "(تم تفريغ النواة)" بعد إشارة خطأ التجزئة على النحو التالي:
خطأ تجزئة:11 (ملقاة الأساسية)
أنظر أيضا: الأساسية ملقاة - ولكن الملف الأساسي ليس في الدليل الحالي؟
أوبونتو
في Ubuntu تتم معالجة مقالب المخلفات الأساسية بواسطة تطبيق ويمكن أن يكون موجودا في /var/crash/
.ومع ذلك، يتم تعطيله افتراضيًا في الإصدارات المستقرة.
لمزيد من التفاصيل، يرجى مراجعة: أين أجد التفريغ الأساسي في أوبونتو؟.
ماك
بالنسبة لنظام التشغيل macOS، راجع: كيفية إنشاء عمليات تفريغ أساسية في نظام التشغيل Mac OS X؟
ربما يمكنك القيام بذلك بهذه الطريقة، هذا البرنامج عبارة عن عرض توضيحي لكيفية محاصرة خطأ التجزئة وإخراجه إلى مصحح الأخطاء (هذا هو الكود الأصلي المستخدم تحت AIX
) ويطبع تتبع المكدس حتى نقطة خطأ التجزئة.سوف تحتاج إلى تغيير sprintf
متغير للاستخدام gdb
في حالة لينكس.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>
static void signal_handler(int);
static void dumpstack(void);
static void cleanup(void);
void init_signals(void);
void panic(const char *, ...);
struct sigaction sigact;
char *progname;
int main(int argc, char **argv) {
char *s;
progname = *(argv);
atexit(cleanup);
init_signals();
printf("About to seg fault by assigning zero to *s\n");
*s = 0;
sigemptyset(&sigact.sa_mask);
return 0;
}
void init_signals(void) {
sigact.sa_handler = signal_handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGBUS);
sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGQUIT);
sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGHUP);
sigaction(SIGHUP, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGKILL);
sigaction(SIGKILL, &sigact, (struct sigaction *)NULL);
}
static void signal_handler(int sig) {
if (sig == SIGHUP) panic("FATAL: Program hanged up\n");
if (sig == SIGSEGV || sig == SIGBUS){
dumpstack();
panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown"));
}
if (sig == SIGQUIT) panic("QUIT signal ended program\n");
if (sig == SIGKILL) panic("KILL signal ended program\n");
if (sig == SIGINT) ;
}
void panic(const char *fmt, ...) {
char buf[50];
va_list argptr;
va_start(argptr, fmt);
vsprintf(buf, fmt, argptr);
va_end(argptr);
fprintf(stderr, buf);
exit(-1);
}
static void dumpstack(void) {
/* Got this routine from http://www.whitefang.com/unix/faq_toc.html
** Section 6.5. Modified to redirect to file to prevent clutter
*/
/* This needs to be changed... */
char dbx[160];
sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname);
/* Change the dbx to gdb */
system(dbx);
return;
}
void cleanup(void) {
sigemptyset(&sigact.sa_mask);
/* Do any cleaning up chores here */
}
قد تضطر إلى إضافة معلمة أيضًا للحصول على gdb لتفريغ النواة كما هو موضح هنا في هذه المدونة هنا.
هناك المزيد من الأشياء التي قد تؤثر على إنشاء التفريغ الأساسي.لقد واجهت هذه:
- يجب أن يكون دليل التفريغ قابلاً للكتابة.بشكل افتراضي، هذا هو الدليل الحالي للعملية، ولكن يمكن تغيير ذلك عن طريق الإعداد
/proc/sys/kernel/core_pattern
. - في بعض الظروف، قيمة النواة في
/proc/sys/fs/suid_dumpable
قد يمنع إنشاء النواة.
هناك المزيد من المواقف التي قد تمنع الإنشاء الموضحة في صفحة الدليل - حاول man core
.
لتفعيل التفريغ الأساسي قم بما يلي:
في
/etc/profile
التعليق على السطر:# ulimit -S -c 0 > /dev/null 2>&1
في
/etc/security/limits.conf
التعليق خارج السطر:* soft core 0
تنفيذ كمد
limit coredumpsize unlimited
والتحقق من ذلك مع كمدlimit
:# limit coredumpsize unlimited # limit cputime unlimited filesize unlimited datasize unlimited stacksize 10240 kbytes coredumpsize unlimited memoryuse unlimited vmemoryuse unlimited descriptors 1024 memorylocked 32 kbytes maxproc 528383 #
للتحقق من كتابة الملف الأساسي، يمكنك إنهاء العملية ذات الصلة باستخدام cmd
kill -s SEGV <PID>
(لا ينبغي أن تكون هناك حاجة إليه، فقط في حالة عدم كتابة أي ملف أساسي، يمكن استخدام هذا كفحص):# kill -s SEGV <PID>
بمجرد كتابة الملف الأساسي، تأكد من إلغاء تنشيط إعدادات التفريغ الأساسي مرة أخرى في الملفات ذات الصلة (1./2./3.)!
لأوبونتو 14.04
التحقق من تمكين التفريغ الأساسي:
ulimit -a
يجب أن يكون أحد الأسطر :
core file size (blocks, -c) unlimited
ان لم :
gedit ~/.bashrc
و أضفulimit -c unlimited
إلى نهاية الملف وحفظه، أعد تشغيل المحطة.أنشئ تطبيقك باستخدام معلومات التصحيح:
في ملف Makefile
-O0 -g
قم بتشغيل التطبيق الذي يقوم بإنشاء تفريغ أساسي (يجب إنشاء ملف تفريغ أساسي بالاسم "core" بالقرب من ملف application_name):
./application_name
تشغيل تحت gdb:
gdb application_name core
بشكل افتراضي سوف تحصل على ملف أساسي.تحقق للتأكد من أن الدليل الحالي للعملية قابل للكتابة، أو لن يتم إنشاء أي ملف أساسي.
من الأفضل تشغيل التفريغ الأساسي برمجياً باستخدام استدعاء النظام setrlimit
.
مثال:
#include <sys/resource.h>
bool enable_core_dump(){
struct rlimit corelim;
corelim.rlim_cur = RLIM_INFINITY;
corelim.rlim_max = RLIM_INFINITY;
return (0 == setrlimit(RLIMIT_CORE, &corelim));
}
انتظر.يتم ذلك تلقائيًا.لا حاجة للقيام بذلك
ومن الجدير بالذكر أنه إذا كان لديك systemd إعداد، ثم الأمور مختلفة قليلا.عادةً ما يتضمن الإعداد نقل الملفات الأساسية عبر الأنابيب core_pattern
قيمة sysctl، من خلال systemd-coredump(8)
.عادةً ما يتم تكوين الحد الأقصى لحجم الملف الأساسي على أنه "غير محدود" بالفعل.
ومن الممكن بعد ذلك استرداد مقالب الأساسية باستخدام coredumpctl(1)
.
تخزين مقالب الأساسية، الخ.تم تكوينه بواسطة coredump.conf(5)
.توجد أمثلة لكيفية الحصول على الملفات الأساسية في صفحة الدليل coredumpctl، لكن باختصار ستبدو بالشكل التالي:
ابحث عن الملف الأساسي:
[vps@phoenix]~$ coredumpctl list test_me | tail -1
Sun 2019-01-20 11:17:33 CET 16163 1224 1224 11 present /home/vps/test_me
الحصول على الملف الأساسي:
[vps@phoenix]~$ coredumpctl -o test_me.core dump 16163