কোনও প্রক্রিয়া "কম র্যামের কারণে নিহত" হলে কী ঘটতে পারে?
কখনও কখনও বলা হয় যে ডিফল্টরূপে লিনাক্স অ্যাপ্লিকেশন কোড থেকে আরও মেমরির জন্য অনুরোধকে অস্বীকার করে না - যেমন malloc()
। 1 এটি বাস্তবে সত্য নয়; ডিফল্ট একটি তাত্পর্যপূর্ণ ব্যবহার করে
ঠিকানার জায়গার সুস্পষ্ট ওভার কমিটগুলি অস্বীকার করা হয়েছে। একটি সাধারণ সিস্টেমের জন্য ব্যবহৃত হয়। ওভার কমিটকে অদলবদলের ব্যবহার হ্রাস করার অনুমতি দেওয়ার সাথে সাথে এটি মারাত্মকভাবে বন্য বরাদ্দ ব্যর্থ হওয়ার বিষয়টি নিশ্চিত করে।
থেকে [linux_src]/Documentation/vm/overcommit-accounting
(সমস্ত উদ্ধৃতি 3.11 টি গাছের)। ঠিক কীভাবে "গুরুতর বন্য বরাদ্দ" হিসাবে গণনা করা হয়েছে তা সুস্পষ্টভাবে তৈরি করা হয়নি, সুতরাং বিশদটি নির্ধারণের জন্য আমাদের উত্সের মধ্য দিয়ে যেতে হবে। আমরা পাদটীকা 2 (নীচে) -এও পরীক্ষামূলক পদ্ধতিটি ব্যবহার করতে পারি এবং চেষ্টা করতে পারি এবং theত্বিকের কিছু প্রতিবিম্বও পাওয়া যায় - তার ভিত্তিতে আমার প্রাথমিক অভিজ্ঞতা অভিজ্ঞতা পর্যবেক্ষণ করে যে আদর্শ পরিস্থিতিতে (== সিস্টেমটি নিষ্ক্রিয়), যদি আপনি না করেন ' টির কোনও অদলবদল নেই, আপনাকে আপনার প্রায় অর্ধেক র্যাম বরাদ্দ করার অনুমতি দেওয়া হবে, এবং যদি আপনার অদলবদল হয়, আপনি আপনার প্রায় অর্ধেক র্যাম প্লাস আপনার সমস্ত সোয়াপ পাবেন। এটাই বা কম প্রক্রিয়া প্রতি (কিন্তু মনে রাখবেন এই সীমা নেই গতিশীল এবং কারণ রাষ্ট্রের পরিবর্তন সাপেক্ষে, পাদটীকা 5 কিছু পর্যবেক্ষণ দেখুন)।
"কমিটলিমিট" ফিল্ডের জন্য আপনার অর্ধেক র্যাম প্লাস সোয়াপ সুস্পষ্টভাবে ডিফল্ট /proc/meminfo
। এখানে এর অর্থ কী - এখানে দেখুন এবং স্রেফ আলোচিত সীমাবদ্ধতার সাথে এর কিছুই করার নেই [src]/Documentation/filesystems/proc.txt
:
কমিটলিমিট: ওভারকমিট অনুপাতের ভিত্তিতে ('vm.overcommit_ratio'), এটি বর্তমানে সিস্টেমে বরাদ্দকৃত মোট মেমরির পরিমাণ । এই সীমাটি কেবলমাত্র কঠোরভাবে ওভারকমিট অ্যাকাউন্টিং সক্ষম করা থাকলে ('vm.overcommit_memory' 2 মোডে মোড) মেনে চলে। কমিটলিমিটটি নিম্নলিখিত সূত্র দিয়ে গণনা করা হয়: কমিটলিমিট = ('ভিএম.ওভার কমিট_রটিও' * শারীরিক র্যাম) + অদলবদলের উদাহরণস্বরূপ, 1 জি ফিজিক্যাল র্যামের একটি সিস্টেম এবং 30-এর একটি 'vm.overcommit_ratio' সহ অদলবদল 7G হবে 7.3 জি এর কমিটলিমিট।
পূর্বে উদ্ধৃত ওভারকমিট-অ্যাকাউন্টিং ডকটি সূচিত হয়েছে যে ডিফল্ট vm.overcommit_ratio
50 হয় So তাই sysctl vm.overcommit_memory=2
আপনি যদি তারপর vm.covercommit_ratio সামঞ্জস্য করতে পারেন (এর সাথে sysctl
) এবং ফলাফলগুলি দেখতে পারেন। 3 ডিফল্ট মোড, যখন CommitLimit
কার্যকরী হয় না এবং শুধুমাত্র "অ্যাড্রেস স্পেস এর সুস্পষ্ট overcommits প্রত্যাখ্যান করছে", যখন vm.overcommit_memory=0
।
ডিফল্ট কৌশলটিতে "গম্ভীরভাবে বন্য বরাদ্দ" রোধের প্রতি প্রক্রিয়াধর্মের একটি হিউরিস্টিক সীমাবদ্ধতা রয়েছে, তবে এটি সিস্টেমকে মারাত্মকভাবে বন্য হওয়ার জন্য সম্পূর্ণ বরাদ্দ দেয়, বরাদ্দ-ভিত্তিক 4 এর অর্থ এটি এক পর্যায়ে স্মৃতিশক্তি শেষ হয়ে যেতে পারে এবং OOM হত্যাকারীর মাধ্যমে কোনও প্রক্রিয়াতে (দে) দেউলিয়া ঘোষণা করতে হবে ।
ওওএম হত্যাকারী কী খুন করে? কিছুই ছিল না যখন মেমরির জন্য জিজ্ঞাসা করা প্রক্রিয়া অগত্যা নয়, যেহেতু এটি অবশ্যই সত্যিকারের দোষী প্রক্রিয়া নয় এবং আরও গুরুত্বপূর্ণ, এটি সম্ভবত যে সমস্যাটি সিস্টেমটিকে খুব দ্রুত সমস্যা থেকে বের করে আনবে তা নয়।
এটি এখান থেকে উদ্ধৃত করা হয়েছে যা সম্ভবত একটি 2.6.x উত্স উদ্ধৃত করে:
/*
* oom_badness - calculate a numeric value for how bad this task has been
*
* The formula used is relatively simple and documented inline in the
* function. The main rationale is that we want to select a good task
* to kill when we run out of memory.
*
* Good in this context means that:
* 1) we lose the minimum amount of work done
* 2) we recover a large amount of memory
* 3) we don't kill anything innocent of eating tons of memory
* 4) we want to kill the minimum amount of processes (one)
* 5) we try to kill the process the user expects us to kill, this
* algorithm has been meticulously tuned to meet the principle
* of least surprise ... (be careful when you change it)
*/
যা শালীন যুক্তিযুক্ত বলে মনে হচ্ছে। তবে, ফরেনসিক না পেয়ে, # 5 (যা # 1 এর অপ্রয়োজনীয়) মনে হয় শক্ত বিক্রয় বাস্তবায়ন হিসাবে মনে হয়, এবং # 3 # 2 এর অপ্রয়োজনীয়। সুতরাং এটি # 2/3 এবং # 4 এ যুক্ত করা বিবেচনা করা বুদ্ধিমান হতে পারে।
আমি একটি সাম্প্রতিক উত্স (3.11) ধরে ফেলেছি এবং লক্ষ্য করেছি যে এই মন্তব্যটি অন্তর্বর্তীকালীনভাবে পরিবর্তিত হয়েছে:
/**
* oom_badness - heuristic function to determine which candidate task to kill
*
* The heuristic for determining which task to kill is made to be as simple and
* predictable as possible. The goal is to return the highest value for the
* task consuming the most memory to avoid subsequent oom failures.
*/
এটি # 2 সম্পর্কে কিছুটা স্পষ্টভাবে বলা যায়: "পরবর্তী ওম ব্যর্থতা এড়াতে সর্বাধিক স্মৃতি গ্রহণ করা কাজটি [হত্যা] করাই হ'ল লক্ষ্য", এবং জড়িত # 4 ( "আমরা ন্যূনতম পরিমাণ প্রক্রিয়াগুলিকে হত্যা করতে চাই ( এক ) ) ।
আপনি যদি ওওএম ঘাতককে ক্রিয়াতে দেখতে চান তবে পাদটীকা 5 দেখুন।
1 একটি বিভ্রান্তি গিলস আমাকে কৃতজ্ঞতার সাথে মুক্ত করেছেন, মন্তব্য দেখুন।
2 এখানে সি এর একটি সরল বিট যা ক্রমবর্ধমান বৃহত অংশগুলি মেমরির বৃহত্তর অংশগুলি নির্ধারণ করতে যখন আরও বেশি জন্য একটি অনুরোধ ব্যর্থ হবে তা নির্ধারণ করতে বলে:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define MB 1 << 20
int main (void) {
uint64_t bytes = MB;
void *p = malloc(bytes);
while (p) {
fprintf (stderr,
"%lu kB allocated.\n",
bytes / 1024
);
free(p);
bytes += MB;
p = malloc(bytes);
}
fprintf (stderr,
"Failed at %lu kB.\n",
bytes / 1024
);
return 0;
}
আপনি যদি সি না জানেন তবে আপনি এটি সংকলন করতে পারেন gcc virtlimitcheck.c -o virtlimitcheck
, তবে চালান ./virtlimitcheck
। এটি সম্পূর্ণরূপে নিরীহ, কারণ প্রক্রিয়াটি যে জায়গাগুলির জন্য জিজ্ঞাসা করে তার কোনও স্থান ব্যবহার করে না - যেমন, এটি সত্যই কোনও র্যাম ব্যবহার করে না।
4 জিবি সিস্টেম এবং 6 গিগাবাইট অদলবদল সহ একটি 3.11 x86_64 সিস্টেমে আমি ব্যর্থ হয়েছি; 7400000 কেবি; সংখ্যা ওঠানামা করে, তাই সম্ভবত রাষ্ট্র একটি ফ্যাক্টর। এই কাকতালীয়ভাবে পাসে হবে CommitLimit
মধ্যে /proc/meminfo
, কিন্তু এই মাধ্যমে পরিবর্তন vm.overcommit_ratio
কোনো পার্থক্য দেখা যায় না। একটি 6.6.১১ ৩২-বিট এআরএম ৪৪৮ এমবি সিস্টেমে MB৪ এমবি অদলবদল সহ, তবে আমি ব্যর্থ। 230 এমবি। এটি আকর্ষণীয় যেহেতু প্রথম ক্ষেত্রে পরিমাণটি প্রায় র্যামের পরিমাণের দ্বিগুণ, অন্যদিকে এটি প্রায় 1/4 যা - স্বাপের পরিমাণকে দৃ .়ভাবে বোঝানো একটি কারণ। এটি প্রথম সিস্টেমে অদলবদল করে নিশ্চিত করা হয়েছিল, যখন ব্যর্থতার প্রান্তটি down 1.95 গিগাবাইটে নেমে গিয়েছিল, এটি সামান্য এআরএম বাক্সের সাথে খুব অনুরূপ অনুপাত।
তবে আসলেই কি প্রক্রিয়াধীন? এটি প্রদর্শিত হয়। নীচের সংক্ষিপ্ত প্রোগ্রামটি কোনও ব্যবহারকারীকে মেমরির সংজ্ঞায়িত অংশের জন্য জিজ্ঞাসা করে, এবং যদি এটি সফল হয়, আপনার প্রত্যাবর্তনের জন্য অপেক্ষা করে - এইভাবে আপনি একসাথে একাধিক উদাহরণ চেষ্টা করতে পারেন:
#include <stdio.h>
#include <stdlib.h>
#define MB 1 << 20
int main (int argc, const char *argv[]) {
unsigned long int megabytes = strtoul(argv[1], NULL, 10);
void *p = malloc(megabytes * MB);
fprintf(stderr,"Allocating %lu MB...", megabytes);
if (!p) fprintf(stderr,"fail.");
else {
fprintf(stderr,"success.");
getchar();
free(p);
}
return 0;
}
তবে সাবধান থাকুন যে এটি নির্বিশেষে র্যাম এবং অদলবদলের পরিমাণ সম্পর্কে কঠোরভাবে নয় - সিস্টেমের অবস্থার প্রভাবগুলি সম্পর্কে পর্যবেক্ষণের জন্য পাদটীকা 5 দেখুন।
3 সিস্টেমেরCommitLimit
জন্য অনুমোদিত ঠিকানা জায়গার পরিমাণকে বোঝায় যখন vm.overcommit_memory = 2. সম্ভবত তখন, আপনি যে পরিমাণ পরিমাণ বরাদ্দ করতে পারবেন তা ইতিমধ্যে প্রতিশ্রুতিবদ্ধ হওয়া উচিত, যা দৃশ্যত ক্ষেত্র isCommitted_AS
এটি প্রদর্শিত একটি সম্ভাব্য আকর্ষণীয় পরীক্ষা #include <unistd.h>
হ'ল ভ্যালিমিচেকেক.কে শীর্ষে যোগ করুন (পাদটীকা 2 দেখুন) এবং লুপের fork()
ঠিক আগে while()
। কিছু ক্লান্তিকর সিঙ্ক্রোনাইজেশন ছাড়াই এখানে বর্ণিত হিসাবে কাজ করার গ্যারান্টিযুক্ত নয়, তবে এটি করার একটি শালীন সুযোগ রয়েছে, ওয়াইএমএমভি:
> sysctl vm.overcommit_memory=2
vm.overcommit_memory = 2
> cat /proc/meminfo | grep Commit
CommitLimit: 9231660 kB
Committed_AS: 3141440 kB
> ./virtlimitcheck 2&> tmp.txt
> cat tmp.txt | grep Failed
Failed at 3051520 kB.
Failed at 6099968 kB.
এটি অর্থবোধ করে - tmp.txt টি বিশদে বিশদভাবে দেখে আপনি প্রক্রিয়াগুলি তাদের বড় এবং বড় বরাদ্দের বিকল্প হিসাবে দেখতে পাবেন (আপনি যদি পিডটি আউটপুটে ফেলে দেন তবে এটি সহজ) যতক্ষণ না একজন স্পষ্টতই দাবি করেছেন যে অন্যটি ব্যর্থ হয়েছে। বিজয়ী তখন CommitLimit
বিয়োগ থেকে সমস্ত কিছু দখল করতে মুক্ত Committed_AS
।
4 এই মুহুর্তে এটি উল্লেখ করার মতো, যদি আপনি ইতিমধ্যে ভার্চুয়াল অ্যাড্রেসিং এবং ডিমান্ড পেজিং বুঝতে না পারেন তবে যে প্রতিশ্রুতি দেয় যে প্রথম স্থানে সম্ভব তা হ'ল যে কার্নেল যা ব্যবহারকারীল্যান্ড প্রসেসগুলিতে বরাদ্দ করে তা শারীরিক স্মৃতি নয় - এটি ভার্চুয়াল ঠিকানার স্থান । উদাহরণস্বরূপ, যদি কোনও প্রক্রিয়া কোনও কিছুর জন্য 10 এমবি সঞ্চয় করে রাখে তবে তা (ভার্চুয়াল) ঠিকানার ক্রম হিসাবে নির্ধারিত হয় তবে সেই ঠিকানাগুলি এখনও শারীরিক স্মৃতিতে সামঞ্জস্য করে না। যখন এই জাতীয় ঠিকানা অ্যাক্সেস করা হয়, তখন এটির একটি পৃষ্ঠা ত্রুটি হয়এবং তারপরে কার্নেল এটিকে সত্যিকারের মেমরিতে মানচিত্রের চেষ্টা করে যাতে এটি একটি আসল মান সঞ্চয় করতে পারে। প্রসেসগুলি সাধারণত তাদের অ্যাক্সেসের চেয়ে অনেক বেশি ভার্চুয়াল স্পেস সংরক্ষণ করে, যা কার্নেলটিকে র্যামের সর্বাধিক দক্ষ ব্যবহার করতে দেয়। যাইহোক, শারীরিক স্মৃতি এখনও একটি সীমাবদ্ধ সম্পদ এবং যখন এগুলি সমস্ত ভার্চুয়াল অ্যাড্রেস স্পেসে ম্যাপ করা হয়ে থাকে তখন কিছু র্যাম খালি করার জন্য কিছু ভার্চুয়াল ঠিকানার স্থানটি অপসারণ করতে হবে।
5 প্রথমে একটি সতর্কতা : আপনি যদি এটি দিয়ে চেষ্টা করে থাকেন তবে vm.overcommit_memory=0
নিশ্চিত হয়ে নিন যে আপনি নিজের কাজটি প্রথমে সংরক্ষণ করেছেন এবং কোনও সমালোচনামূলক অ্যাপ্লিকেশন বন্ধ করেছেন, কারণ সিস্টেমটি 90 ডলার সেকেন্ডের জন্য হিমায়িত হবে এবং কিছু প্রক্রিয়া মারা যাবে!
ধারণাটি হ'ল কাঁটাচামচ বোমা চালানো হবে যা 90 সেকেন্ড পরে বেরিয়ে আসে, কাঁটাচামচ দিয়ে জায়গা বরাদ্দ দেওয়া হয় এবং তাদের মধ্যে কিছু লোক র্যামকে প্রচুর পরিমাণে ডেটা লেখেন, স্টারডারকে প্রতিবেদন করার সময়।
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
/* 90 second "Verbose hungry fork bomb".
Verbose -> It jabbers.
Hungry -> It grabs address space, and it tries to eat memory.
BEWARE: ON A SYSTEM WITH 'vm.overcommit_memory=0', THIS WILL FREEZE EVERYTHING
FOR THE DURATION AND CAUSE THE OOM KILLER TO BE INVOKED. CLOSE THINGS YOU CARE
ABOUT BEFORE RUNNING THIS. */
#define STEP 1 << 30 // 1 GB
#define DURATION 90
time_t now () {
struct timeval t;
if (gettimeofday(&t, NULL) == -1) {
fprintf(stderr,"gettimeofday() fail: %s\n", strerror(errno));
return 0;
}
return t.tv_sec;
}
int main (void) {
int forks = 0;
int i;
unsigned char *p;
pid_t pid, self;
time_t check;
const time_t start = now();
if (!start) return 1;
while (1) {
// Get our pid and check the elapsed time.
self = getpid();
check = now();
if (!check || check - start > DURATION) return 0;
fprintf(stderr,"%d says %d forks\n", self, forks++);
// Fork; the child should get its correct pid.
pid = fork();
if (!pid) self = getpid();
// Allocate a big chunk of space.
p = malloc(STEP);
if (!p) {
fprintf(stderr, "%d Allocation failed!\n", self);
return 0;
}
fprintf(stderr,"%d Allocation succeeded.\n", self);
// The child will attempt to use the allocated space. Using only
// the child allows the fork bomb to proceed properly.
if (!pid) {
for (i = 0; i < STEP; i++) p[i] = i % 256;
fprintf(stderr,"%d WROTE 1 GB\n", self);
}
}
}
এটি সঙ্কলন gcc forkbomb.c -o forkbomb
। প্রথমে এটি ব্যবহার করে দেখুন sysctl vm.overcommit_memory=2
- আপনি সম্ভবত কিছু পাবেন:
6520 says 0 forks
6520 Allocation succeeded.
6520 says 1 forks
6520 Allocation succeeded.
6520 says 2 forks
6521 Allocation succeeded.
6520 Allocation succeeded.
6520 says 3 forks
6520 Allocation failed!
6522 Allocation succeeded.
এই পরিবেশে, এই ধরণের কাঁটাচামচ বোমা খুব বেশি পাওয়া যায় না। নোট করুন যে "এন ফর্কস বলে" এর সংখ্যাটি মোট প্রক্রিয়াগুলির সংখ্যা নয়, এটি শৃঙ্খলা / শাখায় যে প্রক্রিয়াটি তৈরি করে তার সংখ্যা।
এখন এটি দিয়ে চেষ্টা করুন vm.overcommit_memory=0
। আপনি যদি স্টাডারকে কোনও ফাইলে পুনর্নির্দেশ করেন তবে আপনি পরে কিছু অশোধিত বিশ্লেষণ করতে পারেন, যেমন:
> cat tmp.txt | grep failed
4641 Allocation failed!
4646 Allocation failed!
4642 Allocation failed!
4647 Allocation failed!
4649 Allocation failed!
4644 Allocation failed!
4643 Allocation failed!
4648 Allocation failed!
4669 Allocation failed!
4696 Allocation failed!
4695 Allocation failed!
4716 Allocation failed!
4721 Allocation failed!
প্রদর্শক overcommit_memory = 0 জন্য অনুসন্ধানমূলক - কেবলমাত্র 15 প্রসেস 1 গিগাবাইট বরাদ্দ করতে ব্যর্থ হয় রাষ্ট্র দ্বারা প্রভাবিত। সেখানে কতগুলি প্রক্রিয়া ছিল? Tmp.txt এর শেষে খুঁজছেন, সম্ভবত> 100,000। এখন কীভাবে আসলে 1 জিবি ব্যবহার করতে পারা যায়?
> cat tmp.txt | grep WROTE
4646 WROTE 1 GB
4648 WROTE 1 GB
4671 WROTE 1 GB
4687 WROTE 1 GB
4694 WROTE 1 GB
4696 WROTE 1 GB
4716 WROTE 1 GB
4721 WROTE 1 GB
আট - যা আবার অর্থবোধ করে, যেহেতু সেই সময়টিতে আমার কাছে 3 গিগাবাইট র্যাম ফ্রি এবং 6 জিবি স্বাপ ছিল।
এটি করার পরে আপনার সিস্টেম লগগুলিতে একবার দেখুন। আপনার OOM হত্যাকারী রিপোর্টিং স্কোরগুলি দেখতে হবে (অন্যান্য জিনিসের মধ্যে); সম্ভবত এটির সাথে সম্পর্কিত oom_badness
।