ফাইল অ্যাক্সেসের জন্য কখন আমার এমএমএপ ব্যবহার করা উচিত?


276

পসিক্স এনভায়রনমেন্টগুলি ফাইল অ্যাক্সেসের কমপক্ষে দুটি উপায় সরবরাহ করে। সেখানে সাধারন সিস্টেম কল এর open(), read(), write(), এবং বন্ধুদের, কিন্তু এছাড়াও ব্যবহারের বিকল্প mmap()ভার্চুয়াল মেমরি মধ্যে ফাইল ম্যাপ।

কখন অন্যটির উপরে ব্যবহার করা ভাল? দুটি পৃথক ইন্টারফেস সহ যোগ্যতার স্বতন্ত্র সুবিধাগুলি কী?


16
আরও দেখুন mmap () পড়া ব্লক বনাম এবং এই পোস্টে তার উত্তরের এক রেফারেন্সড লিনাস টোরভাল্ডস দ্বারা।
এমভিজি 21

উত্তর:


299

mmapআপনার কাছে একই ফাইল থেকে কেবল পঠন ফ্যাশনে ডেটা অ্যাক্সেস করার একাধিক প্রক্রিয়া থাকে, যা আমি লিখি এমন সার্ভার সিস্টেমের মধ্যে সাধারণ। mmapএই সমস্ত প্রক্রিয়াগুলিকে প্রচুর মেমরি সঞ্চয় করে একই শারীরিক মেমরি পৃষ্ঠাগুলি ভাগ করতে দেয়।

mmapঅপারেটিং সিস্টেমকে পেজিং ক্রিয়াকলাপগুলি সর্বোত্তম করতে সহায়তা করে। উদাহরণস্বরূপ, দুটি প্রোগ্রাম বিবেচনা করুন; প্রোগ্রাম Aযা একটি 1MBফাইলের সাথে তৈরি করে বাফারটি দিয়ে তৈরি করে mallocএবং প্রোগ্রাম বি যা mmapsমেমরিতে 1 এমবি ফাইল। যদি অপারেটিং সিস্টেমটির Aমেমরির কিছু অংশ অদলবদল করতে হয় তবে মেমরিটি পুনরায় ব্যবহার করার আগে এটি অবশ্যই বাফারের সামগ্রীগুলি অদলবদলে লিখতে হবে। ইন Bএর ক্ষেত্রে কোনো অপরিবর্তিত mmapঅপারেটিং সিস্টেম জানে কারণ কিভাবে বিদ্যমান ফাইল তারা থেকে তাদের ফিরিয়ে আনতে 'ঘ পৃষ্ঠাগুলি অবিলম্বে পুনঃব্যবহার করা যাবে mmap' থেকে ঘ। (ওএস সনাক্ত করতে পারে যে লেখাগুলি mmap'ডি পৃষ্ঠাগুলি কেবলমাত্র পঠনযোগ্য হিসাবে চিহ্নিত করে এবং সেটিং কৌশলগুলি অনুলিপি করার অনুরূপ সেগ ফল্টগুলি সনাক্ত করে কোন পৃষ্ঠাগুলিকে অপরিবর্তিত রয়েছে) OS

mmapআন্ত প্রক্রিয়া যোগাযোগের জন্য দরকারী । আপনি mmapযে প্রক্রিয়াগুলিতে যোগাযোগ করতে হবে এবং তারপরে এই mmap'dঅঞ্চলে সিঙ্ক্রোনাইজেশন আদিমগুলি ব্যবহার করতে পারেন সেগুলি পড়ার / লেখার মতো ফাইল করতে পারেন (এটি MAP_HASSEMAPHOREপতাকাটি এর জন্য কী)।

আপনার mmapযদি 32 বিট মেশিনে খুব বড় ফাইলগুলির সাথে কাজ করতে হয় তবে একটি জায়গা বিশ্রী হতে পারে। এটি হ'ল কারণটি mmapআপনার প্রক্রিয়াটির ঠিকানার জায়গার ঠিকানার একটি সংক্ষিপ্ত ব্লক সন্ধান করতে হবে যা ম্যাপ করা ফাইলের পুরো পরিসীমা ফিট করার জন্য যথেষ্ট বড়। আপনার ঠিকানার স্থান খণ্ডিত হয়ে উঠলে এটি সমস্যা হয়ে উঠতে পারে, যেখানে আপনার কাছে 2 জিবি ঠিকানা স্থান মুক্ত থাকতে পারে, তবে এর কোনও স্বতন্ত্র পরিসীমা 1 জিবি ফাইল ম্যাপিংয়ের সাথে খাপ খায় না। এক্ষেত্রে আপনাকে ফাইলটি ফিট করতে চাইলে তার চেয়ে ছোট ছোট অংশে ম্যাপ করতে হতে পারে।

mmapপঠন / লেখার প্রতিস্থাপন হিসাবে আরও একটি সম্ভাব্য বিশ্রী উদ্ভাস হ'ল আপনাকে পৃষ্ঠা আকারের অফসেটগুলিতে আপনার ম্যাপিং শুরু করতে হবে। আপনি যদি অফসেটে কিছু তথ্য পেতে চান তবে আপনাকে Xসেই অফসেটটি ফিক্সআপ করতে হবে যাতে এটির সাথে সামঞ্জস্য হয় mmap

এবং শেষ অবধি, পড়া / লেখাই হ'ল একমাত্র উপায় যা আপনি কিছু ধরণের ফাইলের সাথে কাজ করতে পারেনপাইপ এবং ttysmmap মত জিনিস ব্যবহার করা যাবে না ।


10
আপনি যে ফাইলগুলি বাড়ছে তাতে এমএমএপ () ব্যবহার করতে পারেন? অথবা আপনি যখন এমএমএপি () মেমরি / ফাইল বরাদ্দ করেন তখন পয়েন্টটিতে আকারটি স্থির করা হয়?
জোনাথন লেফলার

29
আপনি যখন এমএমএপ কল করবেন তখন আপনাকে একটি আকার নির্দিষ্ট করতে হবে। সুতরাং আপনি যদি কোনও লেজ অপারেশন এর মতো কিছু করতে চান তবে এটি খুব উপযুক্ত নয়।
ডন নিউফিল্ড

5
আফাইক বিএসডির MAP_HASSEMAPHOREসাথে সুনির্দিষ্ট।
প্যাট্রিক Schlüter

6
@ জোনাথনলফলার অবশ্যই বাড়ছে এমন ফাইলগুলিতে আপনি এমএমএপ () ব্যবহার করতে পারেন তবে ফাইলটি প্রাথমিকভাবে বরাদ্দ করা স্থানের সীমাতে পৌঁছে গেলে আপনাকে নতুন আকারের সাথে আবার এমএমএপ () কল করতে হবে। লেভেলডিবির পোস্টিক্সম্যাপ ফাইল আপনাকে একটি ভাল উদাহরণ দেয়। তবে এটি 1.15 থেকে এমএমএপ ব্যবহার বন্ধ করে দিয়েছে। আপনি এর থেকে পুরোনো সংস্করণ পেতে পারেন গিটহাব
baotiao

4
একাধিক পাসে কোনও ফাইল প্রক্রিয়া করার প্রয়োজনে এমএমএপিও কার্যকর হতে পারে: ভার্চুয়াল মেমরি পৃষ্ঠাগুলি বরাদ্দকরণের ব্যয়টি কেবল একবারই প্রদান করা হয়।
জিব

69

একটি ক্ষেত্র যেখানে আমি এমএম্যাপ () খুঁজে পেয়েছি যাতে সুবিধা না হয় সেগুলি ছিল ছোট ফাইলগুলি পড়ার সময় (16 কে-র নীচে)। পুরো ফাইলটি পড়তে পেজটির ওভারহেডটি কেবলমাত্র একটি একক রিড () সিস্টেম কল করার সাথে তুলনা করে খুব বেশি ছিল। এর কারণ কার্নেল কখনও কখনও আপনার সময় স্লাইসে সম্পূর্ণরূপে একটি পড়া পুরোপুরি সন্তুষ্ট করতে পারে, যার অর্থ আপনার কোড সরে যায় না। একটি পৃষ্ঠা ত্রুটির সাথে, এটি আরও বেশি সম্ভবত মনে হয়েছিল যে অন্য কোনও প্রোগ্রাম নির্ধারিত হবে, ফাইল অপারেশনটির উচ্চতর বিলম্ব হয়েছে।


4
+1 আমি এটি নিশ্চিত করতে পারি। ছোট ফাইলগুলির জন্য এটি mallocএক টুকরো মেমরির দ্রুত readএবং এটিতে 1 করে। এটি মেমোরি ম্যাপসকে ম্যালোক'ড হ্যান্ডেল করে এমন একই কোডটি পেতে দেয়।
প্যাট্রিক Schlüter

35
এটি বলেছে, এর পক্ষে আপনার ন্যায়সঙ্গততা সঠিক নয়। তফসিলটির সাথে পার্থক্যটির কিছুই করার নেই। পার্থক্যটি পৃষ্ঠার সারণিতে লেখার অ্যাক্সেস থেকে আসে, যা কার্নেলের একটি বিশ্বব্যাপী কাঠামো যা কোনও মেমরি পৃষ্ঠা এবং এর অ্যাক্সেসের অধিকারগুলি কী প্রসেস করে তা ধারণ করে। এই অপারেশনটি খুব ব্যয়বহুল হতে পারে (এটি ক্যাশে লাইনগুলিকে আক্রমণ করতে পারে, এটি টিএলবি দিয়ে যেতে পারে, টেবিলটি বিশ্বব্যাপী তাই সমবর্তী অ্যাক্সেস ইত্যাদির বিরুদ্ধে রক্ষা করতে হবে)। আপনার একটি নির্দিষ্ট আকারের মানচিত্রের প্রয়োজন যাতে readঅ্যাক্সেসের ওভারহেড ভার্চুয়াল মেমরি ম্যানিপুলেশনের ওভারহেডের চেয়ে বেশি থাকে।
প্যাট্রিক Schlüter

1
@ প্যাট্রিকস্ল্যাটার ঠিক আছে, আমি বুঝতে পারি যে এমএম্যাপের শুরুতে ওভারহেড রয়েছে () যা পৃষ্ঠার টেবিলটি সংশোধন করে। বলুন আমরা একটি ফাইলের 16 কে মেমোরিতে ম্যাপ করি। 4K পৃষ্ঠার আকারের জন্য, mmapপৃষ্ঠা সারণীতে 4 টি এন্ট্রি আপডেট করতে হবে। তবে read16 কে-এর একটি বাফারে অনুলিপি ব্যবহার করার সাথে সাথে 4 পৃষ্ঠার টেবিল এন্ট্রিগুলি আপডেট করাও জরুরী নয়, এটি ব্যবহারকারীর অ্যাডারে স্পেসে 16 কে অনুলিপি করতে হবে তা উল্লেখ না করে। সুতরাং আপনি পৃষ্ঠা সারণীতে অপারেশনের পার্থক্যগুলি বিস্তারিতভাবে বর্ণনা করতে পারেন এবং এটি আরও ব্যয়বহুল কীভাবে mmap?
ফ্লো 2 কে

45

mmapযখন আপনার বড় ফাইলগুলিতে এলোমেলো অ্যাক্সেস থাকে তখন সুবিধা হয়। আর একটি সুবিধা হ'ল আপনি বাফারিংয়ের সাথে বিরক্ত না করে মেমরি অপারেশনগুলি (মেমকিপি, পয়েন্টার পাটিগণিত) দিয়ে এটি অ্যাক্সেস করতে পারেন। আপনার বাফারের চেয়ে কাঠামো বড় হওয়ার সাথে সাথে সাধারণ আই / ও কখনও কখনও বাফার ব্যবহার করার সময় বেশ কঠিন হতে পারে। হ্যান্ডেল করার কোডটি প্রায়শই সঠিকভাবে পাওয়া মুশকিল, এমএম্যাপটি সাধারণত সহজ। এটি বলেছে, সাথে কাজ করার সময় কিছু নির্দিষ্ট ফাঁদ রয়েছে mmap। লোকেরা ইতিমধ্যে উল্লেখ করেছে, mmapসেট আপ করা বেশ ব্যয়বহুল, সুতরাং এটি কেবলমাত্র একটি নির্দিষ্ট আকারের (মেশিন থেকে মেশিনে পরিবর্তিত) জন্য ব্যবহার করা উপযুক্ত।

ফাইলটিতে খাঁটি অনুক্রমিক প্রবেশাধিকারের জন্য, এটি সর্বদা আরও ভাল সমাধান নয়, যদিও madviseসমস্যাটি প্রশমিত করতে একটি উপযুক্ত কল call

আপনার আর্কিটেকচারের প্রান্তিককরণ বিধিনিষেধের (এসএআরসি, ইটানিয়াম) সাবধানতা অবলম্বন করতে হবে, পড়ুন / লিখুন আইও বাফারগুলি প্রায়শই সঠিকভাবে প্রান্তিক থাকে এবং কোনও কাস্টেড পয়েন্টারকে ডিফারেন্স করার সময় ফাঁদে পড়ে না।

মানচিত্রে বাইরের অ্যাক্সেস না রাখার বিষয়ে আপনাকেও যত্নবান হতে হবে। এটি সহজেই ঘটতে পারে যদি আপনি নিজের মানচিত্রে স্ট্রিং ফাংশন ব্যবহার করেন এবং আপনার ফাইলটির শেষে একটি at 0 থাকে না। এটি আপনার বেশিরভাগ সময় কাজ করবে যখন আপনার ফাইলের আকারটি পৃষ্ঠার আকারের একাধিক না হয় কারণ শেষ পৃষ্ঠাটি 0 টি ভরাট হয় (ম্যাপ করা অঞ্চলটি আপনার পৃষ্ঠার আকারের একাধিক আকারে সর্বদা থাকে)।


30

অন্যান্য দুর্দান্ত উত্তরের পাশাপাশি গুগলের বিশেষজ্ঞ রবার্ট লাভের লেখা লিনাক্স সিস্টেম প্রোগ্রামিংয়ের একটি উদ্ধৃতি :

এর সুবিধা mmap( )

mmap( )স্ট্যান্ডার্ড read( )এবং write( )সিস্টেম কলগুলির মাধ্যমে ফাইলগুলি ম্যানিপুলেট করার কয়েকটি সুবিধা রয়েছে । এর মধ্যে হ'ল:

  • মেমরি-ম্যাপযুক্ত ফাইলটি থেকে পড়া এবং লেখার মাধ্যমে read( )বা write( )সিস্টেম কলগুলি ব্যবহার করার সময় ঘটে যাওয়া বাহ্যিক অনুলিপি এড়ানো হয় , যেখানে ব্যবহারকারী-স্পেস বাফারটিতে ডেটা অনুলিপি করতে হবে।

  • কোনও সম্ভাব্য পৃষ্ঠা ত্রুটিগুলি বাদ দিয়ে, মেমরি-ম্যাপযুক্ত ফাইল থেকে পাঠানো এবং লেখার জন্য কোনও সিস্টেম কল বা কনটেক্সট স্যুইচ ওভারহেড আসে না। এটি মেমরি অ্যাক্সেস হিসাবে সহজ।

  • যখন একাধিক প্রক্রিয়া একই বস্তুকে মেমরিতে ম্যাপ করে, তখন সমস্ত প্রক্রিয়াগুলির মধ্যে ডেটা ভাগ করা হয়। কেবলমাত্র পঠনযোগ্য এবং ভাগ করা লিখিত ম্যাপিংগুলি তাদের সম্পূর্ণরূপে ভাগ করা হয়; ব্যক্তিগত লিখনযোগ্য ম্যাপিংগুলিতে তাদের এখনও নেই এমন COW (অনুলিপি-অন-লিখন) পৃষ্ঠা ভাগ করা আছে।

  • ম্যাপিংয়ের আশেপাশে তুচ্ছ পয়েন্টার ম্যানিপুলেশন জড়িত। lseek( )সিস্টেম কল করার প্রয়োজন নেই ।

এই কারণে, mmap( )অনেক অ্যাপ্লিকেশন জন্য একটি স্মার্ট পছন্দ।

এর অসুবিধাগুলি mmap( )

ব্যবহার করার সময় কিছু বিষয় মনে রাখতে হবে mmap( ):

  • মেমরি ম্যাপিংগুলি সর্বদা আকারের পৃষ্ঠাগুলির সংখ্যার সংখ্যা হয়। সুতরাং, ব্যাকিং ফাইলের আকার এবং পৃষ্ঠাগুলির একটি পূর্ণসংখ্যার সংখ্যার মধ্যে পার্থক্যটি স্ল্যাক স্পেস হিসাবে "নষ্ট" হয়। ছোট ফাইলগুলির জন্য, ম্যাপিংয়ের একটি উল্লেখযোগ্য শতাংশ নষ্ট হতে পারে। উদাহরণস্বরূপ, 4 কেবি পৃষ্ঠা সহ, 7 বাইট ম্যাপিং 4,089 বাইট অপচয় করে tes

  • মেমরি ম্যাপিংগুলি অবশ্যই প্রক্রিয়াটির ঠিকানার জায়গায় ফিট করে fit ৩২-বিট ঠিকানার জায়গার সাহায্যে, বিশাল আকারের বিভিন্ন ম্যাপিংয়ের ফলে অ্যাড্রেস স্পেসটি টুকরো টুকরো হয়ে যায়, ফলে বৃহত মুক্ত সাবলীল অঞ্চলগুলি খুঁজে পাওয়া শক্ত হয়ে যায়। এই সমস্যাটি অবশ্যই 64৪-বিট ঠিকানার জায়গার সাথে খুব কম স্পষ্ট।

  • কার্নেলের অভ্যন্তরে মেমরি ম্যাপিং এবং সম্পর্কিত ডেটা স্ট্রাকচার তৈরি এবং রক্ষণাবেক্ষণের ক্ষেত্রে ওভারহেড রয়েছে। এই ওভারহেডটি সাধারণত পূর্ববর্তী বিভাগে উল্লিখিত ডাবল অনুলিপি মুছে ফেলা হয়, বিশেষত বৃহত্তর এবং প্রায়শই অ্যাক্সেস করা ফাইলগুলির জন্য।

এই কারণগুলির জন্য, mmap( )ম্যাপযুক্ত ফাইলটি বড় হলে (এবং সুতরাং কোনও নষ্ট স্থান মোট ম্যাপিংয়ের একটি ছোট শতাংশ) বা ম্যাপ করা ফাইলের মোট আকার পৃষ্ঠার আকারের দ্বারা সমানভাবে বিভাজ্য হলে এর সুবিধাগুলি সর্বাধিক উপলব্ধি করা যায় ( এবং এইভাবে কোনও নষ্ট স্থান নেই)।


13

Traditionalতিহ্যবাহী আইওয়ের তুলনায় মেমোরি ম্যাপিংয়ের বিশাল গতির সুবিধার সম্ভাবনা রয়েছে। মেমরি ম্যাপ করা ফাইলের পৃষ্ঠাগুলি স্পর্শ করার সাথে সাথে এটি অপারেটিং সিস্টেমটিকে উত্স ফাইল থেকে ডেটা পড়তে দেয়। এটি ত্রুটিযুক্ত পৃষ্ঠাগুলি তৈরি করে কাজ করে যা ওএস সনাক্ত করে এবং তারপরে ওএস ফাইল থেকে সংশ্লিষ্ট ডেটা স্বয়ংক্রিয়ভাবে লোড করে।

এটি পেজিং প্রক্রিয়া হিসাবে একইভাবে কাজ করে এবং সাধারণত সিস্টেম পৃষ্ঠার সীমানা এবং আকারগুলির (সাধারণত 4 কে) ডেটা পড়ে উচ্চ গতির আই / ও এর জন্য অনুকূলিত হয় - এমন একটি আকার যার জন্য বেশিরভাগ ফাইল সিস্টেম ক্যাশে অনুকূলিত হয়।


15
নোট করুন যে এমএম্যাপ () সর্বদা পড়ার চেয়ে দ্রুত হয় না ()। অনুক্রমিক পাঠগুলির জন্য, এমএম্যাপ () আপনাকে কোনও পরিমাপযোগ্য সুবিধা দেবে না - এটি অনুগত এবং তাত্ত্বিক প্রমাণের ভিত্তিতে। আপনি যদি আমাকে বিশ্বাস না করেন তবে নিজের পরীক্ষা লিখুন।
টিম কুপার

1
আমি আমাদের প্রকল্প থেকে আগত নম্বরগুলি দিতে পারি, একটি বাক্যাংশের ডাটাবেসের জন্য এক ধরণের পাঠ্য সূচক। সূচকটি বেশ কয়েকটি গিগাবাইট বড় এবং কীগুলি একটি টের্নারি গাছে রাখা হয়। পাঠ্য অ্যাক্সেসের সমান্তরালে সূচকটি এখনও বাড়ছে, ম্যাপ করা অংশগুলির বাইরে অ্যাক্সেসের মাধ্যমে প্রবেশ করা যাবে pread। সোলারিস 9 স্পার্ক (ভি 890) এ প্রিড অ্যাক্সেস এমএমএপ থেকে 2 থেকে 3 গুণ কম ধীর হয় memcpy। তবে আপনি ঠিক বলেছেন যে ক্রমবর্ধমান অ্যাক্সেস অগত্যা দ্রুত নয়।
প্যাট্রিক Schlüter

19
একটু নিটপিক। এটি পেজিং পদ্ধতির মতো কাজ করে না, এটি পেজিং প্রক্রিয়া। একটি ফাইল ম্যাপিং অজ্ঞাত অদলবদল ফাইলের পরিবর্তে কোনও ফাইলকে একটি মেমরি অঞ্চল বরাদ্দ করা হয়।
প্যাট্রিক Schlüter

2

একটি সুবিধা যা এখনও তালিকাভুক্ত নয় তা হ'ল ক্ষমতা mmap() পরিষ্কার পৃষ্ঠাগুলি হিসাবে কেবল পঠনযোগ্য ম্যাপিং রাখার । যদি কেউ প্রক্রিয়াটির ঠিকানার জায়গায় বাফার বরাদ্দ করে, তবে read()কোনও ফাইল থেকে বাফারটি পূরণ করতে ব্যবহার করে, সেই বাফারের সাথে সম্পর্কিত মেমরি পৃষ্ঠাগুলি এখন নোংরা লিখিত হওয়ার পরে ।

দুর্গন্ধ পৃষ্ঠাগুলি কার্নেল দ্বারা র‌্যাম থেকে বাদ দেওয়া যায় না। যদি অদলবদল স্থান থাকে তবে সেগুলি অদলবদল করার জন্য পেজ আউট করা যায়। তবে এটি ব্যয়বহুল এবং কিছু সিস্টেমে যেমন কেবলমাত্র ফ্ল্যাশ মেমরির সাথে থাকা ছোট এমবেডেড ডিভাইসগুলিতে কোনও অদলবদল হয় না। সেক্ষেত্রে প্রক্রিয়াটি শেষ না হওয়া পর্যন্ত বাফারটি এটিকে ফিরে না দেওয়া পর্যন্ত রাফটিতে আটকে থাকবেmadvise()

অন ​​লিখিত mmap()পৃষ্ঠাগুলিতে পরিষ্কার। কার্নেলটির যদি র‌্যামের প্রয়োজন হয় তবে এটি কেবল এগুলি ফেলে দিতে পারে এবং পৃষ্ঠাগুলি যে র‌্যাম ব্যবহার করত তা ব্যবহার করতে পারে the । একইভাবে তারা প্রথম স্থানে জনবহুল ছিল।

সুবিধার্থে ম্যাপ করা ফাইলটি ব্যবহার করে এটির জন্য একাধিক প্রক্রিয়া প্রয়োজন হয় না।


কার্নেলটি প্রথমে অন্তর্নিহিত ফাইলটিতে এর লিখিত সামগ্রী লিখে কোনও 'নোংরা' এমএমপড পৃষ্ঠাটি ফেলে দিতে পারে না?
জেরেমি ফ্রাইজনার

2
ব্যবহার করার সময় read() , পৃষ্ঠাগুলি যে ডেটাগুলি অবশেষে অন্তর্ভুক্ত করা হয় সেগুলির ফাইলের সাথে তাদের কোনও সম্পর্ক নেই। সুতরাং এগুলি স্থান অদলবদল করা ছাড়া আর লেখা যায় না। যদি একটি ফাইল mmap()ed(যেমন শুধুমাত্র পাঠযোগ্য থেকে ভিন্ন), এবং লিখিত, এবং ম্যাপিং লিখনযোগ্য হয়, তাহলে এটা ম্যাপিং ছিল কিনা নির্ভর করে MAP_SHAREDবা MAP_PRIVATE। একটি ভাগ করা ম্যাপিং ফাইলটিতে অবশ্যই লেখা যেতে পারে / তবে ব্যক্তিগত হতে পারে না।
ট্রেন্টপি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.