<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های هادی اعظمی</title>
        <link>https://virgool.io/feed/@itshaadi</link>
        <description>توسعه دهنده نرم افزار</description>
        <language>fa</language>
        <pubDate>2026-06-09 01:47:05</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/3678/avatar/mrXvWT.png?height=120&amp;width=120</url>
            <title>هادی اعظمی</title>
            <link>https://virgool.io/@itshaadi</link>
        </image>

                    <item>
                <title>شرح پیاده سازی یک سرویس IaP</title>
                <link>https://virgool.io/@itshaadi/how-to-develop-iap-untfi7e2tker</link>
                <description>توی این مطلب می‌خوام درمورد پیاده سازی یک Identity and Access Proxy صحبت کنم. که برای مطابقت دادن الگوی یک مسیر؛ از ساختمان داده Trie استفاده می‌کنه. و برای mutate کردن، از مفهوم pipeline. که بر اساس الگوی Chain-of-responsibility پیاده سازی شده. بهتره که با تعریف چندتا مفهوم شروع کنم.فرض رو بر این بگیرید که سرویس‌ما قراره به زبان جاوا اسکریپت نوشته بشه؛ من مثال‌های این مطلب رو با تایپ اسکریپت نوشتم که درک موضوع با خوندن کد همراه باشه.اصلا این IaP چی‌هست؟اجازه بدید اینطوری توضیح بدم:فرض کنید یه همچین درخواستی اومده سمت سرور شماGET /my-service/whatever HTTP/1.1
Authorization: bearer some-tokenچه خوب می‌شه اگه بتونید دسترسی به این مسیر رو بر اساس توکنی که دریافت شده اعتبار سنجی کنید‌ نه؟ مثلا اگه توکن برای کاربر admin نبود دسترسی مجاز نیست.یا فرض کنید آدرس صفحه پرداخت باید فقط برای کاربران ایرانی نمایش داده بشه:POST /my-service/payment HTTP/1.1
X-Forwarded-For: 83.123.255.56اگه IP ایران نداشتن بهشون یک پیغام نشون بده که جلوتر نرن.درواقع همه این مثالها نیازمند داشتن یک نقطه اجرای سیاست (Policy Enforcement Point) هست. یعنی یک سرویس که به عنوان نقطه ورود معرفی می‌شه (البته بعد از لود بالانسر و reverse proxy) و هدفش اینه که مطمین بشه آدرسی که ازش بازدید می‌شه توسط اون کاربر مجاز هست. و یا یک سری پیشفرض درمورد کاربر استخراج کنه و اونها رو به درخواستش اضافه کنه (Mutation) که سرویس‌هایی که در باقی مسیر قرار دارن با توجه به اون اطلاعات فرایند بعدی رو انجام بدن.مثلا؛ این سرویس می‌تونه اطلاعات کاربر رو از دیتابیس استخراج کنه و باقی سرویس‌ها در ادامه مسیر نیازی به استخراج مجدد ندارن.چنین ابزاری معمولا درخواست رو از یک reverse proxy دریافت می‌کنه مثلا اگر از NGINX استفاده می‌کنید وقتی که درخواست بهش برسه، به این طریق برای سرویس شما ارسال می‌کنه. یا اگه مثل من از Traefik استفاده می‌کنید باید از Forward Authentication Middleware استفاده کنید. یعنی درواقع یک درخواست به این سرویس Forward می‌شه. اگر از سمت سرویس IaP نتیجه 200 برگشت داده بشه اون درخواست به مقصد بعدی منتقل می‌شه. و اگر هر چیزی بجز 200 باشه یعنی خطایی رخ داده و نتیجه رو به کاربر برگشت می‌ده. نحوه انجام Forward Auth توسط Traefikسرویس IaP معمولا نتیجه پردازش‌های خودش رو به صورت یک یا چند هدر به درخواستی که دریافت کرده اضافه می‌کنه.وقتی که از Reverse Proxy استفاده می‌کنیم. یک سری context از بین می‌ره ... مثلا آدرسی که به دست ما میرسه آدرس اون برنامه هست نه کاربری که درخواست رو زده. برای حل این موضوع rfc7239 رو تعریف کردن. یعنی درواقع یک سری هدر به درخواست‌ها اضافه می‌شه توسط ابزارهایی مثل NGINX و Traefik که با X-Forwarded شروع می‌شه. که X یعنی این یک هدر کاستوم هست که جز استاندارد HTTP نیست. و Forwarded بعدش هم یعنی اینکه این هدر از خانواده Forwarded HTTP Extension هست.با استفاده از Traefik این هدر‌ها رو دریافت می‌کنیم:X-Forwarded-Proto
X-Forwarded-Host
X-Forwarded-Uri
X-Forwarded-For
X-Forwarded-Methodسرویس IaP معمولا از سه مورد آخر استفاده می‌کنه. که بفهمه مسیر کجاست؛ متدی که درخواست زدن چیه و Source IP متعلق به چه آدرسی هست.پیاده سازیحالا برای حل کردن فرضیات بالا چه کدی می‌نویسید؟const uri = req.headers[&amp;quotx-forwarded-for&amp;quot]
const method = req.headers[&amp;quotx-forwarded-method&amp;quot]
if (uri === &amp;quot/my-service/whatever&amp;quot &amp;&amp; method === &amp;quotGET&amp;quot) {
     doStuff()
}هووووف؛ اگه به همین سادگی بود! نه!. آدرسها قسمت‌های مختلف دارن... اگر یه قسمت از آدرس داینامیک بود چطوری می‌خوای تشخیصش بدی؟ مثلا اگه UUID بود:GET /my-service/posts/69df78f4-f675-4838-8d69-664c56ed8543خب کاری نداره که!.. از regular expression استفاده می‌کنیم!... بازم نهههه!. البته می‌دونم بعضی از پیاده سازی‌ها ممکنه یک Regex Engine داشته باشن اما این نصیحت رو از من بپذیرید ... «همیشه عبارات با قاعده رو به عنوان آخرین راه حل استفاده کنید». درسته که ما داریم string matching انجام می‌دیم. اما دنبال «یک جواب مشخص» هستیم. معمولا موتوری که عبارات با قاعده رو تفسیر می‌کنه «به هر دری می‌زنه تا به جواب برسه» یعنی مسیر‌های زیادی برای اجرای یک عبارت باقاعده وجود داره. بعلاوه آدم‌ها می‌تونن با عبارات باقاعده کرم بریزن. در نتیجه پردازش کردن regex نیازمند مفسر خودش هست. حتی بر اساس پیچیدگی بعضی از عبارات؛ ممکنه توسط JIT-less V8 تفسیر بشن که این جهش بین دنیای ++c و جاوا اسکریپت هزینه داره.اگه رشته‌هایی که پردازش می‌کنید پیچیده هستن و راه‌های دیگه مثل Trie یا Bloom filter کافی نیست. اونوقت بهتره که از یک Regex Engine استفاده کنید.مطابقت دادن آدرس با یک الگوبه کاری که سرویس IaP در این مرحله انجام می‌ده Access Rule Matching گفته می‌شه. یعنی مطابقت دادن قسمت‌های یک آدرس با یک رشته از پیش تعریف شده. برای انجام این عملیات باید یک موتور تصمیم‌گیری نوشته بشه. یعنی وقتی که این آدرس دریافت می‌شه:GET /my-service/posts/69df78f4-f675-4838-8d69-664c56ed8543
pattern: get*my-service*posts*{uuid}الگویی که با اون مطابقت داره رو پیدا می‌کنه. به قسمت آخر نگاه کنید که نوشته uuid این یک شرط هست. وظیفه Decision Engine اینه که قسمت‌های داینامیک یک الگو، مثل شرط‌ها رو به یک عبارت تفسیر کنه که نتیجه اش True می‌شه.حالا بذارید اینطوری بهش نگاه کنیم؛ همه قسمت‌های این مسیر استاتیک هست بجز تیکه آخر. می‌تونیم این قسمت‌های داینامیک رو جدا کنیم و براشون یک تابع بنویسیم. مگر یک آدرس چقدر مقدار داینامیک توش استفاده می‌شه؟ معمولا کمه. حتی اگه عبارت خیلی پیچیده باشه؛ می‌تونیم یک تابع تعریف کنیم که یک عبارت regex رو اجرا می‌کنه مزیتش اینه که عبارتمون نیاز نداره تمام قسمت‌های استاتیک رو شامل بشه.درواقع به ازای هر مفهوم داینامیک یک تابع تعریف می‌کنیم  بعد مقدار داینامیک رو برای اون توابع می‌فرستیم. جواب که True شد یعنی الگوی مطابق این آدرس پیدا شده و اون مسیر مجاز هست.حالا به این مثال توجه کنید:GET /my-service/posts/all
GET /my-service/posts/69df78f4-f675-4838-8d69-664c56ed8543
GET /my-service/posts/69df78f4-f675-4838-8d69-664c56ed8543/comments
DELETE /my-service/posts/69df78f4-f675-4838-8d69-664c56ed8543
POST /my-service/posts/createنقطه مشترک قضیه چیه؟ GET /my-service/posts داره تکرار می‌شه. درواقع اگر توی سرویس‌هاتون از REST پیروی می‌کنید؛ تعداد قسمت‌های تکراری توی یک آدرس زیاده. این دردسر ساز تره تا قسمت‌های داینامیک. ما نیاز داریم این رشته رو توی حافظه به شکلی ذخیره کنیم که با تغییر دادن یک قسمت؛ مفسر اون رو به عنوان یک رشته جدید در نظر نگیره. که تصمیم نگیره دوباره براش حافظه بسازه.چرا؟ مگه حافظه ارث بابامونه؟ نه!. چون طول رشته ثابت نیست پس روی Heap ذخیره می‌شه. اگر کاری کنیم که طولش ثابت به نظر بیاد؛ دسترسی به Heap یک الگوی قابل تکرار پیدا می‌کنه. و هرچقدر درسترسی به اون قسمت از حافظه بیشتر اتفاق بیوفته داغ تر می‌شه. در نتیجه توسط runtime کش می‌شه.استفاده از Trie برای مطابقت الگو‌هاترای؛ یک ساختمان داده هست که معمولا برای پیش‌بینی متن یا اعتبارسنجی یک رشته ازش استفاده می‌کنن. این ویدیو رو نگاه کنید.مثال جستجو در دیکشنری با Trieمعمولا؛ انبار این ساختمان داده یک Hash table هست که با داده‌هایی که قراره جستجو بشه پر میشه. یعنی ما همیشه برای رسیدن به جواب «یک راه مشخص» داریم. (درحالی که تفسیر و پیمایش عبارات باقاعده هم slow path داره هم fast path).کاراکتر C یک کلید هست توی Hash table در نتیجه پیچیدگی زمانی برای دسترسی به C معادل O(1) هست و در بدترین حالت O(n) (بستگی داره اون Hash function که استفاده شده چقدر خوب عمل می‌کنه). همچنین پیمایش Trie همیشه O(m) هست. به طوری که M تعداد Node‌های پیمایش شده برای رسیدن به نتیجه هست. مثلا برای جستجوی CAR مقدار M می‌شه 3.مهمتر از همه؛ پیمایش فقط وقتی ادامه پیدا می‌کنه که Node پیدا شده. یعنی اگر CARD READER رو جستجو کنید پیمایش فقط تا CARD ادامه پیدا می‌کنه چون توی لیستی که بهش دادیم نیست. این خیلی برای یک IaP مهمه. چرا؟ چون خزنده‌ها اینترنت رو تسخیر کردن! اگه یک خزنده سعی داشته باشه آدرس‌های مختلف رو امتحان کنه پیمایش اون به محض رسیدن به اولین گره‌ای که وجود نداره متوقف می‌شه.و با اعمال rate limiting بر اساس دفعاتی که 404 اتفاق افتاده، حتی می‌تونید کسانی که با Residential IP کرم می‌ریزند رو بعنوان خزنده درنظر بگیرید. (مگر درخواست‌های واقعی از یک IP در یک بازه زمانی کوتاه؛ چندبار به 404 می‌خوره؟ یک بار؟ دو بار؟ ده بار؟ ... بیست بار که دیگه عادی نیست).به عبارات CAr, CAt, CArd دقت کنید ... یعنی ما می‌تونیم C رو یک گره فرض کنیم که فرزندش می‌شه A و فرزندان A میشن R,T و در نهایت D فرزند R در نظر گرفته می‌شه. در نتیجه: الگوی پیمایش قابل تکرار هست.اما هنوزم یک مشکل اساسی داریم ... UUID که یک عبارت ثابت نیست. یعنی:get*my-service*posts*{uuid}رو چطوری توی یک Trie ذخیره کنیم؟ - توی جاوا اسکریپت چی با Hash table پیاده شده؟ Map - خب ما می‌تونیم رشته &quot;{uuid}&quot; رو بعنوان یک کلید ذخیره کنیم. مقدار هر کلید می‌تونه یک Object باشه که یک پراپرتی داره تحت عنوان isCondition.چطوری بفهمیم uuid یک شرط هست؟نحوه پیمایش یک مسیرخب موقعی که داریم انبار رو پر می‌کنیم؛ اول روی کاراکتر ستاره split انجام می‌دیم. بعد روی {} یک split دیگه انجام می‌دیم:const condition = segment.split(&amp;quot{&amp;quot).pop()?.split(&amp;quot}&amp;quot)[0] // {uuid} -&gt; uuidاگر حاصل این عبارت نتیجه داشت؛ پس این گره بعنوان شرط ثبت می‌شه.اما یک مشکل دیگه داریم. وقتی که یک گره پیدا نشه پیمایش متوقف می‌شه. پس چطوری {uuid} رو پیمایش کنیم؟ - کافیه از آخرین گره پیمایش شده بخوایم که گره بعدی خودش رو برگشت بده. بعد می‌تونیم پراپرتی isCondition رو چک کنیم. یادتونه گفتم به ازای هر قسمت داینامیک یه فانکشن داریم؟ خب می‌تونیم به این صورت اونها رو تعریف کنیم.const conditionMap = {
uuid: (s) =&gt; s.length &gt;= 21 &amp;&amp; s.length &lt;= 36,
}
conditionMap[condition]() // true || falseاعمال Mutation با استفاده از Pipelineقدم بعدی در سرویس IaP اعمال پردازش روی درخواست‌ها و ایجاد تغییر روی اونهاست. البته معمولا یک مرحله دیگه قبل از mutation اتفاق می‌افته. که Authorization هست اما؛ اگر قراره نتیجه اون عملیات هم روی درخواست تغییر ایجاد کنه می‌تونه یک mutation به حساب بیاد.خب، ما قراره یک سری داده از Request بخونیم و یک سری هدر به Response اضافه کنیم البته تغییرات می‌تونه هرکجای Response اتفاق بیوفته اما عموما توی هدر عاقلانه تره.آیا می‌تونیم از این فرایند یک الگو درست کنیم؟ بله.type IAPPipeline = (req: Request, res: Response) =&gt; Promise&lt;void&gt;;درواقع پایپلاین در اینجا یک تابع انتخابی هست که قراره req رو دریافت کنه و روی res تغییر ایجاد کنه. اما این تعریف تکمیل نیست اگر ما دوتا تابع داشته باشیم باید همون req, res که به تابع اول رسیده به تابع دوم هم پاس داده بشه. اونوقت تبدیل میشه به یک pipeline.حالا یه مشکل دیگه داریم. اگر A خطا بده چه تضمینی هست که B می‌تونه به کارش ادامه بده یا نه؟ درواقع؛ بهتره که ما یک مدل اجرا داشته باشیم. اینجاست که می‌تونیم از Chain-of-responsibility استفاده کنیم.‍‍interface ExecutionUnit {
next: ExecutionUnit | null;
pipeline: IAPPipeline;
setNext(next: ExecutionUnit): void;
exec&#40;req: Request, res: Response&#41;: Promise&lt;void | never&gt;;
}هر pipeline، باید تبدیل بشه یک «واحد اجرا». هر واحد اجرا دو ویژگی داره:با فراخوانی متد setNext می‌تونیم به واحد اجرای بعدی اشاره کنیم (درنتیجه req, res رو به تابع بعدی پاس بدیم).توی بدنه متد exec می‌تونیم نحوه اجرای یک pipeline رو توصیف کنیم. https://gist.github.com/itshaadi/bdff24da523f118a19eb2a86165d8940 بعنوان مثال حاصل اجرا شدن یک Pipeline که واحد اجرای اون از نوع CanFail هست منجر به متوقف شدن زنجیر نمی‌شه بلکه خطاها رو لاگ می‌کنه و به اجرای واحد بعدی ادامه می‌ده.درعین حال می‌تونیم یک تعریف CanPanic هم داشته باشیم که خطاها رو catch نمی‌کنه. در نتیجه کدی که این چرخه رو اجرا کرده باید خطا رو مدیریت کنه؛ یا مثلا آخرین Middleware می‌تونه همه خطاها رو مدیریت کنه. و مثلا HTTP 500 برگشت بده. https://gist.github.com/itshaadi/395b5ee29c0cd5d4da1dc220b6c3fc7e و حالا باید یک Executor داشته باشیم که یک لیست از Execution Unit رو دریافت می‌کنه، و به همدیگه لینک می‌کنه. بعدش اولین عضو توی زنجیر رو اجرا می‌کنیم. و به ترتیب هر واحد اجرا، واحد بعدی خودش رو فراخوانی می‌کنه. و اگر این وسط یکی از واحد‌ها CanPanic باشه اجرا متوقف می‌شه. درغیر این صورت Mutation‌ها به ترتیبی که توی آرایه تعریف شدن اتفاق میوفتن. https://gist.github.com/itshaadi/d8e4f5db8ae0ad1344b271741d5c8f98 کلام پایانیقطعا پیاده سازی IaP به این موارد ختم نمیشه و این رشته سر درازی داره. بعنوان مثال اگر آدرس شامل query string بود چطوری تفسیرش کنیم؟در ابتدا قصد داشتم کدهای مرتبط با این مفاهیم رو توی همین مطلب درج کنم. اما ویرایشگر ویرگول سوهان روح است. برای همین بزودی یک Proof of concept روی حساب گیت‌هاب خودم منتشر می‌کنم. شامل تنظیمات Traefik و یک پروژه express که همه این مفاهیم توی اون پیاده سازی شده و می‌تونید به صورت داکرایز شده اجرا کنید. هرموقع که منتشر بشه از طریق توییتر اطلاع رسانی می‌کنم. امیدوارم از مطالعه این مطلب لذت برده باشید. همونطور که من از نوشتنش لذت بردم ?</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Fri, 08 Jan 2021 01:53:08 +0330</pubDate>
            </item>
                    <item>
                <title>معرفی BPF: قدرت واقعی لینوکس</title>
                <link>https://virgool.io/@itshaadi/introducing-bpf-ldtfwkmfdt9k</link>
                <description>توی توسعه نرم‌افزار، سریع بودن بزرگ‌ترین دغدغه بین افراد دخیل در فرایند هست. از مدیر پروژه که فقط می‌خواد برنامه سریع باشه تا مدیرفنی که دنبال راه‌های جدید برای سرعت بخشیدن به زیرساخت‌هاست. «سریع» یه حس مشترکه.تاحالا به این فکر کردین مرز سریع بودن کجاست؟ مثلا الان بحث میکروسرویس‌ها دوباره داغ شده. ظاهرا؛ وقتی به بقیه قول می‌دی که قراره برنامه شون رو سریع اجرا کنی خودت به یه چیز خیلی سریعتر نیاز داری تا وضعیت رو بسنجی... چطوری می‌شه یک وضعیت رو خیلی سریع سنجید؟ترسیم مرز سریع بودن؛ بدون فرض گرفتن یک context غیر ممکنه. مثلا مرز سریع بودن یه دونده توان جسمانیش هست. ولی مرز سریع بودن توی برنامه‌نویسی، صرفا توان بالای سخت افزار نیست! «چطوری دستورات به سخت افزار می‌رسن» خیلی مهمه.اگه برنامه توی حلقه صفر اجرا بشه یعنی سریع ترین نقطه دسترسی به سخت افزار. و توی لینوکس این کار با نوشتن یک ماژول اختصاصی انجام می‌شه.ولی اجرا شدن توی حلقه صفر یعنی کسی نیست که جلوی اشتباهات رو بگیره. اگه توی کد‌ها مشکلی پیش بیاد کرنل کلا کرش می‌کنه. همه چیز متوقف می‌شه. تازه این اتفاق اگه بیوفته باید خدا رو شکر کنی؛ چون اگه کرش نکرد احتمال اینکه کدی که نوشتی بدون سروصدا داده‌ها رو خراب کنه خیلی زیاده.پس مرز سریع بودن توی توسعه نرم‌افزار یعنی تا هر جایی که از چهارچوب «صحیح بودن» خارج نشه و BPF در یک کلام؛ تکنولوژی‌‌ای برای تضمین صحیح بودن و سرعته. تضمین صحیح بودنماشین مجازی تکنیکی برای نوشتن برنامه‌های سریع با تضمین صحیح بودن هست. (یه مدلش رو توی مطلب قبلیم درباره وب‌ اسمبلی توضیح دادم). این دوتا مثال رو مقایسه کنید:--------- WebAssembly bytecodes
(i32.add
   (i32.const 1)
   (i32.const 2)
)
--------- BPF bytecodes
ldh      [12]
jeq      #0x86dd     jt 2    jf 7
jeq      #0x6           jt 10  jf 4
jeq      #0x2c         jt 5    jf 11
ldb      [54]
jeq      #0x6           jt 10   jf 11
jeq      #0x800       jt 8     jf 11
ldb      [23]
jeq      #0x6           jt 10    jf 11
ret      #65535
ret      #0ویژگی ماشینهای مجازی:یک بیان متنی از بایت کد دارن (اولی به سبک s-expression و دومی به سبک x86 Instruction).بایت کدشون به اندازه کافی انتزاعی هست که یک انسان بتونه بخونه و بنویسه و در عین حال یک ماشین با کمترین هزینه ترجمه کنه.بایت کد Generic تحویل می‌گیرن و کد ماشین تولید می‌کنن.صحت و سقم برنامه درحال اجرا رو با بررسی بایت کد ورودی تضمین می‌کنن.نکته اول: خوبی بایت کد Generic اینه که فقط برای یک ماشین خاص نیست در نتیجه می‌شه یک قطعه کد توی هر زبانی رو با بالا ترین سرعت ممکن اجرا کرد. فقط کافیه که اون زبان یک کتابخانه داشته باشه که برای دستوراتش معادل بایت کد یک ماشین مجازی رو تولید کنه.نکته دوم: توی یک ماشین مجازی برنامه‌ها از دنیای بیرون خبر ندارن هر دسترسی که لازم داشته باشن براشون شبیه سازی می‌شه.نکته) BPF ماشین مجازی نیست.اجازه بدید با این سوال از BPF Design Q&amp;A شروع کنم به توضیح:Q: Is BPF a generic virtual machine ?A: NO.BPF is generic instruction set with C calling convention.یعنی یک سری دستور Generic هستن ولی بایت کد تولید شده از &quot;قرارداد فراخوانی زبان سی&quot; پیروی می‌کنه. این یعنی بی‌پی‌اف یک bytecode engine هست. به این نقل قول هم نگاه کنید تا فرقش با ماشین مجازی رو بگم:As a general rule, a function which follows the C calling conventions, and is appropriately declared (see below) in the C headers,  can be called as a normal C function. Most of the burden for following the calling rules falls upon the assembly program.الف) موتور بایت‌کد و ماشین مجازی در نگاه اجمالی شباهت زیادی دارن به طوری که همیشه از هردو به معنی یک چیز یاد می‌شه. اما نقل قول اول یک اشاره ریز داره - &quot;قرارداد فراخوانی زبان سی&quot; یعنی؛ موتور بایت‌کد می‌دونه که قراره توی چه محیطی اجرا بشه (ولی ماشین مجازی نمی‌دونه).ب) موتور بایت‌کد؛ بخش کوچیک و کنترل شده‌ای درون یک سیستم بزرگتر‌ هست. برای همین باید از قراردادهای سیستم بزرگتر پیروی کنه (BPF باید از قرارداد فراخوانی زبان سی پیروی کنه چون لینوکس به زبان سی نوشته شده) که باعث می‌شه در زمان اجرا با سیستم بزرگتر ادغام بشه یعنی هرگونه فراخوانی و ارتباط از برنامه BPF به کرنل و برعکس بدون هزینه هست.ساختار برنامه‌های BPFساختار BPF از یک دید اجمالیفهمیدیم که BPF موقع اجرا جزئی از کرنل می‌شه.و باید ابزاری باشه که کد سی رو تبدیل کنه به بایت‌کد. (که وظیفه LLVM هست) اما اون بایت‌کد خودش که بال در نمیاره یکدفعه بشینه توی کرنل باید به یک شکلی لود بشه. اولین سوالی که پیش میاد اینه که برنامه رو چطوری داخل کرنل لود کنیم؟برای جواب دادن به سوال اول بیاید به Front-end و Back-end همین سایت فکر کنیم. برای نظر دادن توی این پست، پایین صفحه یک فرم هست. حرفهای شما رو می‌فرسته به سرور ویرگول. اینجا نظر شما مصداق ELF Object رو داره و فرم ارسال مصداق یک برنامه داخل User Space.پس برنامه‌های BPF در اصل دو قسمتی هستن یک قسمت سمت کاربر اجرا می‌شه که حداقل وظیفه‌اش لود کردن قسمت دوم هست. و قسمت دوم می‌شه برنامه اصلی که داخل کرنل اجرا می‌شه.TC-BPF Workflowاگه هدف فقط لود کردن برنامه اصلی باشه؛ گاهی وقتا ابزارش هست و نیازمند نوشتن برنامه سمت کاربر نیستیم. در این مثال منظورم tc(8) هست (که نقش Front-end داره). اما CLS_ACT چیه؟هربرنامه BPF یک نوع داره، هر نوع حداقل یک (یا بیشتر از یک) attach point داره؛ درواقع برنامه‌های BPF می‌تونن به رویدادهای مشخصی در کرنل وصل بشن و روی یک context عملیات انجام بدن.حالا CLS_ACT یک نوع برنامه BPF هست که:به همه‌نوع کارت شبکه‌ای می‌تونه وصل بشه.پارامتر ورودیش sk_buff هست.روی ترافیک ورودی (ingress) و خروجی (egress) عملیات انجام می‌ده.همه برنامه‌ها لزوما قادر به تغییر داده‌ها نیستن، خیلی‌هاشون صرفا برای مشاهده کردن و سنجش یک وضعیت کاربرد دارن. مثلا به این قطعه کد نگاه کنید تا یک سری مفهوم جدید رو معرفی کنم.BCC Front-endافراد علاقه مند به BPF یک ابزاری درست کردن تحت عنوان BCC یعنی درواقع یک Front-end هست برای برنامه‌های BPF. بهتون اجازه می‌ده کدی که سمت کاربر اجرا می‌شه رو به زبان پایتون بنویسید. البته برنامه اصلی BPF هنوزم باید به زبان C نوشته بشه.prog = &amp;quot&amp;quot&amp;quot   int hello(void *ctx) {     bpf_trace_printk(&amp;quotHello, World!\\n&amp;quot);     return 0;   }   &amp;quot&amp;quot&amp;quotخب این برنامه BPF فقط موقع اجرا شدن &quot;Hello World&quot; رو چاپ می‌کنه. حالا کی‌اجرا می‌شه؟b.attach_kprobe(event=b.get_syscall_fnname(&amp;quotclone&amp;quot), fn_name=&amp;quothello&amp;quot)همونطور که گفتم برنامه‌های BPF می‌تونن موقع رخ دادن یک رویداد اجرا بشن. اینجا گفته موقعی که یک فراخوان سیستمی به اسم clone اتفاق افتاد تابع hello (که ورودی برنامه BPF هست) رو اجرا کن.کلون وقتی اتفاق میوفته که یک پروسه جدید درست می‌کنید. پس هرموقع یک برنامه‌ای رو باز کنید رشته &quot;Hello World&quot; به همراه یک سری پارامتر دیگه چاپ می‌شه. این برنامه از نوع kprobe هست که برای سنجش یک فرایند به کار می‌ره. مثل این برنامه.فعلا زوده و نمی‌تونم بیشتر از این درباره نوع‌‌های دیگه حرف بزنم؛ بریم نکته بعدی.OS and Execution Modelبرای لود کردن یک برنامه BPF از سمت کاربر (trace_fields) دستور BPF_PROG_LOAD (که یک syscallهست) فراخوانی می‌شه و توی prog برای چاپ کردن از bpf_trace_printk (که یک helperهست) استفاده می‌کنه.از این چی می‌فهمیم؟ درواقع این BPF System Call موجب ارتباط بین سمت کاربر و کرنل می‌شه به واسطه این زیربنا هست که می‌تونیم front-end‌های مختلف داشته باشیم.اما قضیه BPF Helper چیه؟ خب درسته که برنامه داره توی دل کرنل اجرا می‌شه اما؛ فقط به یک سری توابع مشخص دسترسی داره پس نمی‌تونه پاشو از گلیمش درازتر کنه.حالا قضیه دیاگرام سمت راست چیه؟ اون مدل اجرای برنامه سمت کاربر و کرنل رو نشون می‌ده. نکته کلیدی اینه که هرموقع یک رویداد کرنل اتفاق بیوفته (مثل clone) برنامه سمت کرنل اجرا می‌شه. پس: برنامه‌های ‌BPF از سمت کرنل Event Based هستن. برای همین خیلی سریعه.اما برنامه سمت کاربر (trace_fields) یک task هست. برای اجرا شدن باید زمانبندی بشه.پس: هر برنامه‌ای که سمت کاربر اجرا بشه پنالتی‌هایی داره. یکیش همین زمانبندی شدنه که منجر به کاهش سرعته.برای درک بهتر این مثال رو ببینید. شناسایی و تشخیص ترافیک http نیازمند بررسی عمیق بسته‌هاست. این منطق رو می‌شه به کمک BPF سمت کرنل پیاده کرد و هرموقع که ترافیک شناسایی شد اون رو به سمت کاربر هدایت کرد. اینطوری خیلی سرعت افزایش پیدا می‌کنه.بررسی ایمنی و صحیح بودنبه خاطر اینکه برنامه BPF موقع اجرا جزئی از کرنل به حساب میاد یعنی می‌تونه به instruction pointer یا return address یا stack pointer دسترسی داشته باشه؟ نه!یک برنامه BPF در چند مرحله بررسی و محدود می‌شه مثلا:یک) خود LLVM به stack pointer نیاز داره ولی مطمین می‌شه که برنامه کامپایل شده به اون هیچ اشاره‌ای نمی‌کنه.دو) درمورد BPF Helpers هم که گفتم. اما Verifier چیه؟سه) وریفایر می‌گه من فقط یک الگوی بخصوص از نوشتن و تعریف دستورات رو می‌پذیرم و اگه خلاف اون باشه برنامه اجرا نمی‌شه مثلا:برنامه BPF باید همیشه خارج بشه. به عبارت دیگه حق استفاده از حلقه‌ها رو ندارید.باید همیشه Bounds checking انجام بشه مثلا اگه قراره از یک اشاره گر استفاده کنید باید مطمین بشید که null نیست (نمونه) اگر من بایت کدی پیدا نکنم؛ که بیانگر null checking باشه خطا میدم و اجازه لود شدن نداری.و کلی قانون دیگه که به مرور توی مطالب دیگه بهش اشاره می‌کنم.چهار) حالا قضیه JIT یکم مفصل تره ولی اونم از جنبه‌های مختلف تاثیر گذاره. می‌تونه دستورات رو ترجمه کنه به یک معماری خاص. فایدش خیلی زیاده مثلا Hardware Offload. یا از یه جنبه دیگه؛ به کمک JIT مفهوم حلقه رو می‌شه تا یه حدی پیاده کرد و ... معمولا به صورت پیشفرض JIT فعال نیست باید نسخه کرنل رو بررسی کنید.کلام پایانیاین بحث واقعا پایان نداره نمی‌تونم همش رو توی یک مطلب جمع کنم. مثلا اگه قرار باشه یه مقدار رو از سمت کرنل بفرستیم سمت کاربر می‌تونیم از BPF Maps استفاده کنیم که نوع‌های مختلف داره و می‌تونه بین چند برنامه به اشتراک گذاشته بشه. صحبت ازش به صورت جدا واقعا مفهوم قابل درکی توی ذهن مخاطب ایجاد نمی‌کنه و تنها با تمرین و دیدن پروژه‌های واقعی قابل درک می‌شه.یا مثلا شرح ELF Object خودش یه مطلب می‌خواد نمی‌تونم با hello world اونو به تصویر بکشم برای همین تصمیم دارم این بحث‌ها رو ادامه بدم. هروقت فرصت شد یک تیکه از BPF رو بیشتر باز کنم و بهتر دربارش بنویسم.لازم به ذکر هست که من خودم تازه افتادم توی این بحث‌ها بنابر این نوشتنش برای من راه خوبی برای به خاطر سپردنش هست. من بسته به نیازم و تا هرجا که بتونم مسیر پیشرفتم رو مکتوب می‌کنم.یه مورد دیگه اینکه عمدا نرفتم سراغ تاریخچه BPF و اونجور چیزها. از نظر من همین یک تیکه رو بفهمید از گذشته کافیه:توی سال 1992 به همراه tcp dump معرفی شد و اون زمان صرفا برای کارهای شبکه بود. یه عده از سال 2013 دیدن پتانسیل BPF خیلی بالاست و توسعش دادن اسمش شد Extended BPF (یا eBPF) بعدش گفتن شکل قبلی اون رو صدا بزنیم cBPF.حالا BPF یک تکنولوژی هست؛ یک سبک جدید از برنامه نوشتن، نه یک مخفف.یه سری منابع که خودتون بیشتر دنبال کنید:Dive into BPF: a list of reading materialBpf — a tour of program typesXDP Hands-On TutorialBPF Compiler Collection (BCC)Netflix talks about Extended BPF: A new software type - YouTubeeBPF Superpowers - YouTubeHow to Make Linux Microservice-Aware with Cilium and eBPF - YouTubeNetdev 1.2 - Advanced programmability and recent updates with tc&amp;amp;amp;amp;amp;amp;amp;#x27;s cls_bpf - Daniel Borkmann - YouTube</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Mon, 27 Apr 2020 03:17:39 +0430</pubDate>
            </item>
                    <item>
                <title>کار با فضانام شبکه در لینوکس</title>
                <link>https://virgool.io/@itshaadi/%DA%A9%D8%A7%D8%B1-%D8%A8%D8%A7-%D9%81%D8%B6%D8%A7%D9%86%D8%A7%D9%85-%D8%B4%D8%A8%DA%A9%D9%87-%D8%AF%D8%B1-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-ijwlxo0vq6gd</link>
                <description>Photo by Thomas Jensen on Unsplashتوی این پست می‌خوام در رابطه با استفاده از ip-netns صحبت کنم که یک ابزار هست برای اجرا کردن برنامه‌ها توی یک فضانام شبکه (Network Namespace).تو لینوکس لایه‌های مختلف مثل سیستم‌فایل، پروسه‌ها، شبکه و غیره رو می‌تونی جدا کنی. اصلا؛ چیزی مثل داکر یا LXD مجموعه‌ای از همین فضانام‌ها رو ایجاد و مدیریت می‌کنه.خیلی‌ها توی این دوران قرنطینه مجبورن به VPN شرکت وصل بشن، اجرا کردنش توی یک فضانام شبکه باعث می‌شه شما کنترل کاملی روی ترافیکش داشته باشید. مثلا این قبیل VPN‌ها دروازه پیشفرض تعیین نمی‌کنن در نتیجه شما هنوزم با IP خودتون وب گردی می‌کنید اما؛ این لازمه که DNS پیشفرض رو تغییر بده تا شما بتونید به زون‌های داخلی شرکت دسترسی داشته باشید ... حالا راه درستش اینه که وقتی فایل resolv.conf ویرایش می‌شه؛ بره به آخرش DNS رو اضافه کنه نه اینکه کلا فایل رو بازنویسی کنه. لذا این اشتباه ساده منجر می‌شه شما یا ترافیک DNS رو بفرستید روی سرورهای شرکت (که حریم خصوصی ندارید) یا اینکه اصلا زون‌های دیگه بلاک شده باشن در نتیجه شما نمی‌تونید وب گردی کنید ...کار با فضانام شبکهداخل یک فضانام شبکه (network namespace) می‌تونید:نوع‌های مختلف از یک اینترفیس رو داخلش بسازید یا مثلا یه اینترفیس رو از داخل سیستم بردارید و اون رو توی فضانام قرار بدید.جدول مسیریابی داخلی رو کاملا از نو تنظیم کنید مثلا دروازه پیشفرض داخل فضانام رو تغییر بدید.قوانین iptables وضع کنید.از یک DNS Server جدا استفاده کنید.خب برای شروع؛ یک تب ترمینال باز کنید (هشدار دسترسی روت):sudo su
/bin/bash --rcfile &lt;(echo &amp;quotPS1=\&amp;quotFrom Earth: \&amp;quot&amp;quot)می‌خوایم یک مریخ نورد بسازیم و از زمین بهش اینترنت بدیم :) پس این شد کنسول ما توی زمین. حالا باید:یک مریخ نورد بسازیم (فضانام شبکه به اسم rover).یه آنتن زمینی بسازیم و وصلش کنیم به آنتن مریخ نورد (veth یه دیوایس مجازی شبکه هست؛ دیوایس‌های دیگه هم هست مثل BRIDGE, MACVLAN, VLAN ...).آنتن مریخ نورد رو داخلش نصب کنیم.یک آدرس بدیم به آنتن زمینی و روشنش کنیم.ip netns add rover
ip link add earth-antenna type veth peer name rover-antenna
ip link set rover-antenna netns rover
ip addr add 10.10.1.1/24 dev earth-antenna
ip link set earth-antenna upحالا یه تب جدید ترمینال باز کنید:وارد کنسول مریخ نورد بشید (هر دستوری بعد از ip netns exec rover بیاد؛ داخل فضانام شبکه rover اجرا می‌شه و در اینجا ما بش رو بالا میاریم) بعدش یه آدرس بدیم به آنتن مریخ نورد و روشنش کنیم:sudo su
ip netns exec rover /bin/bash --rcfile &lt;(echo &amp;quotPS1=\&amp;quotRover Console: \&amp;quot&amp;quot)
ip netns exec rover ip addr add 10.10.1.2/24 dev rover-antenna
ip netns exec rover ip link set rover-antenna up
ip route add default via 10.10.1.1حالا بیاید کاری کنیم که مریخ نورد ما به اینترنت دسترسی پیدا کنه؛ برای اینکار باید از سمت زمین این دستورات رو بزنیم (wlan0 رو با اینترفیسی که دروازه پیشفرض شماست عوض کنید):echo 1 &gt; /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -o wlan0 -j MASQUERADE
iptables -A FORWARD -o wlan0 -i earth-antenna -j ACCEPT
iptables -A FORWARD -i wlan0 -o earth-antenna -j ACCEPTحالا داخل مریخ نورد پینگ بزنیم به گوگل:Rover Console: ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=46 time=60.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=75.9 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=46 time=76.7 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 60.827/71.140/76.727/7.301 msحالا DNS مریخ نورد رو چطوری عوض کنیم؟From Earth: mkdir -p /etc/netns/rover &amp;&amp; echo &amp;quotnameserver 1.1.1.1&amp;quot &gt; /etc/netns/rover/resolv.conf
Rover Console: exit
ip netns exec rover /bin/bash --rcfile &lt;(echo &amp;quotPS1=\&amp;quotRover Console: \&amp;quot&amp;quot)
Rover Console: cat /etc/resolv.confمثال‌های دیگهمن توی خونه یه اینترنت همراه اول دارم و یه TD-LTE ایرانسل؛ نمی‌شه همزمان دوتا دروازه پیشفرض داشته باشم (یه جورایی می‌شه‌ها ولی اون بمونه برای یه پست دیگه). یعنی اگه از ساعت 1 تا 11صبح بخوام چیزی دانلود کنم و همزمان وب گردی کنم سرعتم کم می‌شه. پس چیکار می‌شه کرد؟ برنامه aria2 رو توی یک فضانام جدا اجرا می‌کنم و اینترنت همراه اول رو بهش اختصاص می‌دم بعدش اینترنت ایرانسل رو دروازه پیشفرض بقیه چیزام قرار میدم. اینطوری:ip link add macv0 link eth1 type macvlan mode bridge
ip netns add dm
ip link set macv0 netns dm
ip netns exec dm ip addr add 192.168.8.101/24 dev macv0
ip netns exec dm ip route add default via 192.168.8.1
ip netns exec dm sudo -u hadi aria2c -x 8 -j 1 -i list.txtمن اینجا از macvlan استفاده کردم چون ساده و سریعه؛ و می‌تونم eth1 رو هم از داخل فضانام و هم بیرون از اون کنترل کنم (مثلا شاید من یهویی نیاز داشتم دروازه پیشفرض سیستم رو تغییر بدم به eth1 بدون اینکه دانلودم رو مختل کنم). به جای این کار می‌تونستم دستور ip link set eth1 netns dm رو بزنم و اینترفیس رو کاملا در اختیار فضانام قرار بدم.پروژه تورباکس که چند وقت پیش درست کردم؛ از فضانام استفاده می‌کنه تا کارت شبکه بی‌سیم رو کاملا در اختیار کانتینر داکر قرار بده (به جای اینکه از net=host استفاده کنم) و یه Anonymizing Middlebox تور درست می‌کنه.برنامه از چندتا کانتینر تشکیل شده که همه‌ اونها به busybox وصل می‌شن. بعدش من process id کانتینر busybox رو در میارم؛ و به فضانامش وصل می‌شم و بعدش هم کارت شبکه بی‌سیم رو داخلش قرار می‌دم. من توی اسکریپت به صورت دستی فایل فضانام رو می‌سازم و حذف می‌کنم ولی معادلش می‌تونه این دستور باشه:ip netns attach torbox &amp;quot$(${Docker} inspect -f &#039;{{.State.Pid}}&#039; torbox)&amp;quotکانتینر‌های داکر وقتی ساخته می‌شن یه فضانام هم دارن که اسمش برابر با pid اون کانتینر هست حالا این دستور میاد یه برچسب می‌سازه به اسم torbox بعد وصلش می‌کنه به pid کانتینر داکر که اسمش torbox هست. «شبیه» وقتی که با دستور add فضانام می‌سازید. چرا شبیه؟ ببینید دستور attach به فضانام‌هایی که وجود دارن وصل می‌شه. به دستور زیر نگاه کنید: ip netns attach tg &amp;quot$(pidof telegram-desktop)&amp;quot
ip netns exec tg ip routput:
default via 192.168.1.100 dev wlan0 
10.10.1.0/24 dev earth-antenna proto kernel scope link src 10.10.1.1 
192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.101 metric 600درواقع من می‌خوام از طریق pid برنامه تلگرام به فضانام اون دست پیدا کنم. اما وقتی که ip r رو داخل فضانام تلگرام اجرا کردم؛ دیدم که داره به جدول مسیریابی پیشفرض «کل سیستم» اشاره می‌کنه.چرا اینطوری شد؟ چون تلگرام فضانام اختصاصی نداره؛ ولی هر پروسه «باید» یک فضانام داشته باشه و فضانام پیشفرض شبکه برای همه پروسه‌ها میشه کل سیستم. در نتیجه tg میشه یک برچسب به default </description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Sat, 11 Apr 2020 07:12:32 +0430</pubDate>
            </item>
                    <item>
                <title>نگاهی به امروز و فردای WebAssembly</title>
                <link>https://virgool.io/GNULand/everything-about-wasm-qciuv9gq72pl</link>
                <description>توی این مطلب سعی‌ می‌کنم یه نگاه نسبتا عمیق به وضعیت الان WebAssembly و فردای اون داشته باشم. درواقع یک جمع بندی از وضعیت فعلیش و همچنین چیزایی که خودتون در آینده بیشتر دنبالش کنید.چی هست؟وب اسمبلی یک زبان بهینه و سطح پایین به صورت Bytecode برای وب هست که توی یک ماشین مجازی اجرا می‌شه (با اون ویرچوال باکس که خیلی‌ها شنیدیم فرق می‌کنه اون رو از ذهنتون پاک کنید).فرض کنیم 0x6a یک دستور بایت‌کد در این زبان هست که معادل متنی اون می‌شه add (تابع جمع). حالا شما این امکان رو دارید که به زبان‌های مختلف (Rust, Go, C, CPP ...) کد بنویسید و اون رو تبدیل کنید به WASM (بخونید وزم - مخفف وب اسمبلی).مثلا شما می‌نویسید:int sum = 1+2;و مثلا تبدیل می‌شه به:0x6a 0x01 0x02این قالبی هست که WASM داره و کدی که شما نوشتید «کامپایل می‌شه» بهش که مرورگر اون رو می‌فهمه و اجرا می‌کنه. اگه مطلب قبلی من رو خونده باشید و یا با JIT آشنایی دارید (اگه ندارید اینجا رو بخونید) فوری مشخص می‌شه که وقتی یک کد از قبل کامپایل شده باشه دیگه مرورگر نیاز نداره خروجی اون رو «حدس بزنه» و کلی ژانگولر انجام بده در نتیجه سرعت اجرای WASM بسیار بالا تره.آیا وب اسمبلی از جاوا اسکریپت سریع تره؟خب اره، ولی آیا همچین مقایسه‌ای درسته؟ بذارید اینطوری بهتون بگم:یه آزادراه رو در نظر بگیرید که دوتا ماشین دارن توش حرکت می‌کنن؛ یکی شون پول عوارضی رو آنلاین پرداخت کرده و دیگه نیاز نیست همش وایسه و پول بده اما اون یکی باید دم هر عوارضی توقف کامل کنه و پول بده ...ماشین اول، در تمام سفر با نهایت سرعتی که توی آزادراه مجاز هست حرکت می‌کنه ولی ماشین دوم «گاهی وقت‌ها» به نهایت سرعت مجاز می‌رسه و بعد باید توقف کنه و ...مراحل کامپایل و اجرا شدن جاوا اسکریپتدرواقع وب اسمبلی چون کامپایل می‌شه با «نهایت سرعت» و «کارایی» قابل ارائه توسط محیطی که در اون اجرا می‌شه (در اینجا مرورگر) کار می‌کنه اما جاوا اسکریپت چون باید از JIT رد بشه «ممکنه» به سرعت نهایی برسه ولی خیلی وقت‌ها این اتفاق ممکن نیست.مراحل کامپایل و اجرا شدن وب اسمبلیهمونطور که می‌بینید مرحله Parse کردن حذف شده، درواقع جاوا اسکریپت باید یکبار به AST تبدیل بشه و بعد اون رو تبدیل کنن به بایت‌کد که بعد توسط مرورگر کامپایل و بهینه سازی بشه ... ولی این مرحله توی وب اسمبلی نیاز نیست چون خودش یکبار کامپایل شده و به صورت بایت‌کد به مرورگر ارائه می‌شه. مرورگر فقط نیاز داره اون رو decode کنه که خیلی سریعتر و راحت تر از Parse کردن هست.بعلاوه چون این کد فشرده شده دریافت کردنش هم (یعنی دانلود اون توسط مرورگر) خیلی سریعتر از جاوا اسکریپت اتفاق می‌افته. هرچند مثلا gzip کردن جاوا اسکریپت حجم فایل رو کاهش می‌ده ولیکن خب gzip کردن وب اسمبلی هم حجمش رو «بیشتر» کاهش می‌ده ...یه مرحله دیگه توی JIT هست تحت عنوان Reoptimization. یعنی بر اساس کدی که بهینه شده باید درباره «قدم‌های بعدی» اون همزمان با اجرا؛ تصمیم گرفت. فرض کنید 100 تا Object دارین که 99 تاش بهینه شدن ولی موقع اجرا مشخص می‌شه که Object آخر بهینه نیست. اینجا اون «انتظار» که از اجرای کد داشتیم براورده نشده و دوباره باید بهینه سازی انجام بشه ... که این مرحله توی WASM نیاز نیست.پس، بله! وب اسمبلی سریع تره ولی دوتا ماشینه رو یادتونه؟ حالا فرض کنید اونی که سریع ترین سرعت رو داره مسیر رو بلد نیست ولی اونی که سرعتش کمتره بلده از یه راه کوتاه‌تر بره و مجبور نیست کل آزادراه رو طی کنه.یعنی چی؟ خب ببینید یه چیزی مثل DOM Manipulation درحال حاضر توی وب اسمبلی کندتره چون API اون بهینه نشده... (ولی روش کار می‌شه) پس قرار نیست یک شبه جای جاوا اسکریپت رو بگیره. اما توی خیلی از زمینه‌ها مشکلات و محدودیت‌هایی که جاوا اسکریپت داره رو برطرف می‌کنه و ویژگی‌های قابل توجه‌ای به وب اضافه می‌کنه - اینم بگم، خیلی وقتها لازمه از دنیای جاوا اسکریپت بریم توی دنیای WASM و برگردیم، قبلا این عملیات خیلی کند بود ولی الان سریع شده.مثلا وب اسمبلی برای بازی کردن توی مرورگر خیلی خوبه یا حتی شرکت ادوبی محصول Lightroom رو با وب اسمبلی نوشته تا توی مرورگر قابل استفاده باشه. همه اینها باعث می‌شن که بهش به عنوان یک زبان جدید برای وب نگاه کنیم ... (این ویدیو هم ببینید)آیا می‌تونیم جاوا اسکریپت رو به وب اسمبلی تبدیل کنیم؟نه، جاوا اسکریپت خیلی زبان داینامیک و پیچیده‌ای هست. وب اسمبلی بایت کدی تولید می‌کنه که نوع داده‌ها توی اون استاتیک هستن بعلاوه توی وب اسمبلی هنوز Garbage Collection پیاده سازی نشده و باید خودتون مدیریت حافظه رو در دست بگیرید.تایپ اسکریپت «شاید» در آینده به طور رسمی به وب اسمبلی کامپایل بشه چون به نظر می‌رسه تایپ‌هاش رو اضافه کردن. اما خب یه پروژه دیگه هست به اسم AssemblyScript که دقیقا کارش همینه (ولی یه تیم جداست).شکل دستوراتبه این قطعه کد از پروژه اسمبلی اسکریپت نگاه کنید:(module
   (memory $0 0)
   (table $0 1 funcref)
   (export &amp;quotmemory&amp;quot (memory $0))
)این چیه؟ چرا این شکلیه؟ -- این قطعه کد یک «بیان متنی» از بایت کدهای وب اسمبلی هست. که تحت عنوان WAST شناخته می‌شه. اگه فکر می‌کنید شبیه Lua هست کاملا درسته چون به این شکل از نوشتن میگن S-expressions که توسط زبان Lua ابداع شده.این شکل از نوشتن، به اندازه کافی انتزاعی هست که یک انسان بتونه بخونه و بنویسه و در عین حال یک ماشین با کمترین هزینه ترجمه کنه (بیشتر بخونید). شما می‌تونید به جای Rust یا Go مستقیم توی WAST کد بزنید و کامپایل کنید به WASM. این کار رو کسی نمی‌کنه بجز کسانی که می‌خوان به خود وب اسمبلی یه چیزی اضافه کنن. چون، همه چیز سطح پایین و در کنترل شماست و تقریبا چیزی جلودار خرابکاری‌ها نیست.هرچند اگر، تمایل به نوشتن WAST دارین باید حتما درمورد Stack-oriented programming و Stack machine اطلاعات زیادی داشته باشید. چون وب اسمبلی جز زبان‌های پیرو Stack machine هست.حالا که می‌دونید WAST چیه؛ بیاید 1 رو با 2 جمع بزنیم تا بهتون نشون بدم «پیرو استک ماشین» بودن یعنی چی:(i32.add
   (i32.const 1)
   (i32.const 2)
)این بیان متنی همون بایت کد (0x6a 0x01 0x02) مربوط به عمل جمع هست که توی استک به این شکل هست:اجرای دستور جمع در Stackالبته این یه مثال فرضی هست، کامپایلر هیچوقت برای مقادیر ثابت مثل 1 و 2 استک در نظر نمی‌گیره چون خروجی همون لحظه می‌تونه تشخیص داده بشه که 3 هست ... ولی منظور من اینه که دقیقا مثل یک استک دستورات Push و Pop می‌شه پس شما «باید» بلد باشین با «کمترین» حرکت ممکن دستور یا عملیات خاصی رو پیاده سازی کنید.امیدوارم الان متوجه شده باشید که چرا مقایسه کردنش با جاوا اسکریپت از یک جنبه سطحی (مثل سرعت) معقول نیست.ویژگی‌های اصلی و فرعیچهار رکن اصلی WASMتا اینجا فهمیدیم که یک زبان مثل اسمبلی هست، پس می‌شه باهاش برنامه‌های دسکتاپ رو کامپایل کرد به چیزی مثل x86 و توی «یک محیط» اجرا کرد (1). و اینکه حجمش بسیار کمه برای همین انتقال دادن میزان زیادی از کد توی وب راحت انجام می‌شه‌ (2) و درمورد نحوه اجرای دستورات صحبت شد که نشون دادم چرا سریعه (3) ...اما، ما که نمیتونیم یه برنامه‌ای رو از وب دانلود کنیم و بهش اجازه بدیم از تمام حافظه استفاده کنه. برای همین مدیریت حافظه توی وب اسمبلی به صورت خطی انجام می‌شه. درواقع وب اسمبلی می‌ره یک بخشی از حافظه سیستم رو «اجاره» می‌کنه و بعد اون رو به شکل یک آرایه در اختیار برنامه‌ها قرار می‌ده. اینطوری یک برنامه نمی‌تونه از اون فضا خارج بشه.(4)این فقط «شروع راه» بود! خیلی ویژگی دیگه داره اضافه می‌شه و یا به مرحله‌ای رسیده که علاقه منداش می‌تونن استفاده کنن.مثلا پشتیبانی از multithreading، به مرحله چهار از پیاده سازی رسیده (شیش مرحله داریم از 0 تا 5، مرحله پنجم دیگه تمیز کاری نهایی و پذیرفته شدن کامل اون ویژگی هست). درواقع multithreading این امکان رو داره که فعالیت‌های مختلف یک برنامه رو به بخش‌های کوچیک تقسیم کنیم و بعد به صورت همزمان انجام بشن تا خیلی خیلی سریعتر اون فعالیت به اتمام برسه.یا مثلا پشتیبانی از SIMD که خیلی ساده بگم؛ شما مثلا یه عملیات دارین که «ممکنه» 64 بیتی باشه پس به اندازه یک عملیات 64 بیتی حافظه رزرو می‌کنید. اما اون مجموعه از داده‌ها که عملیات روش انجام می‌شه «ممکنه» همش 64 بیتی نباشه مثلا شاید دوتا 32 بیتی داری. خب اگه یکی یکی انجامش بدی هر بار 32 بیت از حافظه اضافه میاد که الکی رزرو شده. برای همین اگه اون عملیات رو همزمان روی هردوتاشون انجام بدی اینطوری هم از حافظه به خوبی استفاده کردی و هم سرعت پردازش بالا میره. (یه ویدیو جالب).از جهت «بهینه شدن کد و اجرای سریعتر» هم راه‌ حل‌های جالبی ارائه شده مثل Streaming compilation و Tiering compiler که یعنی به محض رسیدن یک قسمت از کد به کامپیوتر؛ همزمان یه مرحله کامپایل می‌شه.بعدش هم وقتی که همه کد رسید یه دور دیگه کامپایل می‌شه تا بهینه‌ترین حالت خودش قرار بگیره. ایده اینه که ما عادت داریم وقتی یه برنامه رو روی کامپیوتر خودمون باز می‌کنیم بتونیم اون رو فوری استفاده کنیم پس، وقتی هم که از اینترنت یه قطعه کد دانلود می‌شه باید بتونیم همونطوری سریع اجرا و استفاد کنیم.قابلیت Implicit HTTP caching هم خیلی جالبه یعنی یک بار که کد کامپایل و بهینه شد دیگه همون نسخه رو می‌تونی اجرا کنی و نیاز به کامپایل و بهینه سازی مجدد نیست. (و کلی ویژگی باحال دیگه مثل Garbage Collection و Exception Handeling و Debuging که اینجا می‌تونید بخونید.)پس؛ با وجود این همه ارکان فرعی که درحال اضافه شدنه یه بحث جذاب شکل گرفته و اونم استفاده از وب اسمبلی در بیرون از وب هست برای IoT یا پردازش ابری و ...وب اسمبلی بیرون از وبچرا بالا تر گفتم «محیط اجرا» و مستقیم نگفتم مرورگر؟ به خاطر اینکه؛ در هیچ کجا ذکر نشده که «باید حتما مرورگر باشه» و بلکه فکرهایی شده برای اینکه خارج از مرورگر هم بشه از وب اسمبلی استفاده کرد.اما به چه درد می‌خوره؟ خب همیشه وقتی به وب فکر می‌کنیم یاد HTML CSS JS میوفتیم اینها «کدهای یه نفر دیگه» هستن که ما «یه راهی پیدا کردیم که با خیال راحت اجراش کنیم». مثلا فرض کنید جاوا اسکریپت به همه سیستم دسترسی داشت و یه کتابخانه معروف مثل React می‌تونست بعدا یه کد مخرب به آپدیت جدیدش اضافه کنه و همه وبسایت‌ها آلوده می‌شدن ...می‌دونم الان با اخم به پاراگراف نگاه می‌کنید. خب React که متن بازه همه می‌تونن سورسش رو ببینن. آره؛ اما آیا همه وب متن بازه؟ همه وب قابل اعتماده؟ یا حتی اون قسمت‌هایی که متن باز هستن اگه یه روزی شیطنت کنن ما «بلافاصله» می‌فهمیم؟ یا بعد چند روز/ماه که گندش در اومد؟خب خوشبختانه مرورگرها خیلی زحمت می‌کشن که یه محیط ایمن و Sandbox ایجاد کنن که این اتفاق‌ها خیلی کم رخ بده و کدها فقط به اون چیزی که اجازه دارن دسترسی پیدا کنن ... پس بحث همینه؛ وقتی وب اسمبلی رو به چشم یک Sandbox نگاه کنیم استفاده ازش خارج از مرورگر می‌تونه خیلی پر کاربرد باشه.یه موضوع دیگه Portability هست. الان وبسایت‌ها فارغ از اینکه بدونن معماری پردازنده شما چیه کد جاوا اسکریپت می‌نویسن و اجرا می‌شه چون مرورگر سکوی اجراست و اون تشخیص می‌ده.دقیقا NodeJS هم هدفش این بود دیگه که جاوا اسکریپت رو بتونی خارج از مرورگر هم اجرا کنی... اما حتی اونجا هم مجبوری یه ماژول CPP بنویسی اگه سرعت بالا می‌خوای.خب، وقتی بتونیم ایمنی رو با Sandbox ارائه کنیم و Portability در کنارش باشه نتیجه اینه که می‌تونیم بدون نیاز به کانتینر؛ فضای اجرای یک قطعه کد رو محدود کنیم و کدهای بیشتری رو در زبون‌های مختلف روی یک ماشین اجرا کنیم. کجا این قضیه به درد می‌خوره؟ خب معلومه توی FaaS.بعلاوه ماهیت وب اسمبلی طوریه که واقعا به زبان ماشین خیلی نزدیکه؛ در نتیجه اگه بشه روی دستگاه‌های IoT ازش استفاده کنیم عملا این امکان رو داریم که با پایتون و تایپ اسکریپت و خیلی زبون‌های دیگه شروع کنیم به برنامه نویسی روی اونها دیگه بازارش محدود نیست به زبان C و پیچیدگی‌های آنچنانی از بین میره.روند اجرا بیرون از مرورگر چطوریه؟روی خیلی از سیستم‌ها یک زبان برنامه نویسی به طور مستقیم به شبکه، حافظه، سیستم‌فایل و غیره دسترسی نداره. چرا؟ چون اگه برنامه‌ها حق داشته باشن آزادانه از تمام منابع استفاده کنن بالاخره چه عمدی یا غیر عمدی سو استفاده رخ می‌ده.حالا سیستم‌عامل یک سری توابع تحت عنوان System Call ارائه می‌کنه و هر قطعه کدی که به منابع سیستم نیاز داشته باشه باید اون توابع رو صدا بزنه و از اونها درخواست کنه.اصطلاحا به این میگن یک برنامه user space که توی حلقه 3 اجرا می‌شه. درواقع سیستم‌عامل یک سری حلقه امنیتی داره و بحث ما مربوط میشه به حلقه سوم.خب، طبیعتا هر سیستم عاملی مجموعه توابع خودش رو داره درسته؟ و این یعنی ما باید کدهای متفاوت بنویسیم برای سیستم‌عامل‌های متفاوت.اما چرا این حرکت لازم نیست؟ به خاطر اینکه اکثر زبون‌های برنامه نویسی خیلی چیزا رو به صورت انتزاعی پیاده می‌کنن (مثلا تابع خواندن از فایل رو توی کتابخانه استاندارد خودشون به برنامه نویس ارائه میدن) بعدش در لحظه کامپایل بر اساس سیستم هدف؛ system call های مورد نیاز رو جایگزین اون لایه انتزاعی می‌کنن و پورتابل شدن از این رویه میاد.اما وب اسمبلی برای یک ماشین فرضی کد تولید می‌کنه یعنی درواقع نمی‌دونه معماری و سیستم‌عامل حاضر روی ماشین چیه. بنابراین همچین اتفاقی میوفته:شما کدتون رو توی یکی از زبانهایی که وب اسمبلی پشتیبانی می‌کنه می‌نویسید. بعد یک «تفسیر» از اون کد تولید می‌شه که کامپایلر وب اسمبلی از اون استفاده می‌کنه تا با دیتا تایپ‌های خودش تطابق بده و بایت‌کد تولید کنه. مثلا چیزی تحت عنوان Struct توی وب اسمبلی نیست این باید تبدیل بشه به چی؟ کار IR تفسیر همچین شرایطی هست.توی فضای وب، مرورگر می‌شه runtime وب اسمبلی و مسئول تبدیل بایت‌کد WASM به کد ماشین (پس اینطوری پورتابل می‌شه).بعدش یک سری system call در اختیار وب اسمبلی قرار می‌ده و اونهم بر اساس نیازهای برنامه درحال اجرا اون توابع رو در اختیار برنامه قرار می‌ده. (اینطوری ایمنی به شکل Sandbox پیاده می‌شه).به همین جهت بیرون از وب هم به یک runtime نیاز داریم که دوتا از معروف ترین هاش WAVM و Wasmtime هستن.اما یه سوال دیگه، بیرون مرورگر کی قراره شرایط Sandbox رو دیکته کنه؟ کی قراره به ما بگه کدوم syscallها رو در اختیار داریم و کدوما رو نداریم؟رابط بین وب اسمبلی و سیستم میزبانوزی (WASI) یا همون رابط بین وب اسمبلی و سیستم میزبان یک سری قرارداد هستن که هر runtime باید اونها رو پیاده کنه و Sandboxing از این طریق به برنامه‌های درحال اجرا دیکته می‌شن.توی حالت عادی برنامه‌ها مسیری که نیاز دارن رو به صورت یک رشته به سیستم عامل می‌دن و بعد سیستم عامل چک می‌کنه که آیا کاربری که این برنامه رو اجرا می‌کنه دسترسی داره به اون مسیر یا نه؟ و چون رشته هست، عملا وقتی دسترسی بهت داد می‌تونی فراتر از اونهم پیش بری.ولی مستندات WASI اینطوری تعریف شده که، سیستم میزبان یک مسیر رو در اختیار runtime قرار می‌ده بعد وقتی برنامه درحال اجرا درخواست می‌کنه که از مسیر x فایل y رو بهم بده، بهش یک file descriptor برگشت می‌ده. یعنی برنامه دیگه فقط حق داره روی فایل y عملیات انجام بده و «اگر» نیاز شد می‌تونه بقیه مسیر x رو طی کنه. اما نمی‌تونه فراتر از x بره.همچنین این سطح دسترسی «از برنامه به برنامه دیگه» فرق می‌کنه و یکسان نیست. همه برنامه‌ها به طور پیشفرض به کل مسیر x دسترسی ندارن.این تعاریف ماژول بندی شدن؛ فعلا بحث‌های سیستم‌فایل و شبکه و غیره توی wasi-core قرار گرفته و این سیستم ماژول بندی باعث می‌شه که بعدا افراد جامعه کاربری ماژول‌های خودشون رو توسعه بدنمثلا خیلی چیزا توی wasi-core داره استاندارد‌های POSIX رو دنبال می‌کنه و چیزایی مثل read,open,close توش پیاده سازی شدن اما بحثی مثل fork واقعا پیچیده هست و نمی‌شه همون مسیر POSIX رو دنبال کرد و در عین حال، به خاطر ماژولار بودن این روال؛ شانس «استاندارد سازی» fork هست و بعدا می‌تونه به عنوان یک ماژول جدا اضافه بشه.برای یک زبونی مثل Rust خودش مستقیم این توابع رو توی اون لایه انتزاعی صدا می‌زنه ولی برای زبون‌های دیگه مثل c و cpp کتابخانه wasi-libc وجود داره.مثلا Rust توی اون لایه انتزاعی یه شرط گذاشته که «اگه قرار بود به WASM کامپایل بشی، باید از wasi syscalls استفاده کنی» زبون‌های دیگه مثل python می‌تونن از اون کتابخانه wasi-libc استفاده کنن و کتابخانه‌های بومی اون زبان رو تولید کنن.حالا پورتابل بودن در کنار امنیت برقرار می‌شه و همزمان «تعریف امنیت» بین هر runtime می‌تونه متفاوت باشه مثلا node یک سری دسترسی بیشتر بده ولی firefox کمتر و همون کد (با سطح دسترسی‌های متفاوت) روی جفتشون اجرا می‌شه ...درست مثل ویژگی‌های فرعی که کم کم اضافه می‌شه؛ یک سری ماژول فرعی هم مثل asynchronous I/O یا file watching و غیره اضافه می‌شن و به مرور دنیای بیرون از مرورگر هم بهتر و بهتر می‌شه.پاسخ به سوالات توییتر+ الان blazor بیاد استقبال میشه ازش؟خب، موسی به دین خود. عیسی به دین خود!. محصولات مایکروسافت همیشه پیروان دو آتیشه خودش رو داره پس در زنده موندن blazor شکی نیست؛ اما وب اسمبلی توی زمینه‌های بزرگتری فعالیت می‌کنه بنابر این اگه بخوایم کلاس بندی کنیم میشه رقیب blazor در زمینه وب. یا میشه رقیب Firecracker در زمینه FaaS. هیچوقت کاملا جایگزینش نمیشه.+ آیا آینده‌ای داره، یا مثل سیلور لایت زود آفتابش غروب میکنه؟بله، اینده خیلی خوبی داره و نه! مثل سیلور لایت سرش نمیاد. بالا تر گفتم باید وب اسمبلی رو کلاس بندی کنیم؛ پس اگه در زمینه وب شکست بخوره در زمینه‌های دیگه آینده داره ...تو Back-End هم استفاده می‌شه؟نه به شکلی که NodeJS حضور داره بلکه در مقیاس بزرگتر. مثلا بازار سرویس‌های PaaS کمرنگ می‌شه و ظهور و فعالیت سرویس‌های FaaS پررنگ تر.بیاید به سرویس فندق (لیارا/اروان) نگاه کنیم؛ چه چیزی باعث می‌شه فندق هزینه‌اش زیاد بشه؟ خب؛ یه سری بحث مثل لود بالانسر یا پشتیبانی و فایروال و غیره هست که توی IaaS هم وجود داره پس این فاکتور‌ها رو نمی‌تونیم دستکاری کنیم.ولی آیا می‌تونیم با یه نرخ معقول «کانتینر بیشتر بسازیم»؟ الان فندق مثل اینه که یه زمین رو اجاره کرده و روش یه هتل ساخته و داره اتاق‌هاش رو کرایه می‌ده .. من نمی‌تونم «نصف یک اتاق» رو اجاره کنم یا «یه اتاق و نیم» رو اجاره کنم درسته؟این باعث می‌شه که اتاق‌هایی با اندازه مشخص اجاره بده و حتی اگه مهمان بین ساعت 7صبح تا 4 ظهر توی اتاقش نیست اون نمیتونه به کس دیگه اجاره بده؛ چمدون‌هاش توی اتاقه (همون registry).پس متوجه می‌شیم که هدف PaaS «سهولت در توزیع/انتشار یک محصول» هست ولی لزوما در «کاهش هزینه» موثر نیست.یه ذره عمقی‌تر صحبت کنم؛ توی شرایط داکر، فهمیدن اینکه «کی چقدر منابع مصرف کرده» سخته و راه آسون تر اینه که بگیم تو 8 گیگ رم داری و 4 هسته پردازنده؛ قیمت اجارش می‌شه اینقدر ...یه ذره منطقی‌تر اینه که اجاره رو روزانه حساب کنیم و مشتری هر لحظه بتونه منابع رو گسترش بده. تا اینطوری از کم شروع کنه و بعد پله پله زیاد کنه؛ یا اگه جمع کرد فقط تا اون روزی که استفاده داشته پول بده.اما وقتی مشتری فندق زیاد می‌شه نمی‌تونه روی همون زمین بازم اتاق هتل بسازه باید بره زمین بیشتر اجاره کنه ...از این طرف FaaS بحث‌های لود بالانسر و فایروال و غیره رو داره و در عین حال می‌تونی به اندازه‌ای که کدت از cycle‌های پردازنده و باقی متریک‌ها استفاده کرده پول بدی. و وقتی کدت اجرا نمیشه‌ همزمان یه نفر دیگه از منابع می‌تونه استفاده کنه.استخراج این متریک‌ها از وب اسمبلی کاملا ممکن هست، بعلاوه سبک بودن runtime اون در کنار قوانین WASI و پورتابل بودنش بهش این امکان رو می‌ده که کدهای «هر زبانی» خیلی «سریع» و «بدون ایجاد تداخل برای کد‌های دیگه» اجرا بشن.شرکتی مثل fastly کاملا از این موقعیت سود می‌بره. چرا؟ خب اونها کلی سرور سراسر دنیا دارن که باید به صورت عادی ترافیک زیادی رو تحمل کنه؛ فایروال و لود بالانسر و بقیه چیز‌ها هم که وجود داره. این‌ها اومدن runtime خودشون رو توسعه دادن که بتونن سرویس FaaS ارائه بدن روی سرورهاشون و انقدر سریعه که بر اساس هر درخواست یک sandbox بالا میارن (فکر کنم در حدود 60 نانو ثانیه طول می‌کشه تا هر sandbox بالا بیاد).البته، قصد من از این مقایسه نادیده گرفتن زحمات فندق یا بقیه نیست. به این اشاره می‌کنم که خیلی از فرایند‌های stateless رو میشه با هزینه کمتر اجرا کرد. و اینم بگم PaaS به کلی از بین نمیره مثلا یه برنامه موبایلی رو در نظر بگیرید که نیاز داره محاسبات خاصی انجام بده و نتیجه رو ذخیره کنه. در اینجا زیرساخت پایگاه داده رو می‌تونم ببرم روی PaaS و اون تابع رو ببرم روی FaaS.کلام پایانیمن سعی کردم جریانی که از 2017 شروع شده تا الان رو توی یک مطلب جمع کنم برای همین خیلی فشرده شد ... مثل تور بازدید از اماکن یه شهر؛ حالا اگه علاقه مند بودید بعد از خوندن این مطلب می‌تونید برید یه جنبه خاص از وب اسمبلی رو یاد بگیرید.اینبار سعی کردم توی توییتر از بقیه سوال کنم و موضوع رو بر پایه اون سوالات بچینم. به خاطر اینکه هرموقع درمورد یه موضوعی می‌نویسم یا توییت می‌کنم بعدش سوالاتی هست که من نمی‌تونم توی اون فضای کاراکتری محدود توییتر جواب بدم... اگه به نوشته‌های من علاقه دارین توی توییتر منو دنبال کنید؛ شاید مطلب بعدی یکی از سوالات شما بود.منابع (بیشترش حول صحبت های Lin Clark هست) :&amp;amp;amp;quot;Isolation without Containers&amp;amp;amp;quot; by Tyler McMullenRust, WebAssembly, and the future of Serverless (DevFest 2019)Bringing WebAssembly outside the web with WASI by Lin ClarkGOTO 2018 • A Cartoon Quest: New Adventures for WebAssembly • Lin ClarkWebAssembly DemystifiedWebAssembly Articles</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Mon, 24 Feb 2020 06:25:01 +0330</pubDate>
            </item>
                    <item>
                <title>چرا از JavaScript به Rust مهاجرت کردم؟</title>
                <link>https://virgool.io/@itshaadi/moving-from-js-to-rust-j1dkqzbwerad</link>
                <description>من درحال نوشتن این مطلب، توضیح مهاجرت واقعا طولانی شده بود.قبل از اینکه شروع کنم لازمه بگم من «کد نویسی» رو دوست دارم و اگه زبان یا تکنولوژی رو انتخاب کنم جدای از اینکه فرصت دارم یا ندارم که توی مقیاس بزرگ امتحانش کنم، همیشه دوست دارم به عمق ماجرا پی ببرم. همیشه نظرات و تجربیات بقیه رو می‌خونم، مستندات خود زبان و پارادایم‌های قابل استفاه در اون زبان رو دنبال می‌کنم و دید این مقاله طبیعتا آکادمیک هست.اینا رو گفتم که مشخص بشه حرفهام از دیدگاه کسی نیست که توی شرکت X کارش وابسته به جاوا اسکریپت باشه، من صرفا به برنامه نویسی (این روزها سیستمی) علاقه دارم و این مطلب «شبیه کسی هست که از یه توزیع به توزیع دیگه مهاجرت کرده» درواقع فاکتور انتخاب من هیچوقت بازار کار و Scale نبوده و شکل دلایلم مقایسه‌ای از هردو زبانه، اگه توسعه دهنده جاوا اسکریپت هستین شاید یه چیزایی یاد گرفتین که قبلا نمی‌دونستید.جاوا اسکریپت چطوری کار می‌کنه؟کامپایلری که توی دانشگاه نشون میدننمی‌دونم که شما «نظریه زبانها» رو پاس کردین یا نه اما خودمونی ترش همون دوتا فلش Front-End و Back-End هست که کشیدم. به من (و شما که درس رو پاس کردی) همیشه گفتن که آقا کامپایلر باید نوع داده رو چک کنه. و گفتن اگه موقع اجرا دستور به دستور تفسیر کرد بهش میگن «مفسر».اما این «تنها شکل کامپایلر» نیست! درواقع «به یقین رسیدن» درمورد نوع داده بر اساس «حدس زدن» خیلی می‌تونه پیچیده بشه! برای همین یه چیزی هم هست که ترکیب کامپایلر و مفسره به اسم JIT.برای اینکه مطلب طولانی نشه کارش اینطوریه:یک) دستورات حین اجرا مانیتور (‫‪profiler‬‬) میشن.دو) قطعه کدی که به صورت مکرر تکرار بشه به عنوان نقطه داغ (HOT Code Point) برچسب گذاری میشه.سه) کامپایلر سعی می‌کنه قطعه کد رو بهینه کنه و معادل Byte Code اون رو جایگزین و اجرا کنه.چهار) اگه مرحله سه بیشتر از یک بازه‌ای تکرار بشه به جای کامپایل کردن، اون کدداغ؛ دستور به دستور تفسیر می‌شه.فکرکنید یه حلقه خیلی بد نوشتین (اکثرا وقتی که از تابع Map سوءاستفاده می‌کنید اتفاق میوفته و خیلی بی‌صدا گند می‌زنید به کد) خب این هی تکرار می‌شه راهی نیست که بخوای بهینه کنی و اگه کامپایلر روشی تعریف نکنه برای «بی خیال شدن» دیگه همونجا گیر می‌کنه. (مثلا 10 بار قطعه کد رو اجرا می‌کنه اگه تونست خروجی رو حدس بزنه خب بهینه می‌شه؛ وگرنه چاره‌ای نیست جز تفسیر دستور به دستور. که بسیار پر هزینه هست) - توجه کنید، گفتم دستور به دستور! نه خط به خط؛ چون شما ممکنه دوتا دستور توی یک خط داشته باشی.شماهایی که یه خورده کارکشته‌تر هستین قطعا به Type Script اشاره می‌کنید. «خب این که روی نوع داده حساسه خیلی بهتره چرا از اون استفاده نمی‌کنی؟»قضیه اینه که تایپ اسکریپت یک «تلاش مستمر» برای بهتر کردن جاوا اسکریپت هست اما. جدای از اینکه، سازکارش خیلی جذابه؛ موضوع اینه که اینهم سعی می‌کنه یک سری «حدس و گمان» منتها با درجه احتمال بالاتر به دست بیاره و بر اساس اونها کد جاوا اسکریپت بهینه بنویسه.این قطعه کد رو ببینید:&amp;quotuse strict&amp;quot
var Foo = {
  get x() { return.this.__x },
  set x(v) {this.__x = v}
}
function Bar (v) {
  this.x = v
}
Bar.prototype = Object.create(Foo)
Bar.prototype.identify = function () {
  console.log(this.x)
}
var p = new Bar(42)
p.identify()این کد عمدا به یک شکل صریح آورده شده، هدفم اینه که بگم حتی تایپ اسکریپت هم اون زیر یه همچین کدی تولید می‌کنه، به خاطر اینکه هرچی جزئیات بیشتر باشه، JIT بهتر می‌فهمه چیکار باید بکنه.از طرفی تایپ اسکریپت فقط Bundler هست. جلوی خطاهایی که به علت نبود تایپ به وجود میان رو میگیره، اما همچین چیزی هنوز هزینه اجرا داره:[1, 2, 3, 4, 5].map(x =&gt; x * 2).filter(x =&gt; x % 2).reduce((a, x) =&gt; a + x, 0);تازه همه‌جا ممکنه نتونی ازش استفاده کنی برای همینه که یه شرکت معقول که دنبال توسعه دهنده NodeJS می‌گرده انتظار داره که ES5 بلد باشی، معنیش اینه که حالشو داشته باشی به صورت صریح کد بنویسی (مثال بالا).این قطعه کد‌هم ببینید:var Foo = {
*[Symbol.iterator] () {
        for (let [idx, val]) of this.nums.entries()) {
              yeild label =&gt; `${label}: ${val}(${idx+1} / ${this.nums.length})`
        }
  },
      bar ([x=1, y=2, ...nums] = []) {
             this.nums = [x,y,...nums]
      }
}
Foo.bar([10,20,30,40,50])
foo (let f of Foo) {
    console.log(f(&amp;quotFoo&amp;quot))
}می‌دونم شما الزاما همچین چیز پیچیده‌ای توی محیط واقعی نمی‌نویسید هدفم اینه که انتزاعی شدن جاوا اسکریپت رو به تصویر بکشم. درواقع یک عالمه از ویژگی‌های ES6 رو توی این کد اومده، ساختار کد «ضمنی» شده.بحث ‌اینه که این انتزاعی شدن «هزینه داره» و زبان «باید» این‌ها رو لحاظ کنه چه شما این شکلی کد بنویسی چه ننویسی. در مجموع چیز بدی نیست، ولی چون هزینه داره باید با احتیاط عمل کنی. اصلا، اینهمه موتور V8 بهینه می‌شه یا میرن تایپ اسکریپت درست می‌کنن برای اینه که انتزاع توی زبان باقی بمونه و با یه شکل دیگه هزینه رو کم کنن. (در شرایط تایپ اسکریپت خیلی چیزا zero-cost abstraction شدن).یادگیری این چیزا، انگیزه زیادی می‌خواد شما نمی‌تونی یک هفته بشینی جلوی Pluralsight و همشو حفظ کنی. «فضای تولید اشتباه» توی این زبان وسیعه برای همین یادگیری عمیق این زبان الزامیه. توی پیاده سازی business logic بیشتر از زبان‌های دیگه مجبوری به «چطور بهینه بنویسم» فکر کنی.مدیریت حافظهپیدا کردن نشتی حافظه توی NodeJS همیشه مورد بحثه، علت عمده‌اش اینه که این زبان Garbage Collector داره درواقع شما هرچی کمتر به کامپایلر اطلاعات بدی اون کار بیشتری باید انجام بده...زنجیره پروتوتایپچندتا نکته:یک) همه چیز توی جاوا اسکریپت Object هست (مربع‌ها توی تصویر بالا)دو) یک Object از پروتوتایپ یک Object دیگه ساخته می‌شه (اینجا Foo پروتوتایپ Global هست)سه) همه Object‌ها یک پروتوتایپ از Global Object هستن. (بازگشت‌همه به سوی اوست!)حالا کار Garbage Collector این هست که در زمان اجرای کدشما دنبال Object هایی بگرده که دیگه از Global نمیتونی بهشون برسی. (یعنی اون فلش بزرگ، از Global شروع میکنه میاد پایین) وقتی تشخیص بده دسترسی قطع شده اون رو به عنوان زباله شناسایی می‌کنه و حافظه‌ای که بهش اختصاص داده شده‌ آزاد می‌شه.هربار که Garbage Collector اجرا بشه، کدشما متوقف می‌شه. برای‌همین اگه زود انجام بشه کدشما کمتر اجرا می‌شه و اگه دیر‌ انجام بشه حافظه زیادی مصرف می‌شه. اینکه بفهمیم «کی آشغالا رو جمع کنیم» همیشه چالش برانگیزه و هیچ زباله جمع‌کنی بی‌عیب نیست.برای همینم انتزاع هزینه داره، اینها باید به زنجیر اضافه بشن و چشممون بهشون باشه که ببینیم کی زباله می‌شن.شماتیک حافظه V8Resident Setوقتی به شماتیک بالا نگاه می‌کنید واضحه که فضای کمتری به اجرای کدهای شما اختصاص داده شده (1). از طرفی چون «همه چیز یک شی هست» میزان قابل توجه‌ای از داده‌ها روی لایه 3 ذخیره می‌شه. تمام رشته‌هایی که طول ثابت ندارن، و همچنین closure (که، خیلی توی انتزاعی شدن کد و افزایش خوانایی اون تاثیر داره) هم روی Heap ذخیره می‌شه.فرق بین 2 و 3 چیه؟ خب ببینید، توی Stack داده‌ها مثل بشقاب روی هم چیده شدن، شما وقتی بخوای بشقابها رو بذاری توی کابینت باید بدونی تعدادشون چندتاست که جای درست قرارش بدی. بعد بشقابهایی که از یه جنس هستن رو دسته بندی می‌کنی دیگه درسته؟ مثلا 6 تا میوه خوری 6 تا سوپ خوری، میدونی کجاست و چندتاس پس سریع بهشون می‌رسی.اما Heap [معمولا] برای داده هایی هست که حجمشون مشخص نیست (مثلا بخش نظرات این مطلب از نوع رشته هست و نمیدونیم هرکس قراره چقدر بنویسه) در نتیجه کنارهم نیستن و پخش شدن توی حافظه، دنبالشون گشتن هزینه بره. مثل اینکه با دوستات بری رستوران بعد پیشخدمت اول سفارش یکی از دوستات رو بگیره بعد بره یه میز کاملا جدا، بعد دوباره برگرده سفارش اون یکی رفیقت رو بگیره بعد بازم بره ....قسمت 4 اشاره می‌کنه به اندازه کل این Resident Set که تعریف شده، 5 و 6 هم اشاره دارن به میزان حافظه ای که در اختیار v8 هست و میزانی ازش که استفاده شده (مثلا یکی از فاکتور‌ها اینه که اگر میزان 6 به 5 نزدیک شد یه دور آشغالا رو جمع کن).قسمت 7 مربوط میشه به وقتی که یه کتابخانه c++ داریم؛ شی هایی که به اشیاء داخل جاوا اسکریپت وصل شدن باید توسط V8 شناسایی و دنبال بشن (که اگر دقت کنید اینم توی Heap ذخیره می‌شه). پس نکته اینجاست که «بریم ماژول c++ بنویسیم» هم لزوما بدون دردسر نیست. مثلا شما ممکنه یک عالمه Object به دنیای JavaScript تزریق کنید که همشون باید دنبال بشن ....اینجا دیگه من خیلی ادامه نمیدم خودتون نشتی حافظه و روش‌های شناسایی رو مطالعه کنید.حالا Rust چطوری کار می‌کنه؟من یک ماهه که مهاجرت کردم، و در این سمت ماجرا «مبتدی‌تر» هستم برای همین؛ امیدوارم اگه از این بحث استقبال شد بیشتر درباره Rust بنویسم. منتها توی همین یک ماه چیزای به درد بخور زیادی از کتاب یاد گرفتم.نکته دیگه هم اینه که، «فضای طرفداران Rust» یه مقداری سمی هست. هر محفلی بری چون زبان تازه داره طرفدار پیدا می‌کنه خیلی‌ها عاشق خوبی‌هاش میشن و چون دیگران کمتر درمورد ایراداتش صحبت می‌کنن پیدا کردنش در دید اول سخته. برای همین اول از بدی هاش می‌گم:اینکه اینا اول تصمیم گرفتن که unified ABI رو پیاده سازی کنن که، بعدا بدون اعلان یک بیانیه درست حسابی بیخیال قضیه شدن. نبودن ABI بحث رو پیچیده می‌کنه خصوصا توی زمینه safety in portability (این ویدیو رو ببینید) که Rust ادعاهای بزرگی دربارشون داره...بحث بعدی توی نحوه تعریف زبان هست، ما می تونیم هم well defined specification داشته باشیم (که یعنی یک تعریف در زبان تغییر نمی‌کنه/مگر به ندرت) و هم pseudo-specification که یعنی اون زیر ماجرا «می‌تونه» تغییر کنه وقتی نسخه زبان کمترین بروزرسانی رو دریافت کنه یا حتی کامپایلر به کامپایلر اسخراج مفاهیم متفاوت می‌شن.که با توجه به نبود ABI این مشکل شدت پیدا می‌کنه چون منجر به تولید باینری‌های متفاوت می‌شه که مستقیما باهمدیگه سازگار نیستن! (اینم بگم فقط پشتیبانی از ABI کافی نیست، پایداری هم ملاکه مثلا توی c++ چون پیاده سازی‌های مختلفی داریم بعضی هاشون ABI پایدار ندارن! بعضی‌ها دارن)اگه به Standard Library زبان نگاه کنیم جاهای زیادی به Unsafe Blockها پناه برده که بهشون اجازه میده وقتی نیازه از حصار type system ای که خودشون تعریف کردن بیرون برن به امید اینکه این قطعه کد‌های نا‌امن کپسوله شدن و مشکل جدی پیش نمیاد در هر صورت.یه جورایی یعنی من‌ سازنده زبان می‌دونم چیکار می‌کنم پس اشکالی نداره... ولی خب چون تایپ سیستم این زبان جدیده، «تایید» ادعا‌هاشون هم نیازمند تحقیق و توسعه ابزارهای درخور تحلیل این زبان هست.خلاصه بگم، اهدافشون قابل تحسین هست ولی درست نیست که ذوق زده بشیم و به چشم «گلوله نقره‌ای توسعه نرم افزار» بهش نگاه کنیم. و دلیل نمیشه بیخیال یادگیری عمیق بشیم.انتزاع بدون هزینه pub fn search&lt;&#039;a&gt;(query: &amp;str, contents: &amp;&#039;a str) -&gt; Vec&lt;&amp;&#039;a str&gt; {
    let mut results = Vec::new();
    for line in contents.lines() {
        if line.contains(query) {
              results.push(line);
        }
    }
 results
}

pub fn search&lt;&#039;a&gt;(query: &amp;str, contents: &amp;&#039;a str) -&gt; Vec&lt;&amp;&#039;a str&gt; {
    contents.lines().filter(|line| line.contains(query)).collect()
}این دوتا متد یک کار رو انجام میدن، دومی سریعتره و معادل انتزاعی همون کد هست. بدون هزینه اضافی! درواقع می‌خوام بگم شما «فکر می کنید» که Rust طولانی و خسته کننده هست نه! شما می‌تونید با خیال راحت به صورت «ضمنی» کد بنویسید، فصل‌های اول کتاب برای یادگیری بهتر همه چیز صریح هست و طولانی اما تابع دوم شکلی هست که برنامه‌های واقعی نوشته میشن.موارد دیگه Trait و Generic هستن که توی انتزاع بسیار نقش دارن، با یه مثال بدون کد توضیح میدم که نه شما خسته بشید نه محتوا طولانی بشه (چون چشم شما ممکنه عادت نداشته باشه به Rust و نامفهومه).دربان ساختمانی رو تصور کنید، گفتن یه عده زن و مرد بین 18 تا 30 سال قراره وارد ساختمان بشن برای شرکت در یک رویداد. خب این دربان باید کارت ملی همه رو نگاه کنه که بفهمه هر کس چند سالشه ... بعد اینکه «زن و مرد» اینجا معنی نداره! ما فقط سن رو نیاز داریم درسته؟حالا به همون دربان میگن، هرکسی که «این کارت رو داشت» بهش اجازه بده بره توی رویداد شرکت کنه.انتزاع یعنی این! شما نیاز نیست حتما با تمام جزئیات یه چیزی رو تعریف کنی؛ توی Rust هم کافیه بگی، پارامتر ورودی متد من «هر چیزی هست که، فلان Trait رو داره». درواقع مثل Interface.اینها همه بدون هزینه هستن علتش رو اینجا بخونید. (شاید درباره اینم بعدا مفصل نوشتم)خطایابی آسونتوی Rust خیلی چیزا به صورت پیشفرض Panic میدن - یعنی خطایی که برنامه دیگه قادر به ادامه نیست و خارج می‌شه. برای همین وقتی به شما گفتن کد فلانی رو بخون کافیه در اولین قدم دنبال الگوهایی باشید که خطا تولید می‌کنن.مثلا اگه یه جایی خروجی اون از نوع Option باشه و بعد از Unwap استفاده کنن یعنی این کد اگر خطا بده، برنامه کاملا متوقف میشه! - پس دنبال کردن این سرنخ‌ها بهتون ایده می‌ده که چطوری خطای احتمالی رو بو بکشید.یه نمونه دیگه استفاده از Unsafe Block هست. وقتی کد رو تحلیل می‌کنید مطمئن بشید که چنین بلاک‌هایی به خوبی تعریف شدن.بعلاوه اینها، قابلیت نوشتن Unit Test و Integration Test توی اکوسیستم زبان تنیده شده و بندرت نیاز دارید به یادگیری ابزارهای شخص ثالث.اینجا Null نداریمبله، NULL یا همون اشتباه میلیون دلاری! توی این زبان وجود نداره. «هیچی» داریم، که به صورت یه پرانتز باز و بسته نوشته می‌شه و بهش میگن Unit و «یه چیزی» هم داریم، همین Option هست. یا مقدار بهت می‌ده یا نمیده که برای همین «ممکنه» خطا تولید بشه. (نترسید تو Rust کلی روش هست برای اینکه جلوش رو گرفت).کامپایلر مثل معلم خصوصیlet x = 5;
let mut y = x;
println!(&amp;quot{} - {}&amp;quot, x,y);من گفتم Y مقدارش Mutable هست (یعنی می‌خوام بعدا عوضش کنم) ولی نیازی نیست، چون فقط قراره چاپ بشه و از Scope خارج میشه.بنازم به شعورت ای کامپایلرخب شما بودی عاشقش نمی‌شدی؟ زیرشم خط کشیده !! (مجبور شدم اسکرین شات بگیرم چون ویرایشگر ویرگول پاراگراف رو بهم زد). بدون شک این بهترین قسمت زبان هست.منابع یادگیری تمیز و بروزتوی دنیای جاوا اسکریپت مشکل اینه که یک عالمه محتوای شخص ثالث وجود داره که بروز نشدن و آدمی که می‌خواد تازه یاد بگیره قطعا احتمالش زیاده که با اونها رو به رو بشه و از همون شروع یه چیز غلطی رو دنبال کنه. یا مثلا شما اگه کتاب Rust رو با مستندات Go مقایسه کنی انگار تیم اونها می‌خواسته قضیه رو از سرش باز کنه، تازه کتابی هم که سازنده زبان نوشته پولیه! ...برای من خیلی با ارزش بود که دیدم کتاب Rust انقدر پر محتوا و بروز هست.کلام پایانیحدودا از دو سال پیش خیلی به JIT و بحث Garbage Collection و این حرفا علاقه مند شدم و سعی کردم با JavaScript یه سری پروژه شخصی درست کنم. مثلا یکی از ایده‌ها شبیه raspap-webgui بود با NodeJS و داکر؛ هدفم این بود که هر قسمت از برنامه (مثل DHCP و Access Point و «قند شکن») توی یه کانتینر جدا باشن و ماژولار مثلا Ovpn کار نکرد سریع با کانتینر Tor جایگزین کنم...که به نحوی zero-config باشه و آزادی عمل داشته باشم برای نصبش توی توزیع‌های گنو/لینوکس - و شاید ویندوز‌! بعد با NodeJS یه رابط کاربری و یه خط فرمان درست کنم که یه سری واحدهای اندازه گیری رو از سیستم بخونه، کانتینر‌های داکر رو بالا بیاره و غیره ... (پروژه تورباکس یه نسخه خیلی خلاصه شده از همین ایده هست).از اون موقع تا الان خیلی چیزا درمورد اون ایده تغییر کرده خیلی راه‌ها هست که می‌تونم برم، می‌دونم که «بهینه ترین» فکر ممکن نبوده (مثلا جای داکر، از guix استفاده کنم چون داکر اینجا فقط نقش یه مدیر بسته داره و بعدش خودم فضانام شبکه به پروسه‌ها اختصاص بدم -- شبیه تورباکس).و هدفم از نوشتن این مطلب اینه که، فرقی نداره کدوم زبان رو برای شروع انتخاب کردین؛ اینکه از لحظه شروع تا الان چه چیزی یاد گرفتین خیلی با ارزش تره تا اینکه اون انتخاب بد بوده یا خوب.اینطوری فکر نکنید که Garbage Collection کاملا بده و باید ازش دوری کنید، نه! منتها در اکثر موارد «کم کاری» رخ میده. مثلا زبان ML یکی از معقول ترین پیاده سازی هاست.من خیلی دوست داشتم بنویسم و بنویسم و بنویسم ولی حوصله مخاطب هم مهمه. اگه ازش استقبال بشه درمورد Rust بیشتر می‌نویسم. مثلا یه مطلب جدا کاملا درباره قرض گرفتن و مالکیت داده نیازه توی این مقایسه‌ها نمی‌گنجید.منابع کلی:The Rust Programming LanguageKyle Simpson - Keep Betting On JavaScriptA crash course in just-in-time (JIT) compilersAnders Hejlsberg on Modern Compiler ConstructionMemory Leaks DemystifiedERC Project &amp;amp;amp;quot;RustBelt&amp;amp;amp;quot;</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Sat, 15 Feb 2020 19:22:22 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی HTTP over QUIC و جایگاهش در ایران</title>
                <link>https://virgool.io/@itshaadi/http-over-quic-in-iran-tmgphermlldc</link>
                <description>نشان پیشنهادی برای QUICمی‌خوام در رابطه با HTTP over QUIC صحبت کنم، از مشکلات و مسیر پیشرفت HTTP بگم و بعد نظرات شخصیم رو بر اساس شواهد در رابطه با جایگاهش در ایران بیان کنم.این رو قبلش بگم که تا آخر ماجرا منظورم از QIUC و HTTP/3 مفاهیم زیر هست:وقتی می‌گم QUIC منظورم پروتکل انتقالی هست که توسط IETF داره روش کار می‌شه اولین نسخه اون توسط گوگل توسعه پیدا کرد که طرفای 2013‌ منتشر شد و تا حدودای 2016 روش کار کردن و بعدش هم برخی از اعضای اون تیم رفتن با IETF همکاری کنن ... پس اون نسخه قدیمی تحت عنوان gQUIC شناخته میشه. جزئیات نسخه جدید رو تحت عنوان QUIC و یا QUIC v2 می‌تونید پیدا کنید.وقتی می‌گم HTTP/3 منظور نسخه سوم از پروتکل HTTP هست که روی لایه انتقال QUIC پیاده سازی شده.شروع مسیرنسخه اول HTTP توی سال 1997 معرفی شد، بعدش یه فاصله طولانی داشتیم و توی 2015 نسخه دومش منتشر شد و بلافاصله بعدش داستان‌های نسخه سوم شروع شد.توی نسخه اول داده‌ها به صورت متن واضح و در قالب ASCII ارسال می‌شن، نسخه دوم (بیشتر بدونید) و سوم به صورت رمزنگاری شده و در قالب باینری.HTTP/1 - HTTP/2 Stackاولین روش پیاده سازی به صورت HTTP over TCP بود و بعدش هم HTTPS شروع شد که اساس HTTP/2 رو شکل داد.معایب HTTP over TCPپروتکل TCP برای خیلی سال پیشه، اگرچه خب دستخوش تغییراتی هم شده اما ماهیت کلی اون اینه که بسته‌ها مثل زنجیر هستن و یه سلام و احوالپرسی طولانی باید انجام بشه که تازه بعدش بتونی شروع کنی به ارسال داده. این جوک گویای همه چیزه:Hello, would you like to hear a TCP joke?Yes, I&#x27;d like to hear a TCP joke.OK, I&#x27;ll tell you a TCP joke.OK, I&#x27;ll hear a TCP joke.Are you ready to hear a TCP joke?Yes, I am ready to hear a TCP joke.OK,  I&#x27;m about to send the TCP joke. It will last 10 seconds, it has two  characters, it does not have a setting, it ends with a punchline.OK,  I&#x27;m ready to hear the TCP joke that will last 10 seconds, has two  characters, does not have a setting and will end with a punchline.I&#x27;m sorry, your connection has timed out... ...Hello, would you like to hear a TCP joke?تازه مسئله TCP HOL Blocking هم داریم، یعنی فرض کنید یکی از بسته‌ها کند بشه و دیرتر برسه؛ همه بسته‌های قبلی هم مسدود میشن.تازه این روزها دیگه‌ بخش بزرگی از وب HTTPS هست، یعنی ما دیگه فقط TCP خالی رو نداریم بلکه TLS هم باید یه دور سلام احوالپرسی کنه. درواقع RTT یا همون مجموع مدت زمانی که طول می‌کشه تا یه سیگنال ارسال بشه، و جواب اینکه «بله ما سیگنالتون رو دریافت کردیم»؛ دریافت بشه، خیلی زیاده.الان یه درخواست عادی وب با TLS نسخه 1.2 یا قبل تر، چهارتا RTT انجام می‌ده و اگه دوباره به همون وبسایت وصل بشید 3 تا. ناگفته نماند شما قبلش باید درخواست DNS بزنی و اونم خودش RTT داره مخصوصا اگه DoH استفاده کنید 3 یا 4 RTT دیگه انجام میشه - منتها خب آدم عاقل DNS رو کش می‌کنه یا حالا سیستم‌عامل تا یه حدی براش کش می‌کنه. (اینجا درمورد DoH نوشتم)معایب HTTP/1صحبت‌های این بخش، HTTP/1.1 و هرچی قبلش بوده رو شامل می‌شه، به طور کلی برای سرعت بخشیدن به وب ایده این بود که بیایم چندتا اتصال کوتاه TCP به صورت موازی بزنیم به سرور که معمولا هم 6 تا بیشتر نمی‌شد. یعنی شما حساب کن اون همه عکس و جاوا اسکریپت و CSS (خدا نکنه یکی حالا gif گذاشته بود یا آهنگ پخش می‌کرد وقتی وارد سایت می‌شدی ...) باید همشون توی 6 تا اتصال می‌اومدن روی کامپیوتر کاربر.فاصله زمانی توسعه نسخه 1 و 2 خیلی زیاده برای همینه مباحثی مثل css sprites و چپوندن همه کدهای front-end توی یه فایل بزرگ مد شد، ایده این بود که توی این چندتا اتصال، سریع جمع و جور کنیم بره. که خب کار بدی نبود مرورگرها خیلی از این فایلها رو کش می‌کنن شما یک بار دانلود کنی تا یه مدت طولانی اعتبار داره.اما غافل از اینکه، بسته‌های TCP یکهو که با تمام سرعت ارسال نمی‌شه! کم‌کم، زیاد می‌شه تا وقتی که می‌رسه به حد نهایی (اگه تورنت انجام داده باشین احتمالا متوجه شدین سرعت اولش کمه بعد یکهو زیاد میشه، یه همچین داستانی هست - حالا نه دقیقا - ولی برای درک بهتر اونطوری بهش فکر کنید).بعلاوه اون جوک TCP یادتونه؟ چقدر طول می‌کشید تا اتصال TCP وصل بشه ! حالا فکر کن بخوای تندتند سلام احوالپرسی کنی! این که کند تره!!و در نهایت HTTP HOL Blocking هم وجود داره! یعنی شما نمی‌تونی یقین پیدا کنی کدوم یک از اون 6 تا اتصال سریع تره تا درخواست رو از اون طریق بفرستی. در نتیجه به طور میانگین یک اتصال می‌زدن مرورگرها.معایب HTTP/2خب توی نسخه دوم، یک اتصال TCP به چندین جریان موازی تقسیم می‌شه که اینجا دیگه TCP HOL Blocking اتفاق می‌افته و بالاتر گفتم. شما اگه یکی از این جریان ها رو از دست بدی بقیه شونم بلاک می‌شن. به تعبیری سرعت شما برابره با سرعت کند ترین جریان توی اون اتصال TCP.بحث RTT هم وجود داره که خب اونم اشاره کردم، حالا البته TLS 1.3 معرفی شد که هزینه اتصال جدید و اتصال مجدد ثابته (هردو سه تا RTT) بعد یه بحث دیگه مطرح شد تحت عنوان 0-RTT یعنی درواقع دفعات بعدی که سایت رو بازدید می‌کنی دیگه Full TLS Handshake انجام نده. که فعال کردن این ویژگی باعث می‌شه آدمای بد بتونن Replay Attack انجام بدن. (یکی درخواست رمزنگاری شده شما رو دریافت می‌کنه، اونو دوباره می‌فرسته به سرور اصلی و وانمود می‌کنه که شماست -- پایین تر توضیح میدم تاثیرش توی ایران رو).معرفی پروتکل QUICQUIC Stackاما پروتکلی که درحال استاندارد شدنه. یک لایه انتقال هست و درواقع HTTP/3 به عنوان Application Layer روی این لایه سوار می‌شه.مجموع این حرفها یعنی ممکنه DNS over QUIC هم بیاد و خیلی چیزای دیگه !!برخلاف TCP؛ این پروتکل جدید اتصالات رو به یک IP مرتبط نمی‌کنه و به جاش از Connection-ID استفاده می‌کنه که یعنی اگه از روی WIFI بریم روی 4G خودش می‌فهمه که ما همون اتصال قبلی هستیم.این پروتکل مشکل HOL Blocking نداره، درواقع جریانها موازی و ناپیوسته ایجاد می‌شن برای همین اگر چندتا جریان داده دچار مشکل بشن، فقط همون چندتا هستن که دیرتر می‌رسن و بقیه به مسیرشون ادامه می‌دن بعلاوه خب چون از TLS1.3 استفاده می‌کنه می‌تونه یک اتصال با Zero-RTT ایجادکنه و خب این باعث می‌شه مثل TFO بتونیم ارسال داده رو زودتر آغاز کنیم (و همزمان معایب TFO رو نداشته باشیم -- در ادامه بحث رو باز می‌کنم).اما خب QUIC یه لایه انتقال هست روی یک لایه انتقال دیگه (UDP) حالا درسته به مراتب سریعتر از TCP اما خود پروتکل UDP هنوزم نیاز به بهینه سازی داره. جدای از این بحث‌ها QUIC یک user space protocol هست. یعنی هرکسی می‌تونه (با رعایت قوانین حاکم) نسخه خودش رو بزنه که بازم «می‌تونه» با نسخه یه شرکت/گروه دیگه چند درجه متفاوت باشه. مسئله ای که ما سر DoH و DoT داشتیم و داریم اینجا باز تکرار شده.اینکه هرکسی بتونه نسخه خودشو بزنه در اوایل خوبه اما در آینده ممکنه توروالدز تصمیم بگیره یکی از این نسخه‌ها رو توی کرنل قرار بده بعد مایکروسافت یه نسخه کمی متفاوت تر رو قرار بده ... اونوقت می‌شه با آنالیز تفاوت بسته‌ها فهمید کی ویندوز داره و کی لینوکس (بلایی که سر TCP اومده، پکت‌ها توی ویندوز و لینوکس تفاوت‌هایی دارن که تشخیص سیستم‌عامل رو ممکن می‌کنه)اگه به تصویر دقت کنید می‌بینید که TLS داخل QUIC استفاده می‌شه یعنی ما داده ها رو به لایه بالاتر انتقال نمی‌دیم بلکه یک سری از پیغامهای TLS رو مستقیم داخل خود QUIC استفاده می‌کنیم این یعنی کتابخانه‌های موجود برای TLS باید بروزرسانی بشن و این سبک جدید از «احوالپرسی» رو پشتیبانی کنن. و خود این موضوع به تنهایی یک بحث خیلی گسترده هست که من قبلا درموردش توییت هایی زدم.بحث اصلی اینه که پیداکردن کتابخانه‌(ها)ای که بهت اجازه بدن «تمام فیلد‌های TLS» رو تنظیم کنی، سخته. شاید بشه uTLS رو مثال زد اما حتی اونم جای خطا داره. فکر کن یه API جدید نیازه که به علت پیشرفت سریعتر QUIC؛ قطعا کامل نیست. این بحث انقدر مهمه که حتی کتابخانه پولی‌ هم در اومده براش.تا اینجای کار چون فقط HTTP/3 رو داریم که روی QUIC پیاده شده به نحوی میشه گفت این دو برابر هستن. اما یه سوال پیش میاد، چطوری بفهمیمhttps://virgool.ioمی‌خواد با QUIC ارتباط برقرار کنه یا با HTTP over TCP؟ - جوابش فیلد Alt-Svc هست. فرض کنید آدرس یه محله به شما دادن ولی نمی‌دونید کدوم پلاک مال خونه‌ای هست که مد نظرشماست. از یه نفر می‌پرسید «ببخشید منزل فلانی کجاست؟» و اون شخص به سمت منزل «اشاره» می‌کنه. این فیلد «اشاره می‌کنه» که راستی، من روی UDP/443 هم سرویس میدم. پس اولین درخواست شما (حداقل در لحظه نوشتن این مقاله) یک درخواست TCP هست و بعدش با خوندن Alt-Svc مرورگر می‌فهمه که سرور شما از QUIC پشتیبانی می‌کنه. در آینده می‌تونه برعکس باشه... و بگه من تا فلان روز و فلان ساعت از سرویس HTTP Over TCP پشتیبانی می‌کنم.جایگاه (احتمالی) QUIC در ایراناین قسمت رو گذاشتم آخر چون حکم نتیجه گیری داره و اینکه بیشترش نظر شخصی من هست بر اساس شواهد موجود.اول اینکه، اینترنت کلی مسیریاب و فایروال و اصطلاحا Middlebox داره که شرکت‌ها (چه داخل ایران و چه از دید بین المللی) هزینه زیادی بابتشون دادن. و اشاره کردم فاصله بین HTTP/1 تا الان خیلی زیاده در نتیجه دستگاه‌های زیادی هستن که باید بروز بشن (حالا دیر یا زود یا اینکه اصلا ممکنه بروز نشه بماند).دوم اینکه مفهومی هست به اسم &quot;ossification&quot; یا استخراج و بهینه سازی. به این معنا که دستگاه مثلا برای استخراج و بهینه کردن HTTP/1 الگوریتم داره ولی HTTP/2 رو تشخیص نمیده در نتیجه خیال می‌کنه همون نسخه قبلیه و کار رو خراب می‌کنه. برای همینه که HTTP/2 استفاده از TLS رو الزامی کرده تا نشه دستکاری بی‌جا انجام داد. شما هنوزم می‌تونی برای آزمایش کردن اون رو به صورت متن واضح ارسال کنی!بالاتر گفتم TFO ایراد داره، خب این ویژگی ده سال پیش اضافه شده به TCP و اینطوریه که یه سری بایت اضافی ارسال می‌کنه که بگه من درخواست TFO هستم بعد خیلی از Middlebox ها بروز نشدن یا به دلایلی نمی‌خوان که اینو به رسمیت بشناسن در نتیجه drop میشه. حالا کلاینت بازم باید یه درخواست TCP معمولی بزنه و دوباره کاری میشه. برای همین میزان خیلی کمی از TFO استفاده می‌کنن و موفق نبود. (یه دلیل دیگه رو توی توییت‌هام گفتم).سوم اینکه، فعلا شواهد کافی نیست که یقین پیدا کرد QUIC در ایران عمدا قطع می‌شه یا Middleboxها بروز نشدن. صادقانه بگم، استفاده از QUIC به عنوان لایه انتقال در ابزارهای ضدسانسور قبلا هم انجام شده. و خب به خاطر استفاده از zero-RTT میشه بهش Replay Attack زد که یقین پیدا کرد این اتصال فیلترشکن هست یا نه (البته این موش و گربه بازی ادامه داره، میشه جلوش رو گرفت). یا اصلا به خاطر استفاده بسیار کمش به طور کلی مسدود کنن.هرچند deploy کردن QUIC روی Edge Server ها و مرورگرها کاری به مراتب راحت تره، اتفاقا کلاودفلر همین الان از این پروتکل پشتیبانی می‌کنه. شاید اگه ابرآروان و سایر CDN‌ های ایرانی هم پیش قدم بشن بتونیم امیدوارتر بشیم.اما خب هیچ کدوم از این تکنولوژی‌های جدید «گلوله نقره ای» نیستن، مثلا قبلاهم TLS 1.3 باعث خراب شدن Middleboxها شده. و راه‌هایی هست که کلاینت رو «مجبور کرد» که از TLS 1.2 استفاده کنه که به بهانه اون حتی از ESNI محروم بشیم!</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Fri, 17 Jan 2020 04:53:05 +0330</pubDate>
            </item>
                    <item>
                <title>نگاهی عمیق به Firecracker microvm</title>
                <link>https://virgool.io/@itshaadi/aws-firecracker-deep-dive-aatsy29agbx2</link>
                <description>از وقتی که Firecracker توسط آمازون منتشر شد همیشه دوست داشتم یه نگاه مفهومی بهش بندازم و ببینم چرا تصمیم گرفتن چرخ رو از اول اختراع کنن!؟ هرچیزی که توسط غولی مثل آمازون قراره ارائه بشه باید بسیار سریع و امن باشه و طبیعتا وقتی تصمیم می‌گیرن به جای استفاده از راه‌حل های موجود روش خودشون رو پیاده سازی کنن، موضوع جذاب تر هم می‌شه.به طور خلاصه، این ابزار موتور AWS Lambda هست که برای رایانش بی‌سرور استفاده میشه. مزیتش اینه که دقیقا بر اساس منابعی که توسط کد شما مصرف شده هزینه پرداخت می‌کنید و به حضور یک sysadmin توی تیم نیاز نیست چونکه تمام قضیه از بالا آوردن سیستم عامل به صورت مجازی و تنظیم فایروال و ... همشون توسط شرکت ارائه دهنده این خدمت انجام می‌شه. -- تاحالا توی ایران سرویسی برای FaaS ندیدم.ابزاری که آمازون ارائه کرده می‌تونه لینوکس رو توی 125 میلی ثانیه اجرا کنه و جالب اینجاست که محیط‌های lambda به روش «مجازی سازی سخت‌افزار» ازهم جدا شدن. -- با شنیدن این عبارت شاید یاد Virtual Box بیوفتین، بله! منظورم همونه! اما خیلی جذاب تر.یه مجازی ساز سخت‌افزاری چطوری کار‌می کنه؟قبل از اینکه بخوام Firecracker رو توضیح بدم اولش لازمه که بفهمیم با مجازی ساز چطوری ویندوز رو توی گنو/لینوکس اجرا می‌کنن؟ یا چطوری می‌تونن MacOS رو اجرا کنن بدون اینکه صاحب مک بوک باشن.اینها رو عمدا مثال زدم چون سورس کد سیستم عامل در دسترس عموم نیست. درواقع چطوری می‌تونیم درایورها، دستگاه‌ها و غیره رو برای یک سیستم عاملی که عملا نمی‌تونیم دقیقا بفهمیم به چی نیاز داره رو فراهم کنیم؟ابزاری مثل QEMU یا Virtual Box برای اجرای همچین چیزی باید روتین‌های BIOS یا ماژول‌های قدیمی، درایور کارت صدا، گرافیک، شبکه و ... رو شبیه سازی کنه.درواقع شبیه سازی BIOS و حضور boot loader در این شرایط الزامیه؛ بدون این مرحله مجازی ساز‌ها نمی‌تونن خیلی از این سیستم‌عامل‌ها رو اجرا کنن.سکوی Firecracker فقط برای اجرای کرنل لینوکس پیاده سازی شده و برای این کار به KVM و بخش زیادی از سورس کد پروژه crosvm تکیه می‌کنه. درواقع KVM خودش به تنهایی سخت افزار‌های چندانی رو شبیه‌سازی نمی‌کنه برای همین انتخاب بهینه‌ای هست.چیزی که از این مفاهیم باید برداشت کنید اینه که یک مجازی ساز «معمولا» سعی‌ می‌کنه تا جایی که ممکنه به یک رایانه واقعی نزدیک باشه برای همین مجبوره گستره متنوعی از سخت افزار‌ها رو شبیه سازی کنه و این کار خیلی سرعت اجرا رو میاره پایین! (حتی اگه از SSD هم استفاده کنید خیلی ربطی نداره هنوزم قضیه کند هست قطعا نه به اندازه وقتی که HDD دارین اما بازم 125 میلی ثانیه نیست !!!)اما آیا لازمه «همیشه» boot loader و BIOS حضور داشته باشن؟نحوه لود شدن کرنل لینوکسخب وقتی که یک رایانه معمولی اجرا می‌شه (من مثالم برای رایانه‌هایی هست که همه داریم، برای معماری‌های دیگه طبیعتا فرق‌هایی داره) پردازنده توی یک حالت 16 بیتی شروع به کار می‌کنه.بایوس هم POST رو انجام میده و بعدش boot loader رو توی RAM فراخوانی می‌کنه و کنترل همه‌چیز رو میده بهش. بعد boot loader از روتین‌های تعریف شده توی BIOS استفاده می‌کنه که متنی روی صفحه نمایش بده یا فضای ذخیره سازی رو بخونه، مشخصات سیستم رو به دست بیاره و «با کرنل ارتباط برقرار کنه». پس درواقع حضور BIOS برای خود لینوکس نیاز نیست و این boot loader هست که بهش نیاز داره.یه جورایی حتی boot loader هم نیاز نیست. کرنل یه چیزی داره به اسم Boot Protocol که «هرپردازشی» که در زمان بوت درحال اجراست می‌تونه باهاش ارتباط برقرار کنه و از طریق این پروتکل بفهمه چطوری اونو اجرا کنه و ساختار داده‌ها رو توی RAM بچینه و در نهایت چطوری کنترل همه چیز به کرنل سپرده بشه.پس، Firecracker خودش کرنل رو لود می‌کنه از طریق همین پروتکل (layout.rs - mod.rs) در نتیجه دیگه نیازی به BIOS نداریم و حضور یک boot loader عمومی که کلی edge case رو باید رعایت کنه هم الزامی نیست وقتی «هرپردازشی» بتونه با کرنل حرف بزنه. همین یک قدم در گفتار چیز ساده‌ای به نظر میاد اما وقتی قابلیت‌های Virtual Box رو ببینید درک می‌کنید که این مرحله چقدر در سبک شدن ماجرا تاثیر داره.بعلاوه اینها، یک سری عملیات / دستورات وجود دارن که منجر به رخ دادن رویدادی تحت عنوان VM exit میشن. هرچی این اتفاق کمتر بیوفته مجازی ساز سریعتر عمل می‌کنه. به عنوان مثال دستوراتی که برای انجام عملیات‌های I/O نیاز هست مثل OUT منجر به VM exit میشن.اما توی Firecracker برای انجام اکثر عملیات‌ها از MMIO استفاده می‌کنه به زبان ساده یعنی به جای استفاده از دستوراتی مثل IN و OUT برای انجام عملیات‌های I/O از دستورات عادی تر مثل mov استفاده می‌کنه. درواقع یک سری مقدار خاص توی یک قسمت ویژه از RAM قرار گرفتن که با mov کردن این مقادیر عملیات‌های I/O رو انجام میده.وقتی آدرس‌های MMIO دستکاری بشن رویداد KVM exit اتفاق میوفته. مزیتش اینه که VMM می‌تونه مشخص کنه کی این اتفاق انجام بشه پس کنترل بیشتری روی «زمان و تعداد دفعات رخ دادن رویداد» وجود داره.اگر بخوام وارد جزئیات بشم به این صورته که هروقت VM exit اتفاق بیوفته، VMM بر اساس مقادیری که توی MMIO دستکاری شدن می‌فهمه که ورودی و خروجی چه دستگاهی بوده و بعد داده مربوطه رو به دستگاه موردنظر وصل می‌کنه. مثلا برای کنترل ماشین مجازی و دریافت خروجی (که به صورت متن هست) از یک کنسول سریال استفاده می‌کنه و اون رو به کرنل داخل ماشین مجازی وصل می‌کنه. (سورس کد)مثلا اینجا رو ببینید این صفحه مربوط به job log پروسه‌ای هست که توسط CI انجام شده این «دقیقا» خروجی کنسول هست! [من به AWS دسرسی ندارم برای همین از travis-ci مثال اوردم ولی این قسمت نمایش کنسول مفهومی یکسان هست بین مجازی سازها]. یادتون باشه کنسول در اینجا به معنی خط فرمان هست و همیشه خروجی اون صفحه نمایش نیست، قدیم کاغذ بود! این ویدیو جذاب رو ببینید.مدل میزبان و دستگاه‌های پشتیبانی شدهیک قسمت جالب دیگه تعداد محدود دستگاه‌هایی هست که Firecracker توی هر‌ ماشین مجازی پشتیبانی می‌کنه. لینوکس توی معماری x86 فرض می‌کنه که interrupt controller و interval timer وجود دارن. این دو قطعه بخش هایی هستن که یک CPU به صورت پیشفرض اونها رو نداره پس باید شبیه سازی بشن.شبیه به مثال کنسول، VMM می‌تونه این دوتا دستگاه رو به دوتا دستگاه موجود روی سیستم میزبان وصل کنه ولیکن این کار منجر به VM exit میشه. یادتونه؟ هرچقدر بیشتر توی کرنل بمونیم و یا دستورات ماشین مجازی اجرا بشن، سرعت و کارایی بالاتره تا اینکه که مجبور به VM exit بشیم.خوشبختانه KVM یه راهی داره که می‌تونه چیپ‌های i8259 و i8254 رو داخل کرنل شبیه سازی کنه‌! یعنی اصلا نیازی به VM exit نیست. خودمم نمی‌دونم چطوری، درکش برام سخته فقط با دنبال کردن سورس کد Firecracker به KVM_CREATE_IRQCHIP و KVM_CREATE_PIT2 رسیدم که توی مستندات خودش کلی توضیحات داده دربارشون. اولی برای interrupt controller هست، دومی برای interval timerیه سری چیزای دیگه هست که دوست دارم بدونید مثلا i8042.rs شبیه ساز کنترلر ماوس و کیبورد PS/2 هست. یا serial.rs تمام چیزیه که نیاز داره برای گرفتن ورودی و خروجی. هرچند هنوزم به کارت شبکه نیازه که برای این کار از VirtIO Net استفاده می‌کنه. و VirtIO Block هم که مشخصه دیگه برای دیسک و فضای ذخیره سازی لازمه. همین ! این تمام چیزاییه که نیازه برای اجرای ماشین مجازی.پس امنیت چطوری تامین میشه؟چرا آمازون اصرار داشته به اینکه از ماشین مجازی استفاده کنه؟ درحالی که گوگل، یک غول دیگه تمام ناوگان cloud console رو با کانتینر اداره می‌کنه؟دلیلش سادست، وقتی که از حصار یک container فرار کنید تمام API لینوکس در اختیار شماست! ولی با فرار از حصار یک ماشین مجازی دسترسی بسیار محدود تره.این ویدیو رو ببینید درواقع یک نفر تونسته با فرار از حصار کانتینری که cloud console ارائه می‌کنه به سیستم عامل میزبان دسترسی پیدا کنه. اما این مسئله برای گوگل باگ امنیتی نیست چون اونها کانتینر‌ها رو روی یک ماشین مجازی اجرا می‌کنن (ویدیو رو کامل ببینید قضیه طولانیه، یه کانتینر داکر در اختیار کاربر نهایی قرار داره که خود اون کانتینر توی یه کانتینر دیگه قرار گرفته !!! و بعد کل این کانتینر روی یه ماشین مجازی اجرا میشه همش به خاطر اینکه «وقتی/اگه» یکی از namespace تعریف شده فرار کرد، سطوح دیگه‌ای برای جلوگیری وجود داشته باشه)آمازون عاقلانه تر عمل کرده [به نظر من] و به جای درست کردن قلعه با بالش، وقت گذاشته و قضیه رو از ریشه کنترل کرده.ناگفته نماند، توی یه کانتینر داکر با دسترسی root می‌تونید هر دستگاه ورودی / خروجی یا مسیر ذخیره سازی رو mount کنید. اما Firecracker کلا از دستگاه‌های محدودی پشتیبانی می‌کنه.ولی قضیه به اینجا ختم نمیشه. تیم آمازون خیلی جدی تر به این مسئله فکر کرده، مثلا از chroot و cgroups استفاده می‌کنه و علاوه بر اون یک سری قوانین Seccomp هم تعریف کرده یعنی حتی پروسه Firecracker هم فقط به چیزایی که «دقیقا» لازمه دسترسی داره نه بیشتر و این حیرت انگیزه.نتیجه گیریخب با توجه به تلاش‌های آمازون و راه حلی که پیاده شده در نهایت کاملا واضحه که اختراع چرخ واقعا لازم بوده. البته  ابزار‌های دیگه ای هستن که کار مشابه رو انجام میدن مثلا katacontainers یه رابط Kubernetes ارائه می‌کنه ولیکن در عین حال هر کانتینر به صورت سخت افزاری مجزا شده. یا تلاش اینتل برای لخت کردن QEMU منجر به NEMU شد ایدش اینه که فقط از لینوکس پشتیبانی کنه برای همین خیلی از درایورها و چیزای دیگه که نیازه تا «هر سیستم‌عاملی» اجرا بشه رو برداشتن ولی هنوزم بوت لودر و بایوس وجود دارن منتها بهینه شدن. [البته NEMU ممکنه در آینده از windows server هم پشتیبانی کنه].به هرحال شاید نشه گفت مخترع این قضیه آمازون هست اما تمیز ترین محصول رو قطعا اونها پیاده سازی کردن.</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Wed, 13 Nov 2019 04:03:01 +0330</pubDate>
            </item>
                    <item>
                <title>نگاهی عمیق به DNS over HTTPS</title>
                <link>https://virgool.io/@itshaadi/deep-dive-into-doh-tjarleupuver</link>
                <description>می‌خوام درمورد DNS over HTTPS (DoH) صحبت کنم. چی هست؟ چطوری کار می‌کنه؟ چرا مهمه که همه شروع کنیم به استفاده کردنش؟ و چطوری شروع کنیم.یک مقدمه خـــیـــلـــی خلاصه از DNS: همین سایت ویرگول رو در نظر بگیرید آدمها تایپ می‌کنن virgool.io اما کامپیوتر باید یه راهی پیدا کنه تا اون رو تبدیل کنه به 172.64.201.24 که بتونه باهاش ارتباط برقرار کنه. تکنولوژی این کار متعلق به 35 سال پیش هست، زمانی که اینترنت خیلی ساده کار می‌کرد و به اندازه امروز دله دزد و سانسور چی و «علی حکر» نداشت.الان ما توی زمانه‌ای زندگی می‌کنیم که با متادیتا‌ها میشه آدم کشت! درواقع DNS زیادی دهنش لقه خیلی بی‌ریا آدرس سایتی که بازدید می‌کنید رو جار میزنه. آدم‌های زیادی هستن که خوراکشون خریدن این داده‌ها و تحلیل اونهاست. مثلا ازش برای تبلیغات استفاده می‌کنن، بدون اینکه بدونن شما کی هستین می‌فهمن که چی دوست دارین و همون رو بهتون نشون میدن. [اگه به اندازه کافی متادیتا به دست بیارن می‌تونن یقین پیدا کنن که شما کی هستین.]جدای از این، شرکت‌های ارائه دهنده اینترنت می‌تونن با DNS hijacking جلوی دسترسی به سایت‌ها رو بگیرن که این قضیه جز DPI محسوب نمیشه در نتیجه از نظر قانونی (و فنی) منع کمتری داره - مثلا توی حادثه تیراندازی مسجدهای کرایست‌چرچ ارائه دهنده‌های اینترنتی نیوزیلند از این روش استفاده کردن برای محدود کردن دسترسی کاربرها (منبع)پس این DNS over HTTPS چی‌هست؟ چطوری‌ کار می‌کنه؟به شکل خلاصه؛ درخواست‌های DNS رو در بدنه یک درخواست HTTP می‌پیچیم. این باعث می شه بتونیم از TLS استفاده کنیم در نتیجه درخواست‌ها رو در قالب HTTPS ارسال کنیم.اینکه دقیقا چطوری‌ کار‌ می‌کنه جای بحث داره. مشخصات لازم برای درست کردن یک سرور و کلاینت DoH طوری تعریف شده که دست توسعه دهنده‌ها برای پیاده سازی اون نسبتا باز هست (باز تر از چیزی مثل TCP برای همین هر کسی می‌تونه ایده خودش رو با رعایت مقررات ذکر شده پیاده کنه اما محدود بهشون نیست.) Implementations of DoH clients and servers need to consider the   benefit and privacy impact of these features, and their deployment   context, when deciding whether or not to enable them.   Implementations are advised to expose the minimal set of data needed   to achieve the desired feature set. (rfc8484 page 13)These requirements are listed here to help readers understand the current protocol, not to limit how the protocol might be developed in the future. (rfc8484 Appendix A)چیزایی که لازمه:پروتکل باید از شماتیک و مفاهیم HTTP استفاده کنه [پس حق ندارین پروتکل شخصی توسعه بدین]کوئری (پرسش‌های سمت کلاینت) و پاسخ‌ها (جواب سمت سرور) باید مثل DNS معمولی (*) باشه (سعی خودش رو بکنه که شبیه به اون در بیاد -- انعطاف پذیر باشه) اما درخواست‌هایی که نیازدارن به دریافت چندتا پاسخ رو نیاز نیست پیاده سازی کنید.پروتکل باید قالب و ساختارهای جدیدی که [در آینده] برای DNS در نظر گرفته می‌شه رو پیاده سازی کنه. [پا به پای ساختار  DNS بره جلو، خودش قالب جدیدی تولید نکنه]پروتکل باید https استفاده کنه (شرایطی رو پیروی کنه که بر عملکرد https حاکمه)نسخه دوم HTTP/2 به عنوان «حداقل» پیش نیاز پیاده سازی این پروتکل هست.حداقل متادیتای لازم رو استفاده کنه [چیزایی مثل User-Agent و Accept-Language جز پروتکل http هستن اینا هنوزم می‌تونن کاربرها رو لو بدن؛ مگر ثابت بشه که وجودش نیازه در غیر این صورت پیاده سازی اون پیشنهاد نمیشه]* منظور DNS over UDP هست.با این چندتا شرط میشه فهمید که DoH درواقع یک تونل هست نه یک پروتکل جدید. برای همین شرکت Cloudflare تونسته نسخه خودش رو درست کنه (که یه قضیه جالب هست در ادامه اونم کالبد شکافی می‌کنم)مشکلات منطقی در رابطه با DoHاینا دلیل نمیشه که بگیم DoH خرابه! بلکه مشکلات منطقی هستن که راه حل خودشون رو دارن. شاید «بهترین راه حل» نباشن. اما «یه راه حل» هستن.خب ساختار زیر رو در نظر بگیرید:GET myserver.com/dns-query?dns=AAABAAABAAAAAAAAAWE-NjJjaGFyYWN0ZXJsYWJlمن قراره یک درخواست GET بفرستم به myserver.com تا اون به من IP گوگل رو بده. خب کی قراره IP خود myserver.com رو بده؟ نمیشه که از HOST NAME بپرسیم آدرس IP خودت چیه؟سه تا راه حل داره:آدرس IP سرور داخل برنامه کلاینت کد نویسی بشه (hard-coded) -- یا داخل فایل hosts سیستم میزبان نوشته بشه.به جای آدرس دامنه مستقیم از آدرس IP استفاده کنیم (اصلا دامنه ثبت نکنیم IP رو همه بتونن استفاده کنن به صورت مستقیم نیازی هم به هارد کد کردنش نباشه).از یه DNS دیگه آدرس این سرور رو درخواست بدیم بعد بقیه درخواست‌ها رو به سرور DoH بفرستیمبه عنوان مثال فایرفاکس الان از گزینه اول استفاده می‌کنه. آدرس سرور‌های کلاودفلر رو داخل سورس خودش ذخیره کرده.مشکل منطقی دیگه به انتخاب http به عنوان قالب داده مربوط میشه. دوتاش رو بالا مثال زدم که باعث لو رفتن وضعیت کاربر میشن. بیاید یه لایه بریم پایین تر، http روی لایه TCP سوار شده.و توی لایه TCP هرچقدر که اتصال طولانی تر باشه کیفیت اون بهتره. اما اتصال طولانی باعث به وجود اومدن همبستگی داده‌ (data correlation) می‌شه. به عبارت دیگه اگه خیلی به یه چیزی وصل باشین متادیتا تولید میشه.از یه جهت دیگه نگاه کنیم، اگر سرور قابلیت TCP Fast Open رو پیاده سازی کنه به خاطر استفاده از کوکی باعث می‌شه سشن های TCP به هم ارتباط پیدا کنن (بازم متادیتا تولید می‌شه).راه حل؟ «خواهی نشوی رسوا، همرنگ جماعت شو» -- اگر خودتون می‌خواین سرور DoH رو میزبانی کنید اونو پشت یه CDN قایم کنید. درخواست های زیادی روزانه به سمت CDN ها میرن و در نتیجه داده شما بین اون همه داده گم می‌شه.نکته مهم: اگر سرور «تحریم شکن» دارین؛ هیچوقت روی همون سرور DoH هم میزبانی نکنید. [قیمه ها رو نریزید تو ماستا]. به خاطر اینکه میزان تمرکز شما روی اون سرور زیاد میشه. تمرکز زیاد متادیتا به وجود میاره.فرض کنید روی اون سرور OpenVPN میزبانی می‌کنید و الان DoH هم اضافه شده. خب دوتا جریان داده به وجود میاد که میشه ازشون متادیتا استخراج کرد و چون هردوتاشون به یه نقطه وصل میشن، میشه اینا رو بهم مرتبط کرد تا به یک «یقین» رسید.یادتون باشه، الگوریتمها چه تبلیغاتی و چه سانسورینگ بر اساس یقین عمل می کنن، نه شک و شبهه هرچقدر میزان یقین کمتر باشه شما بیشتر در امان هستین.نحوه استفاده فایرفاکس از DoH ارائه شده توسط cloudflareتا اینجای داستان فهمیدیم DoH چیه و چطوری کار می‌کنه. ولی گفتم دست توسعه دهنده‌ها بازه مثلا کلاودفلر به یه شکل جذاب اون رو پیاده سازی کرده.توی مسیر ارسال درخواست سه تا نقطه وجود داره که داده می‌تونه دستکاری بشه (این مربوط بحث اعتماد هست)داده می‌تونه توسط resolver شنود بشه، می‌تونه در راه رسیدن به DNS Server دستکاری بشه و یا خود سرور یه مقصد غیرقابل اعتماد باشه.خب برای امن کردن مسیر انتقال می‌تونیم از DoH استفاده کنیم. اما کسی که DoH رو ارائه میده (resolver) باید مورد اعتماد باشه درسته؟ همچنین اون resolver باید خودش یه راهی پیدا کنه که مطمئن بشه سرور DNS قابل اعتماد هست.اینجا فایرفاکس می‌گه من کلاینت DoH رو توی سورس کد خودم اضافه می‌کنم و بعدش به تو (کلاودفلر) اعتماد می‌کنم که resolver من بشی. تو هم باید یه راهی پیدا کنی که به DNS Server ها اعتماد کنی.چرا فایرفاکس همچین کاری می‌کنه؟ خب کلاودفلر یه شرکت شناخته شده هست که خدمات زیادی ارائه میده مهم ترین اون خدمات CDN هست (یادتونه؟ همرنگ جماعت شدن...) بعلاوه کلاودفلر بحث حذف داده‌ها رو ماموریت خودش قرار داده و بر اون نظارت می‌کنه. طبق خط مشی اونها داده‌ها بعد از 24 ساعت از بین میرن.چه داده ای؟ چه چیزایی قراره به کلاودفلر بره؟من یه چیزی رو اول مطلب نگفتم درمورد DNS معمولی، اینکه دقیقا چه چیزایی رو جار میزنه و چرا؟یک درخواست DNS معمولی آدرس IP شما و آدرس کامل دامنه‌ای که می‌خواید ببینید رو ارسال می‌کنه. اما چه نیازی هست به اینکه کاملا جار بزنه آدرس IP شما چیه؟ به خاطر اینکه موقعیت شما رو تشخیص بده و درخواست رو بفرسته به نزدیک ترین سرور DNS موجود. [این قسمت رو حتما یادتون باشه دوباره بخونید اگر نفهمیدین]یه درخواست DoH چطور؟ گفتیم که باید «سعی کنه» مثل درخواست DNS باشه. پس یه سرور معمولی DoH که شما بخوای به صورت خود مختار میزبانی کنی هنوزم نیاز داره آدرس IP تون رو ذخیره کنه تا اون رو به نزدیک ترین DNS بفرسته فقط اونو به صورت https می‌فرسته که کسی نبینه اما مقصد نهایی می‌فهمه که شما کی هستین.یه تیکه مهم دیگه (بالا بهش اشاره کردم):  Implementations are advised to expose the minimal set of data neededیعنی پیشنهاد میشه که [اگر می‌تونید] کمترین متادیتای ممکن رو به کار ببرید / تولید کنید. خب کلاودفلر می‌تونه.این چیزیه که cloudflare برای پیاده سازی DoH نیاز داره. داده ها هنوز «شبیه» درخواست DNS هستن اما خیلی خیلی نامفهوم شدن.آدرس IP شما به طور کل حذف شده اصلا نیاز نیست یادداشت بشهآدرس دامنه ای که درخواست می‌کنید حذف شده فقط TLD ارسال می‌شه (در این مثال org)چطوری ممکنه؟ خب کلاودفلر یک CDN هست درسته؟ یعنی شبکه توزیع محتوا. پس کلاودفلر می‌تونه به جای آدرس IP شما، آدرس IP نزدیک ترین سرور خودش به شما رو بفرسته برای سرور DNSاما چطوری فقط TLD رو نیاز داره؟ با استفاده از تکنیک Query Name Minimisation که خودش یه مقاله می‌شه.پس نتیجه می‌گیریم کسی که DoH رو پیاده سازی کرده بسیار مهمه.چه چیزایی هنوزم قابل شنود هستن؟خب ببنید اولین باری که مرورگر درخواست دیدن یک وبسایت رو میده یک بسته SNI ارسال می‌کنه. توی این بسته آدرس کامل دامنه ذکر شده و متاسفانه رمزنگاری نشده. این یعنی ISP هنوزم می‌تونه با شنود کردن این بسته بفهمه مقصد شما کجاست و اون درخواست رو متوقف کنه.این «شنود کردن» از نوع Deep Packet Inspection هست. سرویس دهنده اینترنت خودش نمی‌تونه همچین حرکتی انجام بده قانونی نیست. مگر به فرمان قضایی و دولتی. و اینم بدونید این عملیات هم از نظر تجهیزات و هم مالی هزینه بر هست.برای همینه که توی چین، ایران، سوریه، قزاقستان و ... نمی‌تونید با DoH سانسور رو دور بزنید پس منع قانونی نداره استفاده ازش.در آینده ESNI هم فراگیر میشه درواقع یه راهی پیدا شده که درخواست SNI رو رمزنگاری کرد اونموقع تجهیزات شنود هم باید خیلی پیشرفته تر بشن. چون متادیتا به اندازه کافی تولید نمیشه که به یقین برسن.به عنوان آخرین نکنه این بحث، یادتونه گفتم سرور DoH باید حد اقل از https/2 پشتیبانی کنه؟ به خاطر اینکه توی این نسخه یه ویژگی هست به اسم connection coalescing درواقع یعنی استفاده مجدد از اتصال قبلی.وقتی دوباره به یک وبسایت وصل بشید دیگه درخواست SNI ارسال نمیشه یعنی متادیتایی که تولید میشه فقط یک بار اتفاق میوفته در نتیجه بازم میزان پیدا کردن یقین کاهش پیدا می‌کنه. (این موضوع در گذشته رعایت نمی‌شد مرورگرها انقدر احمق بودن که نمی‌تونستن بفهمن SNI ارسال شده و همیشه ارسالش می کردن اما از فایرفاکس 62 به بعد این مشکل برطرف شده -- درمورد مرورگرهای دیگه نمی‌دونم)با وجود CDN ها، چه داخلی و چه خارجی آدمهای زیادی حالا دیگه به یک نقطه وصل می‌شن این نشون میده فیلتر کردن اون نقطه منطقی نیست چون خیلی چیزا تحت تاثیر اون قرار می‌گیرن. حتی دولت چین هم کلاودفلر رو فیلتر نکرده! درضمن CDN ها باعث می‌شن تعداد دفعاتی که SNI فرستاده می‌شه کمتر بشه. مثلا وقتی x و y هردو روی کلاودفلر میزبانی بشن با دیدن x شما دیگه نیاز نیست برای دیدن y هم درخواست SNI بفرستید.چطوری شروع کنیم به استفاده از DoH؟فایرفاکس نصب کنید! روی همه دستگاه هاتون حتی موبایل. راهنمای استفاده اش خیلی سادست.به کلاودفلر اعتماد ندارین؟ مشکلی نیست فایرفاکس بهتون اجازه می‌ده از DoH دلخواهتون استفاده کنید.منابع:A cartoon intro to DNS over HTTPSrfc8484: DNS Queries over HTTPSهووف! سرم درد گرفت. امیدوارم این مطلب مورد توجه تون قرار گرفته باشه؛ لطفا اون رو با بقیه به اشتراک بذارید تا منم برای انتشار محتوای بهتر انگیزه پیدا کنم.</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Wed, 16 Oct 2019 02:23:01 +0330</pubDate>
            </item>
                    <item>
                <title>چطوری درست و هوشمندانه سوال فنی بپرسیم؟</title>
                <link>https://virgool.io/GNULand/smart-questions-eal15dceyo2x</link>
                <description>این نوشته نظر شخصی من هست؛ اگر کسی لینک این مطلب رو برای شما فرستاده، نه من و نه اون شخص از شما متنفر نیستیم و این مطالب به هیچ عنوان قصد توهین به شما رو نداره. صرفا به نظر رسیده که شما شیوه درستی برای سوال پرسیدن ندارین.حرف‌هام خیلی مشابه نوشته Eric S. Raymond و Rick Moen هست (1) اما رو‌خوانی و ترجمه نکردم. اگر دقیقا شبیه به همون شد به خاطر اینه که «واقعا» این مسئله رو قبول دارم.قبل از خوندن این پست موارد زیر رو بپذیرید:1- کمک داوطلبانه در مقابل پشتیبانی پولی بسیار متفاوته؛ شما آدم مهمی نیستین، کسی هم که تصمیم گرفته به شما کمک کنه آدم مهمی نیست. شما دو نفر در فضای مجازی هستین که در رابطه با یک مشکل فنی گفتگو می‌کنید. پس:باید به من کمک کنیدمن آشنای فلانی هستمتو هیچ میدونی من کی ام؟جایگاهی در این فضاها نداره. اگر من یک سکه قورت بدم کسی من رو «بانک» خطاب نمی‌کنه همچنین شماهم اگر در یک محفل بخصوصی آدم مهمی هستین دلیل نمیشه دیگران همه جا به شما احترام بذارن، احترام از اعتماد به دست میاد و اعتماد به غریبه ها به طرز برخوردشون با همدیگه بستگی داره.2- وقت من (کسی که قراره به شما کمک کنه) دقیقا به اندازه وقت شما (کسی که سوال رو مطرح کرده) با ارزشه. پس به وقت همدیگه احترام بگذاریم.3- آدمها نمی‌تونن ذهن همدیگه رو بخونن. باید به طور دقیق تمامی جزئیات موردنیاز برای درک موضوع مطرح بشه.4- در لحظه عصبانیت و بلاتکلیفی هیچوقت از کسی سوال نپرسید، اول به آرامش برسید بعد. (بند یک رو به خودتون یاد آوری کنید.)5- تمرینات رو خودتون حل کنید، دیگران مسئول حل کردن تمرین شما نیستن. اسمش رو گذاشتن تمرین که یاد بگیرین... خیال کردین اون کسی که انتظار دارین جواب بده خودش چطوری یاد گرفته؟ با حل کردن تمرینجمع آوری اطلاعات (قبل از سوال پرسیدن)چهار نوع سوال وجود داره، سوالاتی هستن که با «بله» / «خیر» به نتیجه می‌رسن؛ سوالاتی هستن که باید در موردشون تحقیق بشه [مثل باگ‌های جدید سخت افزاری و یا نرم افزاری]. سوالاتی هستن که در جوابشون یک سوال پرسیده می‌شه و مهمتر از همه سوالاتی هستن که باید کنار گذاشته بشن. [کالی!!!]موارد زیر رو انجام بدین:جستجو توی اینترنت.جستجو توی انجمن مربوط به موضوع شما.خوندن مستندات مربوط به برنامه/زبان/ابزار مورد بحث.اگر مربوط به برنامه نویسی هست، نوشتن یک قطعه کد. (حتی اگه اجرا نمیشه، اصلا لازم نیست اجرا بشه فقط یه نقطه شروعه)تمامی لینک‌ها و مستنداتی که پیدا کردین رو موقع سوال پرسیدن مطرح کنید.+ ای آقا من اگه حوصله این چیزا رو داشتم که از تو نمی پرسیدم
- من چرا باید حوصله جواب دادن به تو رو داشته باشم که اینقدر تنبلی؟سوال اگه جذاب نباشه، اگه به اندازه کافی پر محتوا نباشه ازش به خوبی استقبال نمیشه.چون من می‌خوام با جواب دادن به سوال شما، خودم هم یه چیزی یاد بگیرم. پیام هرچقدر پربار تر باشه آدمهای بیشتری به شما و سوال شما علاقه نشون میدن. اعتبار این شکلی به دست میاد؛ با درست سوال پرسیدن و نشون دادن اینکه شما شخص لایقی هستین. تنبل یا فرصت طلب نیستین و صرفا در این لحظه خاص به مشکلی برخوردین که توانایی حل کردنش رو به تنهایی ندارین.موقع سوال پرسیدنجای مناسب سوال بپرسید، اگر انجمن هست توی دسته بندی مربوط به خودش. اگر مطمئن نیستین که موضوع به چه دسته بندی‌ای مربوطه کافیه توی ذهنتون بهش سه تا کلمه کلیدی ربط بدید هر کدوم که بیشتر به دسته بندیها نزدیک بود اونجا سوال رو مطرح کنید.اگر گروه تلگرامی هست حتما قبل از سوال پرسیدن قوانین گروه رو بخونید. تا یکی به پیامتون جواب داد فوری وارد چت خصوصی نشید، اصلا تا حد امکان از این قضیه پرهیز کنید؛ گروه رو ساختن تا بقیه هم یه چیزی یاد بگیرن.بریده بریده پیام نفرستین، همه رو توی یک الی دو پیام متوالی بفرستین (پیام قبلی رو reply کنید تا ادامه اون گم نشه). هر داده ای که می‌تونه به سوال شما ربط پیدا کنه رو توی پیام بنویسید. چیزایی مثل:نوع سیستم عامل و نسخه‌ای که استفاده می‌کنیدآدرس مطلب یا مطالبی که درباره این مشکل پیدا کردین (*)اگر مشکل درباره یک برنامه هست، نحوه نصب اون برنامه رو ذکر کنید (از مخزن گرفتین؟ از فلان سایت گرفتین؟ و ...)* حتما، لینک یا کلمات کلیدی که در رابطه با این مشکل پیدا کردین رو توی پیام ذکر کنید به خاطر اینکه بقیه می‌فهمن اون موارد رو امتحان کردین در نتیجه جواب تکراری نمیدن. بعلاوه ممکنه شما درحین تحقیقات  درست متوجه چیزی نشده باشین و الان یکی می‌تونه براتون بر اساس همون منابع بیشتر توضیح بده.سعی کردم این بحث رو زیاد کشش ندم تا کسایی که حوصله ندارن بتونن یه نگاهی بندازن. کسانی که این موارد رو می‌دونن و رعایت می‌کنن نیازی به خوندن این مطلب ندارن.در پایان اینکه همه اداب و رسوم معقول که توی یک جامعه واقعی رعایت می‌شه توی فضای مجازی هم می‌تونه رعایت بشه. چون اون رو نمی‌بینید و اون هم شما رو نمی‌بینه دلیل نمیشه که انسانیت رو فراموش کنید.</description>
                <category>هادی اعظمی</category>
                <author>هادی اعظمی</author>
                <pubDate>Tue, 15 Oct 2019 02:16:35 +0330</pubDate>
            </item>
            </channel>
</rss>