<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های روزبه شریف‌نسب</title>
        <link>https://virgool.io/feed/@rsharifnasab</link>
        <description>همینجا بگم که روزبه شریف نسب درسته و نه شریف نصب یا شریفی نسب یا هرچیز غلط دیگه..</description>
        <language>fa</language>
        <pubDate>2026-04-14 22:33:42</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/16109/avatar/kRhPPG.png?height=120&amp;width=120</url>
            <title>روزبه شریف‌نسب</title>
            <link>https://virgool.io/@rsharifnasab</link>
        </image>

                    <item>
                <title>به بهانه‌ی درس شبکه‌های پیچیده: علم شبکه به ما چه می‌گوید؟</title>
                <link>https://virgool.io/@rsharifnasab/%D8%A8%D9%87-%D8%A8%D9%87%D8%A7%D9%86%D9%87-%DB%8C-%D8%AF%D8%B1%D8%B3-%D8%B4%D8%A8%DA%A9%D9%87-%D9%87%D8%A7%DB%8C-%D9%BE%DB%8C%DA%86%DB%8C%D8%AF%D9%87-%D8%B9%D9%84%D9%85-%D8%B4%D8%A8%DA%A9%D9%87-%D8%A8%D9%87-%D9%85%D8%A7-%DA%86%D9%87-%D9%85%DB%8C-%DA%AF%D9%88%DB%8C%D8%AF-papsos7mgbr2</link>
                <description>این مطلب تلاش می‌کند تا به شما دید کلی‌ای از علم شبکه بدهد، من این درس را به عنوان یکی از درس‌های دوره‌ی ارشد مهندسی کامپیوتر زیر نظر دکتر علی‌اکبری فرا گرفتم ولی برای دوست شدن با علم شبکه شما نیاز نیست یک درس دانشگاهی را بگذرانید، این مطلب اثبات این موضوع است! زمانی که صحبت از شبکه می‌شود، مخصوصا زمانی که کنار آن صحبت از کامپیوتر باشد، ناخودآگاه به فکر شبکه‌های کامپیوتری می‌افتیم، مثلا شبکه‌ای که کامپیوترهای یک خانه یا یک سازمان را به هم متصل می‌کند و یا شبکه‌ی اینترنت که بسیاری از شبکه‌های سراسر جهان را به هم متصل می‌کند. اما اینجا مقصود از شبکه، شبکه‌های کامپیوتری نیستند، بلکه اینجا صحبت از «شبکه‌های پیچیده» می‌کنیم که تعریف خودش را دارد، اما برای شروع بیاید چند مثال بزنیم: ارتباطات افراد در یک شبکه‌ی اجتماعی، ارتباطات حضوری با آدم‌های دیگر، شبکه‌ی توزیع برق یک کشور، شبکه‌ی راه‌های یک شهر و ... شبکه‌ی اینترنت هم البته یک شبکه‌ی پیچیده محسوب می‌شود. تعریف شبکه‌های پیچیدهبا ارائه چند مثال سعی کردم شما را با آنچه قرار است بخوانید آشنا کنم اما به شکل رسمی تر، شبکه را تعریف کنیم. شبکه‌ی  یک شبکه (یا گراف، اگر با آن آشنا هستید) است که از تعدادی گره و تعدادی یال تشکیل شده است. این گره‌ها موجودات ما هستند که با کمک علم شبکه می‌خواهیم آن‌ها بررسی کنیم، مثلا در یک شبکه‌ی اجتماعی، اکانت‌ها گره‌ها هستند. هر گره به تنهایی برای خودش هویتی دارد اما آن‌چه برای ما اهمیت دارد ارتباطاتش با سایر گره هاست. در اینستاگرام می‌توان ارتباط بین دو کاربر را فالو کردن در نظر گرفت، در فیس‌بوک می‌توان دوست بودن در نظر گرفت و در توییتر هم می‌توان فالو کردن در نظر گرفت. با این تفاسیر می‌توان از اینستاگرام یک مدل ساخت که شامل تعداد گره (اکانت) و تعداد ارتباط و لینک بین این گره‌هاست (فالو شدن/فالو کردن) تا اینجا تعریف شبکه (گراف) را بررسی کردیم، اما شبکه‌ی پیچیده تعریف خاص تری دارد، شبکه‌ی پیچیده به دسته‌ای از شبکه‌ها می‌گویند که اولا از روی اتفاقات دنیای واقعی استخراج شده باشند و دوم اینکه ویژگی‌های غیربدیهی و جالبی داشته باشند! مورد اول تقریبا واضح است، مثلا اگر چند نقطه و خط روی یک کاغذ بکشیم خودمان به شکل مصنوعی یک شبکه را طراحی کرده‌ایم و بر اساس اتفاقات دنیای واقعی نیست، اما اگر داده‌های راه‌های یک شهر را استخراج کنیم از دیتاهای دنیای واقعی شبکه را استخراج کرده ایم. برای قسمت دوم هم می‌توانم فعلا اینطور بگویم که شبکه‌ی اینترنت یک شبکه‌ی پیچیده است اما شبکه‌ی خانگی با ۴ دستگاه چندان پیچیده نیست! علم شبکه اصلا به چه دردی می‌خورد؟من که تا اینجا قانع نشدم، یک تعریف برای خودتان داده‌اید و یکسری اتفاقات دنیای واقعی را با نقطه و خط مدل کرده‌اید، خب که چه؟ این مدل‌سازی هدف نهایی ما نیست، بلکه هدف نهایی این است که بتوانیم با کمک تحلیل‌هایی که روی این شبکه‌ی مدل شده انجام می‌دهیم، به اطلاعاتی از دنیای واقعی برسیم. مثلا فرض کنیم در یک شبکه‌ی اجتماعی می‌خواهیم تبلیغ فلان مارک دوغ را انجام دهیم. مقدار محدودی هم پول داریم، باید این پول را به چه کسانی بدهیم تا برای ما تبلیغ کنند که بیشترین تاثیر را روی مخاطبان داشته باشد؟ بدون علم شبکه هم مشخص است که اکانت روزبه که ۴ فالور دارد گزینه‌ی خوبی نیست تا برایمان تبلیغ کند چون کسی اصلا تبلیغ ما را نخواهد دید! و یا بیاید کمی خبیث‌تر شویم، فرض کنید ما جک دورسی هستیم و بعد از فروش توییتر به ایلان ماسک می‌خواهیم کاربران را به سمت شبکه‌ی اجتماعی جدیدمان (بلو اسکای) هدایت کنیم، برای اینکار می‌خواهیم به چند نفر از کاربران پول بدهیم تا به جای توییتر در بلو اسکای فعالیت کنند. به نظر شما به کدام کاربران باید پول بدهیم؟ چند کاربر پرطرفدار یا تعداد زیادی کاربر کم طرفدار یا مقداری از هردو؟ علم شبکه برای چنین سوالاتی جواب دارد یا حداقل با کمک علم شبکه می‌توان به پاسخ‌های خوبی برای این سوالات دست یافت. با کمک مباحثی که در ادامه مطرح می‌شوند می‌بینید که چه دسته از سوالات قابل بررسی هستند.چه سوالاتی توسط علم شبکه جواب داده می‌شود؟علم شبکه تحلیل‌های متفاوتی را روی شبکه‌ی ما اعمال می‌کند و هر تحلیل برای پاسخ دادن به یکسری سوال مناسب است، بیاید در ابتدا تحلیل‌های متفاوت را ببینیم و برای هر کدام هم چند مثال ببینیم. گره‌های مهم اگر ایلان ماسک را بشناسید، حتما می‌دانید که آدم مهمی است، همینطور باراک اوباما. این دو نفر (تا این لحظه) پر مخاطب ترین افراد در توییتر هستند، پس حتما افراد مهمی هستند نه؟ شناختن این افراد مهم چه کمکی به ما می‌کند؟  به عنوان یک شخص شاید به ما کمک می‌کند که بدانیم اگر حرفی می‌زند احتمالا درست است (واقعا هست؟) و یا مثلا به عنوان یک شرکت می‌دانیم که اگر یک شخص مهم تبلیغ کسب‌وکار ما را انجام دهد بازی را برده‌ایم! یا به عنوان مخرب می‌دانیم که اگر افراد مهم را هدف بگیریم احتمالا سریع‌تر می‌توانیم شبکه را از کار بیندازیم مثلا شبکه‌ی اینترنت با از دست رفتن گره‌های مهم از دست می‌رود. علم شبکه چند معیار را برای مهم بودن یک گره به ما معرفی می‌کند، تا اینجا صحبت از «درجه‌ی یک گره» بود، یعنی تعداد کسانی که به او متصل هستند، هرکس درجاتش بیشتر باشد یعنی دوستان بیشتری دارد و مهم‌تر است اما آیا تمام ماجرا همین است؟ خیر. مثلا کسی که خودش هیچ دوستی ندارد به جز ایلان ماسک و باراک اوباما آیا آدم مهمی است؟ قطعا بله! شاید اگر در کار SEO باشید با مفهوم PageRank آشنا باشید، این الگوریتم هم سعی می‌کند که گره‌های مهم را شناسایی کند، در این الگوریتم گره‌ای مهم است که گره‌های مهم دیگری به او متصل باشند مثلا اگر سایت گوگل به یک سایت لینک بدهد حتما سایت خوبی است! اما این تعریف بازگشتی است و نمی‌توان اهمیت یک گره را به تنهایی حساب کرد. تولید یک شبکهیکی از کاربردهای جالب و داغ علم شبکه، تولید شبکه است، فرض کنید که ما شبکه‌ای نداریم که روی آن تحلیل کنیم اما یک دستگاه داریم که می‌تواند برای ما یک شبکه‌ی پیچیده با خواص مناسب تولید کند، پس هر تعداد بخواهیم شبکه داریم! این مورد هم برای محققات جذاب است هم در حالاتی که به خود دیتا یا کل دیتا دسترسی نداریم با کمک مدل ‌سازی می‌توانیم حدس بزنیم قسمتی که دسترسی نداریم به چه شکل است. یکی دیگر از سوالاتی که «مدل‌های مولد شبکه» جواب می‌دهند این است که اصلا چه شد که شبکه به این شکل در آمد؟ چون قرار است که یک شبکه‌ی پیچیده تولید شود می‌توان اول آن را با اندازه‌ی کوچک تر تولید کرد و روال گسترشش به حالت فعلی را بررسی کرد یا می‌توان آینده‌ی شبکه را پیش‌بینی کرد. مثلا فیس بوک در ۲ سال آینده میزبان چند نفر است؟ این تعداد از عدد فعلی بیشتر خواهد شد یا کمتر؟ یا حتی می‌تواند به این سوال پاسخ دهد که آیا من با علی علوی دوست خواهم شد؟ در چه شرایطی دوست خواهم شد؟ این موضوع که مدل‌سازی یا مدل‌های مولد نام دارد روش‌های سنتی متفاوتی دارد که بدون هوش مصنوعی کار می‌کنند اما اخیرا با گسترش یادگیری عمیق روش‌های یادگیری ماشین تقریبا به طور کلی جای روش‌های سنتی را گرفته‌اند.گروه‌هاگروه‌ها به دسته‌ای از گره‌ها گفته می‌شود که به طور کلی با هم دوست‌تر هستند،‌ مثلا من در توییتر همه هم دانشکده‌ای هایم را دنبال می‌کنم،‌ آن‌ها هم تقریبا به همین ترتیب، پس اگر گراف توییتر را رسم کنیم اطراف من دوستانی را می‌بینیم که نه تنها من با تک تک آن‌ها دوست هستم بلکه آن‌ها نیز تا حد زیادی با هم دوست هستند، به این پدیده یک گروه (یا خوشه یا community یا انجمن) می‌گوییم. یک باشگاه رزمی را در نظر بگیرید، کسانی که به این باشگاه می‌آیند را می‌خواهیم به عنوان یک شبکه مدل کنیم، آن را ها به هم متصل می‌دانیم اگر با هم بیرون از باشگاه دوست باشند. می توان حدس زد که دوستی های زیادی خارج از باشگاه هم وجود دارد ولی همه‌ی افراد با همه دوست نیستند. حالا این دوستی‌ها باعث ایجاد دو خوشه‌ی کلی شده اند که اگرچه بین این دو خوشه هم دوستی وجود دارد ولی بیشتر تمرکز و چگالی دوستی‌ها درون خوشه هاست. اگر اختلافی بین دو عضو از این دو خوشه در این باشگاه پیش بیاید و باشگاه بخواهد به دو باشگاه تبدیل شود، می‌تواند حدس زد که خوشه‌ها از هم جدا می‌شوند و هر خوشه به شکل یک باشگاه در می‌آید. (باشگاه کاراته‌ی Zachery را جست و جو کنید)پویایی‌های روی شبکهتا اینجا اکثر مواردی که بررسی شد مربوط به ساختار خود شبکه مثل مهم بودن گره‌ها و اتصالات بود، اما بیاید فرض کنیم که ساختار شبکه را داریم و ثابت است، حالا یکسری استفاده از این شبکه می‌توان کرد.مثلا میلگرام یک آزمایش جذاب انجام داد، او یکسری نامه را به مقاصد افراد گوناگون جهان نوشت و به افراد دیگری ارسال کرد! به هرکس گفت که اگر صاحب نامه را می‌شناسی مستقیم به او بفرست و اگر نه به کسی که حدس می‌زنی او را بشناسد بفرست. همچنین مقداری هم پول داخل هر نامه قرار داد تا هزینه‌ی پست باعث عدم ارسال نامه نشود. در کمال تعجب این آزمایش جواب داد و نامه‌ها به مقصد‌های خودشان رسید!اتفاقی که افتاد در واقع مانند این بود که او یک نامه را برای علی علوی در شهر یزد ارسال کند اما مقصد اصلی اش روزبه در دانشگاه شهید بهشتی باشد. علی علوی خودش روزبه را نمی‌شناسد اما یک دوست به اسم تقی تقوی دارد که در تهران زندگی می‌کند پس به او ارسال می‌کند و می‌گوید که سعی کن روزبه را پیدا کنی. تقی هم که در تهران است نامه را دریافت می‌کند اما خودش روزبه را نمی‌شناسد اما می‌داند که دوستش نقی نقوی در دانشگاه شهید بهشتی تحصیل می‌کند پس نامه را به دست او می‌رساند. با گذشتن از چند مرحله نامه به دانشکده و ورودی مناسب می‌رسد و نهایتا به دست روزبه می‌رسد. به این ترتیب بدون استفاده از ابزارهای سرچ مجتمع و فقط با کمک ارتباطات میان افراد علی علوی توانست یک غریبه به اسم روزبه را پیدا کند. به این ویژگی سرچ می‌گویند که اتفاقا خیلی هم در دنیای واقعی کاربرد دارد. مثلا من به دوستم می‌گویم دندان پزشک خوب سراغ داری؟ یا توییت می‌کنم که آیا کسی فلان دارو را دارد یا جایی را می‌شناسد که داشته باشد؟ و یا مثلا همکارم به من می‌گوید که به دنبال React کار هستیم، آیا کسی رو می‌شناسی؟ یکی دیگر از اتفاقاتی که روی ساختار کنونی شبکه می‌افتاد، وایرال شدن تبلیغات و شایعه‌ها و به طور کلی اخبار هستند. مثلا خبر زلزله‌ی ترکیه مثل برق و باد به همه می‌رسد حتی کسی که اخبار را چک نمی‌کند و هیچ کانال خبری را نیز دنبال نمی‌کند اما چرا؟ چون دوستانش به او خبر می‌دهند، اما دوستانش چطور به او خبر می‌دهند؟ چه مسیری دنبال می‌شود؟ علم شبکه می‌تواند به ما کمک کند که تبلیغ خودمان را مانند یک خبر جذاب فراگیر کنیم تا همه از آن باخبر شوند. می‌خواهم تحلیل‌هایی با علم شبکه انجام دهم، چه کنم؟فرض کنیم که تا اینجای مطلب برای شما جالب بوده است و تصمیم گرفتید که با کمک علم شبکه یکسری تحلیل انجام دهید و مشکلات خودتان را حل کنید، باید چه کار کنید؟ قدم اولی که باید انجام دهید این است که یک شبکه‌ی دنیای واقعی را انتخاب کنید و آن را به دنیای کامپیوتر بیاورید. مثلا من شبکه‌ی اجتماعی توییتر را انتخاب می‌کنم اما چطوری آن را به دنیای نقطه و خط تبدیل کنم؟ برای این کار به اطلاعات توییتر نیاز دارید که می‌توانید از روش‌های مختلفی تهیه کنید. روش اول می تواند این باشد که به شکل دستی وارد اکانت کاربرهای مختلف شوید و فالوینگ/فالورهای آن‌ها را یادداشت کنید تا در نهایت پس از صرف زمان بینهایتی(!) یک مدل از شبکه داشته باشید. روش دوم این است که یک کامپیوتر را مسئول این کار کنید که با «خزش» اطلاعات این مدل را استخراج کند، اما شرکت‌ها چندان از خزش اطلاعات خوشحال نمی‌شوند مثلا توییتر برای API محدویت گذاشته و با رقم‌های سنگین می‌توانید از آن استفاده کنید. دسته‌ی سوم از روش‌ها هم این است که شبکه برای خودتان است، مثلا شما به عنوان مدیرعامل توییتر یا اوبر یا یک برنامه‌ریز شهری می‌خواهید یک مسئله را حل کنید، پس دیگر احتیاجی که دنبال داده گشتن وجود ندارد چون منبع داده‌ها در اختیار شماست (خوشا به حالتان!)قدم دوم این است که دیتاهایی که داریم را به یک شبکه تبدیل کنیم، برای این منظور یک ابزار کامپیوتری احتیاج داریم. مطرح‌ترین ابزاری که می‌توان از آن نام برد ابزار Networkx است که یک کتاب‌خانه‌ی پایتونی است و با کمک زبان برنامه‌نویسی پایتون می‌توانید با آن کار کنید و اطلاعات گرافتان را به آن داده و گراف را بررسی کنید. این ابزار نقاط قوت بسیار زیادی دارد و بسیاری از الگوریتم‌ها را نیز پیاده‌سازی کرده است اما برای استفاده از آن نیاز به برنامه‌نویسی و آشنایی با کتاب‌خانه است. ( اگر این روش را انتخاب کردید توصیه می‌شود این پروژه که توسط نگارنده تهیه شده و برخی کاربردهای اولیه‌ی networkx را نشان می‌دهد را نیز ملاحظه کنید.)ابزارهای دیگری نیز وجود دارند که بدون نیاز به دانش برنامه‌نویسی می‌توانید از گراف خودتان اطلاعات استخراج کنید،‌ برای مثال Gephi و Cytospace اما برای گراف‌های بزرگ و الگوریتم‌های سنگین ممکن است شما را اذیت کنند. برای من کاربرد این ابزارها بیشتریک نمونه‌ی اولیه از چیزی که می‌خواهم به آن با برنامه‌نویسی برسم است. قدم سوم این است که با کمک الگوریتم‌ها و روش‌هایی که ابزار در اختیار شما قرار می‌دهد اطلاعاتی از گراف استخراج کنید. مثلا با کمک خوشه‌بندی، خوشه‌های آن را پیدا کنید یا مثلا یک شبیه‌سازی از انتشار ویروس کرونا را روی آن اجرا کنید. این قدم بسته به شما دارد و کاربردی که در نظر دارید و می‌توانید تا رسیدن به نتیجه‌ی مطلوب آن را تکرار کنید! جمع‌بندی به عنوان جمع‌بندی می‌توان گفت که علم شبکه شاید آن چکش طلایی نباشد که جواب همه‌ی سوالات را داشته باشد اما می‌توان با کمک آن رفتار انسان‌ها و حتی کامپیوترها را به عنوان یک گروه و یک سیستم بررسی کرد و پیش‌بینی‌هایی کرد که اگر این کار را کنم چه اتفاقی می‌افتد. برای بهبود کسب و کار و تبلیغ یا زمین زدن کسب و کار رقیب یا پیدا کردن روابط و اطلاعات مخفی و ناشناخته که در دل یک شبکه پنهان شده‌اند از آن استفاده می‌شود. اگر به داده‌های شبکه‌ای دسترسی دارید، توصیه می‌کنم علاوه بر تحلیل‌های دیگر از تحلیل‌های علم شبکه نیز کمک بگیرید. اگر هم در شبکه‌های اجتماعی فعالیت جدی می‌کند برای پیشرفت خود می‌توانید از دانسته‌ها و تجربیات این علم کمک بگیرید. </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Wed, 21 Jun 2023 23:24:07 +0330</pubDate>
            </item>
                    <item>
                <title>چند توصیه برای دوستان برنامه‌نویس جوان‌ترم!</title>
                <link>https://virgool.io/@rsharifnasab/programming-advices-pucsgoqyywru</link>
                <description>سلام. من روزبهم، دانشجوی ارشد مهندسی کامپیوتر گرایش نرم‌افزار. به سنم نمی‌خوره که چنین مطلبی بنویسم و عنوان این مطلب هم برای خودم عجیبه ولی یکسری توصیه دارم براتون که می‌تونه کمک‌کننده باشه. شاید هم نباشه و بهم خواهید گفت و هممون یاد خواهیم گرفت. بر خلاف مطلب‌های دیگه که توی ویرگول می‌نویسم از ابتدا، این مطلب‌ها ظرف چند ماه، توی یه کانال تلگرامی نوشته شده با آدرس زیر. اگر دوست داشتی اونجا هم می‌تونی منو دنبال کنی.https://t.me/terminal_stuff https://t.me/terminal_stuff اما برسیم به توصیه‌ها: توصیه‌ی اول: جای راحتحتما سعی کنید برای برنامه‌نویسی جای نشستن‌تون مناسب و راحت باشه. به چند تا دلیل:تعمیر «کمر» اصلا آسون نیست.قراره سال های زیادی تو این حالت بشینید و اگر یکم هم بد باشه تأثیرات زیادی داره. اگه جاتون ناراحت باشه احتمالا زودتر خسته می‌شید، بازدهیتون کم میشه و حواس‌پرتیتون بیشتر میشه. در واقع ممکنه تو یه حالت نامناسب بشه سریال دید ولی نمیشه برنامه نویسی کرد چون تمرکز بیشتری لازم داره. :)شمایی که میگی نه من از هفته دیگه درست میشینم پشت میز، با شما هم هستم.توصیه‌ی دوم: نشستن در حین کار با لپتاپسعی کنید لپتاپ (بدون لوازم جانبی) سیستم اصلی‌تون برای کار نباشه. برای اینکه حالت پشت لپتاپ نشستن برای سلامت خوب نیست. در واقع معمولا اینطوریه که دستاتون به هم نزدیکه. همچنین مانیتورش نزدیک سطح میزه و گردنتون هم اذیت میشه. حالا اگه بذارید روی پاتون که دیگه مشکلاتش زیاد گفته شده. صفحه نمایش کوچک هم می‌تونه برای چشماتون خوب نباشه. برای بهبود وضعیت: حداقل کاری که می‌تونید بکنید اینه که یه موس و کیبورد بگیرید و وصل کنید بهش. لپتاپ هم جای بالاتری بذارید تا گردنتون هم اذیت نشه. با ۳۰۰-۴۰۰  هزارتومن می‌تونید یه موس و کیبورد نسبتا خوب بگیرید. اگر جای ثابتی هستید بهتره مانیتور هم داشته باشید. با حدود ۴ ملیون می‌تونید یه مانیتور IPS Full HD بگیرید. البته که عالی نیست ولی خب. https://ergonomictoolbox.com/ergonomics/5-reasons-why-laptops-are-bad-for-your/توصیه‌ی سوم:‌ ویرایشگر متنی شمابرای editorتون ارزش قائل باشید و بهش مسلط باشید. چرا: ادیتور/IDE برای برنامه‌نویس‌ها مثل آچاره. اگر کسی از دور نگاه کنه و ببینه که خوب استفاده نمی‌کنیم فکر می‌کنه آدم باسوادی نیستیم.  وقتی می‌خواید یه تغییری تو کد بدید و نتیجه رو ببینید بهتره که در کمترین زمان بتونید انجام بدید تا ایده‌اش نپریده و خلاصه feedback loop رو کوتاه تر میکنه.ابزارهایی که وجود دارن، برای هم‌صنف‌ها و همکارهای شما ساخته شده پس احتمال خیلی زیادی داره که شما هم بهش نیاز پیدا کنید. اگر بلد نباشید احتمالا مجبورید از یه راه سخت‌تر برید. مثلا refactor یا دیباگ ممکنه خیلی براتون ساده‌تر باشه. ممکنه هم از یه سری چیزا چون جالب نیستن تصمیم بگیرید استفاده نکنید مثل version controlچطوری: می‌تونید ویدیوی آموزشی ببینید. خیلیا تنظیم می‌کنن که کلیدهایی که می‌زنن رو تصویر نشون داده بشه، پس می‌تونید کامل یاد بگیرید. یکسری cheat sheet هم وجود داره که می‌تونید استفاده کنید.  توی تنظیماتش بگردید و دستکاری کنید و سرچ کنید تا به بهترین شکل مناسب شما بشه. اگر کاری می‌خواید انجام بدید که سخته یا باید از ادیتور خارج بشید، سرچ کنید «انجام این کار در ادیتور فلان» و ببینید راهی هست یا نه. مثلا edit remote file in YOUR_EDITOR لیست پلاگین‌های به درد بخور رو تو اینترنت سرچ کنید و با احتیاط نصب کنید. همچنین می‌‌تونید ببینید دوستاتون از چه پلاگین‌هایی استفاده می‌کنن. شاید حتی تو گیتهاب/وبلاگشون گذاشته باشن. قسمت آخر (برای اینکه تو این مطلب از vim هم اسمی آورده باشم)ابزارتون آگاهانه انتخاب کنید. قرار نیست چون همه از ابزارهای Jetbrains یا VS code استفاده می‌کنن شما هم استفاده کنید. البته که احتمالا کیفیت خوبی هم داره ولی تنها انتخاب نیست. حتی اگه از اونا استفاده می‌کنید می‌تونید شخصی‌سازی کنید. ادیتورهای دیگه‌ای هم هستن که می‌تونید تا انتها شخصی‌سازیشون کنید و قابلیت‌های مورد نیاز رو به شکل مورد نیازتون بهشون اضافه کنید. Vim و neovim و emacs نمونه‌های مهم هستن. شخصی‌سازی به شما کمک می‌کنه به بهره‌وری بیشتری برسید. البته که باید دقت کنید خود شخصی‌سازی هم ازتون وقت می‌گیره. کلیدواژه‌ی lsp هم مدنظر داشته باشید. به کمک این استاندارد (که ٰVS Code استانداردش کرد) ابزارهای auto complete مختلف (در واقع language server) تحت این پروتکل با ادیتور صحبت می‌کنن و می‌تونید از موتور اتوکامپلیتی که برای ادیتور شما نیست هم به راحتی استفاده کنید. چون همگی از یک پروتکل واحد پشتیبانی می‌کنن.توصیه‌ی چهارم: ترویج علمسعی کنید علمتون رو ترویج کنید. با فقط برای خودتون نگه داشتن یه مزایایی به دست میاد ولی ترویجش هم مزایایی داره. + برای بقیه مفید باشید: خودتون چقدر از Stackoverflow استفاده کردید؟ چقدر فیلم های جادی رو دیدید؟ (به طول مثال) همین چنل رو چرا دنبال می‌کنید؟ + برای آینده شغلیتون مفید باشید: اگر به دیگران چیزی یاد بدید، برای خودتون برند شخصی ساختید. یه جور نمونه کار می‌شه. براتون موقعیت‌های شغلی (نه فقط معلمی) هم میاره به زودی.+ برای مشکلات بعدیتون مفید باشید: شاید به نظر بیاد احتمالش کمه ولی برای من که کم پیش نیومده که به یکی از مطالب قبلیم سر بزنم و مرور کنم. ما فراموشکاریم و اتفاقا چیزایی که یه زمانی خودتون نوشتیم بهترین چیز برای یادآوری اون موضوعه. چون به زبون خودمونه. به چشم جزوه نوشتن بهش نگاه کنید.اما چه کارهایی می‌تونی بکنیم؟ اینا نظرات منه، قطعا میشه بهتر و کامل ترش کرد. استقبال می‌کنم.وبلاگ فارسی بنویسید. مخاطب وبلاگ فارسی معمولا کسیه که دوست داره شروع کنه ولی زبان انگلیسی بلد نیست که مطالب انگلیسی رو بخونه یا کلا با موضوع آشنا نیست که خودش سرچ کرده باشه. وبلاگ انگلیسی بنویسید. اگر زبانتون خوبه یا حتی معمولیه چرا که نه، اینطوری به آدمای بیشتری کمک کردید. اگر هرکس به زبان مادری خودش درس میداد چقدر علم ما کمتر بود؟  فیلم ضبط کنید. منتور/معلم می‌تونید بشید. تو فروم‌های پرسش و پاسخ فعال باشید: اینجا خیلی باید دقت کرد. فروم منظورم جاییه که توش سوالات به درد بخور پرسیده می‌شه و جواب‌های به درد بخوری داده می‌شه و برای عموم قابل دسترسی و سرچه. نمونه چیزایی که مناسب نیست: گروه‌های تلگرام/هر پیامرسانی. چون چیزی که توش نوشته می‌شه برای همیشه دفن می‌شه و کسی که اون مشکل رو داره نمی‌تونه از اون وقتی که گذاشتید استفاده کنه. فروم/گروه‌های خیلی مبتدی‌ها. معمولا اینجور جاها سوالات از «چطوری درایور گرافیک نصب کنم» یا «ویندوزم بالا نمیاد چیکار کنم» فراتر نمی‌ره. جدا از اینکه مشکل قبلیو داره، عملا وقتتون رو دارید تلف می‌کنید چون اون شخص می‌تونه سرچ کنه یا اگه خیلی از موضوع پرته بره پیش تعمیرکاری که شغلش همینه.جایی که برای دیدن جواب سوال یا خلاصه خوندن موضوعات نیاز به اکانت داره. اینطوری هم احتمالا توی موتور جست و جو قابل دیدن نیست هم وقت آدما تلف میشه. در کل به نظرم بهترین جا همون خانواده stack exchange ئه، هرچند که یه مقدار شروعش ترسناکه چون وقتشون و کیفیت کار براشون مهمه پس مهربون نیستن.به عنوان نکته آخر هم سعی کنید تو کمک به دیگران، همچنان برای وقت خودتون ارزش قائل باشید و کار تکراری نکنید. اگر جواب سوالی جایی هست، شما دوباره جواب ندید. اگر با سرچ می‌تونه به مطالب قابل قبولی برسه شما دوباره وقت نگذارید.توصیه‌ی پنجم: مدام ایده بزنیدیه عبارتی به کار می‌برن به اسم «ماهیچه‌ی خلاقیت»، تعبیرش هم اینه که هرچی بیش‌تر از خلاقیتتون استفاده کنید مثل یه ماهیچه قوی‌تر میشه. برای برنامه‌نویسی هم به این ماهیچه نیاز داریم که بتونیم مسائل رو حل کنیم. البته منظورم این نیست که باید خودمون ایده بزنیم که یه درخت باینری رو برعکس کنیم، برای همین کارهای روتین هم نیاز به ایده داریم.حالا چطوری ایده بزنیم و تمرین خلاقیت کنیم؟ برای مسائل زندگی که حل نشده راه حل بدید، بعدا اگر حل شدن با راه حل خودتون مقایسه کنید و ایده بگیرید.مثلا دارن دوربین زیر نمایشگر می‌سازن، فکر کنید که چطوری چنین چیزی ممکنه؟ یا چرا ممکن نیست؟ برای مسائلی که حل شدن ایده بزنید و خودتون سعی کنید اصلاحش کنید. بعد برید راه حل فعلی رو هم ببینید و اگه با مال شما تفاوت داشت به دلیل تفاوتش فکر کنید. مثلا صفجه نمایش لمسی می‌سازن، بهش فکر کنید چطوری ساخته شده؟ بعد برید سرچ کنید که واقعا چطوری ساخته شده.یا مثلا یه برنامه هست که مصرف جوهر پرینتر رو کم می‌کنه، اول فکر کنید چطوری کار می‌کنه بعد برید سرچ کنید. مسائل زندگی خودتون رو به شکل متفاوتی حل کنید. مثلا اگه همیشه از یه مسیر میرید خونه، به مسیرهای جایگزین و نقاط ضعف و قوت هر کدوم فکر کنید. حتی چند تا مسیر رو امتحان کنید. ایده‌ها رو ترکیب کنید. یه ایده که جای دیگه جواب داده یا نداده رو سعی کنید جای دیگه به کار بگیرید. مثلا موتور هواپیما ایده‌ کارکردش چطوریه؟ آیا میشه تو ماشین لباسشویی هم استفاده بشه؟ تو ابمیوه گیری چی؟ اگه بخواد بشه چطوری میشه؟ اگه قطعا نمیشه چرا قطعا نمیشه؟ایده‌هاتون رو هرچند احمقانه با دیگران به اشتراک بگذارید. خیلی اوقات ایده‌های خوب از پردازش یه ایده بد توسط یه آدم دیگه به وجود میاد. اینطوری خودتون هم یاد می‌گیرید ایده هاتون رو بهتر کنید.اگه اعتماد به نفس ندارید، به این فکر کنید که قرار نیست این ایده‌ها به کسی آسیب بزنه. نهایتا تو ذهن خودتون یا برای کارهای خودتونه. همچنین به این فکر کنید که ایده‌های خوبی که الان استفاده میشه هم یه زمانی تست نشده و آزمایشی بوده. این دو تا مطلب هم جالب بودن. مثلا ایده‌ی ۶ کلاه رو مطرح کرده بود یکیش. https://www.verywellmind.com/how-to-boost-your-creativity-2795046  https://www.gcu.edu/blog/performing-arts-digital-arts/7-tips-developing-creativity توصیه‌ی ششم: خوب تعامل کنیداین قبوله که عمده‌ی کار برنامه‌نویس‌ها پشت کامپیوتره اما یه قسمت مهمش هم اینه که بدونیم پشت کامپیوتر چیکار باید بکنیم. و معمولا این دونستنه از تعامل با آدم‌های دیگه به دست میاد.  حالا چه مدیر پروژه یا کارفرما یا مشتری یا ... باید سعی کنیم مهارت‌های نرممون رو هم تقویت کنیم. مثلا یه موردش که خیلی به من کمک میکنه اینه که بعد از یه بحث، جمع‌بندی خودم از صحبت رو به طرف میگم ببینم موافقه یا نه. خیلی اوقات موافق نیست و آدم تعجب میکنه! یه سری از این موارد توی کتاب «کدنویس تمیز» هست.توصیه‌ی هفتم: به‌روش‌هاپیگیر best practice ها باشید ولی آگاهانه بشکنیدشون.به‌روش‌ها یا همون best practice ها کارهایی هستند که خوبه انجام بدیم و احتمالا اگه انجام ندیم داریم اشتباه میکنیم. پس می‌تونن به یه برنامه نویس تازه کار کمک کنن تا اصطلاحا idiomatic کد بزنه. مثال ساده ش می‌تونه این باشه که اگه یه دستور یه بار قراره اجرا بشه لازم نیست ببریمش توی یه do while که شرطش falseئه. اما آگاهانه شکستن به این معنیه که اگه یه مدت با این کار کردید و خبره شدید اگرچه «کلا اون رو اجرا می‌کنید» اما اگه جایی دقیقا می‌دونید دارید چیکار می‌کنید اشکالی هم نداره شکستنش. مثلا همین مثال بالا یه کاربرد معتبر داره و استفاده هم میشه. یا مثلا گوتو رو تاکید دارن استفاده نکنیم اما برنامه‌نویس های خیلی خفن سی (مثلا کد sudo یا kernel یا ...)‌ رو می‌نویسن از گوتو استفاده می‌کنن. اما اونا حواسشون هست و به‌جا دارن اون قاعده رو می‌شکنن. میشه گفت اگه یه پترن عجیب ببینیم دو حالت داره. یا طرف خیلی حرفه‌ایه و می‌دونه داره چیکار میکنه یا خیلی تازه کاره و نمی‌دونه داره چیکار می‌کنه.توصیه‌ی هشتم: کد خوب بخوانیدلازمه‌ی نوشتن کد خوب اینه که کد خوب دیده و خونده باشید. الان هم توی دوره‌ای هستیم که میلیونها خط کد آزاد توی گیتهاب هست و خودنشون می‌تونه خیلی مفید باشه براتون. مثل یه نویسنده که کتاب خوب زیاد می‌خونه.برای شروع هم می‌تونید پروژه‌های متوسط یا حتی کوچک رو با یه زبون مورد علاقه‌تون پیدا کنید و برید سراغشون. کارهای مختلفی میشه کرد مثلا کامپایل کردن سورس کد و دیدن نحوه کامپایل. یا پیدا کردن main برنامه و دنبال کردن نحوه صدا کردن باقی توابع. یا مثلا یه کارکرد خاص برنامه که به نظرتون نسبتا ساده و جالبه رو بگردید و پیدا کنید که چطوری پیاده سازی شده. مثلا توی پروژه‌ی تلگرام دسکتاپ (که البته پروژه بزرگیه و برای شروع مناسب نیست)، قابلیت فولدرها چطوری پیاده سازی شدن.توصیه‌ی نهم: دانش نامرتبط داشته باشیددانش نامرتبط با موضوع اصلیتون داشته باشید. حتی اگر اقیانوسی به عمق یک سانت شوید. البته نمی‌گم هیچ موضوعی رو عمیق بلد نباشید. بلکه اینجا فرض می‌کنم تو یه موضوع خبره هستید. حالا برای ادامه‌ی راه توصیه می‌کنم:  توی برنامه‌نویسی مدام در حال حل مسئله هستیم و لازمه‌ی حل مسئله ایده‌است. دیدن چیزهای مختلف و ایده‌های پشتشون کمک می‌کنه ایده‌های بهتری برای حل مسئله هامون داشته باشیم. اگر یه روز قرار شد از یه چیزی جدا از تخصص اصلیمون استفاده کنید می‌دونیم حداقل قراره با چی مواجه بشیم. اگه یه موقعیتی پیش اومد که اون ابزار که کمی بلدیم به درد میخورد، می‌تونیم پیشنهاد بدیم چون از قبل باهاش آشنا هستیم.</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Tue, 16 May 2023 16:02:17 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی معماری چند نرم‌افزار موفق: توییتر، ماشین مجازی جاوا، Nginx و توییچ</title>
                <link>https://virgool.io/@rsharifnasab/review-software-arch-aatgrnmglbsz</link>
                <description> معماری نرم‌افزار را می‌توان اینگونه تعریف کرد: تصمیماتی که کاش می‌توانستیم در روند توسعه‌ی نرم‌افزار زودتر بگیریم. نرم‌افزار‌ها چه بزرگ باشند چه کوچک، چه در یک شرکت بزرگ توسعه داده‌شوند چه در لپتاپ یک برنامه‌نویس تازه‌کار،  چه بدانیم و چه ندانیم و چه مستنداتی از آن موجود باشد و چه نباشد دارای معماری هستند. اما در این بین، به عنوان نمونه و الگو دوست داریم نرم‌افزار‌های موفق‌تر را بررسی کنیم. در اینجا نرم‌افزارهایی را انتخاب کرده‌ایم که هم تیم توسعه‌ی فعالی دارند و از نحوه کارشان مستنداتی موجود است و از آن مهم‌تر، در زمینه کاری خود زبانزد هستند.در این لیست هم نرم‌افزار‌های تحت وب با میلیون‌ها کاربر روزانه وجود دارد هم نرم‌افزاری که ۳ میلیارد دستگاه آن را اجرا می‌کنند. (بله ماشین مجازی جاوا) توییترتوییتر یکی از بزرگ‌ترین شبکه‌های اجتماعی در دنیاست که حدود ۴۰۰ ملیون کاربر دارد و از این بین، ۲۰۰ ملیون از آن‌ها کاربر ماهانه هستند، چیزی حدود ۳ برابر جمعیت ایران! این کاربران نیز در سراسر دنیا پخش شده‌اند و جدا از ایالات متحده، کاربران زیادی نیز ژاپن، هند، انگلستان، برزیل و ... دارد. به عنوان یک شبکه اجتماعی مطرح در جهان، امنیت و پایداری زیادی هم دارد. یک جوک اینترنتی هست که می‌گوید «وقتی هر برنامه‌ای کار نمی‌کند در توییتر به هم می‌گوییم اما وقتی توییتر از کار می‌افتد کجا باید برویم؟». توییتر به شکل سنتی قابلیت ارسال پیام متنی با محدودیت ۱۴۰ کارکتر را داشت، اما به مرور این محدودیت در حال کمتر شدن است، برای مثال در سال ۲۰۱۷ این محدودیت دوبرابر شد و به ۲۸۰ کارکتر رسید. امروز که در حال نوشتن این مطلب هستم نیز خبرهایی از توییت‌های طولانی‌تر ۴۰۰۰ کارکتری به گوش می‌رسد. البته این قابلیت هنوز در همه‌ی کشورها فعال نیست.  از قابلیت‌های دیگر توییتر می‌توان به ارسال تعدادی تصویر و ویدیوهای کوتاه و نظرسنجی و امکان دریافت و ارسال پیام خصوصی اشاره کرد. نمی‌توان امروزه از توییتر سخن گفت و از ماجرای خرید آن توسط ایلان ماسک سخن نگفت. ایلان ماسک توییتر را در سال ۲۰۲۲ به قیمت ۴۴ میلیارد دلار خرید، البته پس از چالش‌های تعیین قیمت. معماری کلی (بر اساس حدس‌ها)پس از بررسی‌های مختلفی که روی معماری توییتر انجام شد، به یک تصویر کلی قابل استناد نرسیدیم، چرا که در مطلب‌های متفاوت و در گوشه‌‌گوشه‌ی اینترنت، مطالبی جزئی در مورد «نحوه حل یک مشکل» یا «نحوه‌ی کار یک قسمت» به چشم می‌خورد اما همه‌ی اینها کنار هم چه شکلی می‌شوند؟ دو تصویر رسمی مربوط به سال‌های ۲۰۱۲ و ۲۰۲۲ منتشر شده‌اند که ادعا می‌کنند «تصویر کلی توییتر» هستند اما هیچ کدام تصویر کاملی ارائه نمی‌دهند.تصویری که ادعا می‌کند معماری توییتر در سال ۲۰۱۲ است اما بیشتر به روال «ارسال توییت» میپردازدتصویری که ادعا می‌شود مربوط به معماری توییتر در سال ۲۰۲۲ است:این تصویر نیز بیشتر به روند ساخت تایملاین اشاره دارد و همچنان تصویر کلی‌ای به ما نمی‌دهد.بنابراین و بنا به این محدودیت‌ها، به جای استناد به موارد منتشرشده‌ی رسمی، خودمان حدس می‌زنیم. برای کمک گرفتن هم از یک مطلب استفاده شد. اینگونه مطالب معمولا با دید «سوالات مصاحبه‌ برای system design» پاسخ داده می‌شوند که از نیاز ما هم چندان دور نیست.برای معماری از میکروسرویس استفاده شده که همانطور که در ادامه مطلب هم می‌بینیم با مستندات رسمی مطابقت دارد، البته که با عنایت به حجم بزرگ کد و تیم های مختلف توسعه تصمیم بسیار منطقی‌ای هم هست.علاوه بر نیازمندی‌های کارکردی که در بخش مقدمه ذکر شد، برخی نیازمندی‌های غیرکارکردی نیز برای این قسمت مطرح شده‌اند:دسترسی‌پذیری بالای سرورهاپرفورمنس مناسب: حداکثر در نیم ثانیه تایم‌لاین تولید شود.امکان اسکیل سیستم متناسب با تعداد کاربراندر این طراحی (که قطعا تنها طراحی ممکن هم نیست)، معماری اصلی توییتر به چند سرویس شکسته شده است. این سرویس‌ها عبارت‌اند از:سرویس ارسال توییتسرویس ارسال توییت به سرچ و تایم‌لاین (فن اوت)سرویس تایملاین مستقیم کاربرسرویس تایم‌لاین الگوریتمیسرویس گراف روابط (دنبال کننده و دنبال شونده)سرویس جست‌و‌جوچیزی که در تصویر بالا می‌بینیم برگرفته از وبسایت interviewnoodle است که منبع این حدس از ساختار توییتر است. در اینجا سرویس‌های اصلی مشخص‌شده اند و یک دیتابیس و کش یکپارچه نیز متصور شده است. البته در عمل باید این دیتابیس جزئیات بیشتری مثل sharding داشته باشد تا بتواند از پس چنین حجم کاربرانی بر بیاید. سرویس fanout که در اینجا اشاره شده نیز قرار است همانطور که گفته شد. همه توییت‌های ارسالی را به سرویس‌های تایم‌لاین و سرچ ارسال کند.در نهایت در این تصویر می‌بینیم که کاربر (یا کلاینت کاربر) به شکل مستقیم با سرویس‌های مختلف در ارتباط است که شاید در عمل میسر نباشد و به جای ارتباط مستقیم، استفاده از از یک API gateway مناسب تر باشد، چرا که مزایایی مثل ورژن زدن برای api و امکان دیپلوی بدون داون تایم را به ما می‌دهد.معماری بر اساس شواهدجدا از حدسیات مطرح شده در قسمت بالا، در این قسمت به برخی شواهد و مطالبی که توسط منابع رسمی منتشر شده‌اند یا به شکل دقیق از بررسی‌ها به دست آمده می‌پردازیم.معماری کلیدر صحبت‌های مقامات رسمی (مثلا ایلان ماسک رئیس جدید توییتر) صحبت از میکروسرویس می‌شود و حتی در مرحله‌ای چندین میکروسرویس خاموش شدند، بنابراین می‌توان با قطعیت گفت که توییتر از میکروسرویس استفاده می‌کند. چند میکروسرویس که می‌دانیم وجود دارند عبارت‌اند از «تایید هویت دومرحله‌ای» و «ساخت ترکیب تایم‌لاین الگوریتمی» و «اضافه کننده‌ی تبلیغات به تایم لاین» (بر اساس معماری منتشرشده‌ی توییتر در سال ۲۰۲۲)دیپلویبر اساس شواهدی مثل تغییرات سریع کد حتی با کمبود برنامه‌نویسان می‌توان گفت که قطعا یک سیستم CI/CD مناسب وجود دارد که عملیات deploy را نیز به شکل مناسب انجام می‌دهد. اساس این صحبت آن است که در ماه‌های اخیر با کاهش تعداد برنامه‌نویسان، همچنان امکانات جدیدی به برنامه اضافه می‌شود و دیپلوی می‌شوند و بدون مشاهده‌ی مشکل خاصی از سمت کاربر این اتفاقات می‌افتد.همچنین نکته‌ی دیگری که در زمینه‌ی استقرار وجود دارد این است که در گذشته تا کنون، همواره به شکل گسترده و طولانی از تست‌های استقرار جزئی استفاده می‌کرده است. برای مثال در روزهای اخیر امکان توییت ۴۰۰۰ کلمه‌ای به توییتر برخی کاربران در ایالات متحده اضافه شده که می‌توان گفت از نوع تست قناری است و خود تیم توییتر قصد دارد امکان‌سنجی فنی و عیب‌یابی کند. در گذشته نیز امکاناتی مثل ادیت پیام، ویدیو‌های طولانی‌تر و اسپیس و مخصوصا امکان فلیت به شکل محدود برای برخی کاربران عرضه شده بودند تا واکنش کاربران را بسنجند. (a/b testing) حتی گاها این روال به سال‌ها می‌رسید و برخی قابلیت‌ها نیز واقعا حذف شدند، مثلا همین فلیت مدت زیادی در حال تست بود و پس از مدت طولانی‌ای نیز با اینکه هیچگونه مشکلی از نظر فنی و حتی تجربه کاربری نداشت، حذف شد. یکی از انتقادات ایلان ماسک نیز به همین روند کند عرضه قابلیت‌های جدید بود.زبان‌های مورد استفادهتوییتر یکی از استفاده‌کنندگان مطرح روبی‌آن‌ریلز بود اما بعدها با توجه به نیاز خود از ماشین مجازی جاوا با زبان اسکالا هم استفاده کرد. همچنین در بخش‌های کوچک دیگر از جاوا نیز استفاده شده است. البته اینها زبان‌های سمت سرور هستند و شاهد استفاده گسترده از جاوااسکریپت سمت کلاینت‌ نیز هستیم.نکته جالبی که وجود دارد این است که بین اسکالا و جاوا، کارمندانی که قبلا روبی کار کرده‌اند ترجیح می‌دهند اسکالا را انتخاب کنند و کارمندانی که قبلا با سی و سی‌پلاس‌پلاس کار کرده‌اند ترجیحاشان خود جاوا است.همچنین لازم به ذکر است که استفاده از زبان‌های استاتیک‌تایپ برای توییتر مزیت داشته و گفته می‌شود بهره‌وری برنامه‌نویسان را افزایش داده است.لاگ زدنیک میکروسرویس مستقر در سراسر جهان، نیاز به یک سیستم لاگ‌زدن مناسب و قدرتمند دارد، چرا که لاگ‌هایی از سراسر جهان در سرور‌های مختلف باید بررسی و تجمیع شوند. به همه‌ی اینها، تعداد زیاد کانتینر‌های مستقر را نیز اضافه کنید.در گذشته از سرویس LogLens که محصول خود توییتر است استفاده می‌شد،‌ تلاش شده بود این سیستم تا حد امکان ساده باشد ولی بعد از مدتی این سادگی به قیمت امکانات کم تمام شد و باعث استفاده از سیستم دیگری شد.معماری سیستم سنتی مانند تصویر زیر است:روند کار به این شکل بود که لاگ‌ها در هر کانتینر در سیستم Scribe نوشته می‌شدند، سپس وارد کافکا شده و بعد توسط اندیس‌گذار‌های LogLens بررسی و اندیس‌گذاری می‌شدند. در نهایت هم برای ذخیره‌سازی از یک HDFS استفاده می‌شد. اما مشکلی که وجود داشت این بود که ظرفیت پایین این سیستم، باعث ایجاد گلوگاهی برای ورود لاگ‌ها می‌شد و به ناچار با تنظیم rate limiter‌هایی، ۹۰ درصد لاگ ها دور ریخته می‌شد!در معماری جدید با کمک Splunk، این مشکل مرتفع شد. با امکانات بیشتر و ظرفیتی که وجود داشت نه تنها می‌شد لاگ‌ها دور ریخته نشوند، بلکه می‌شد لاگ‌های امکانات سخت‌افزاری مثل اجزای شبکه نیز بررسی و مدیریت شود. اینبار به جای Scribe، یک Forwarder در سرورها نصب شد که لاگ‌ها را به جای مناسبی فوروارد کند. در ادامه نیز مانند تصویر زیر، لاگ‌ها به کمک Splunk جمع‌آوری و اندیس‌گذاری می‌شوند.در اینجا خوب است اشاره شود که اتفاقا حجم زیاد اطلاعاتی که از سخت‌افزار ها می‌آمدند مشکلی نبود، بلکه شکل دریافت لاگ‌هایی بود که در میکروسرویس‌های جاری با روال قدیم ارسال می شد. برای این منظور از همان کافکای LogLens استفاده شد و از آن‌جا به بعد با سیستم جدید جایگزین شد. این تغییر یکی از مزیت‌های سیستم با طراحی مناسب را نشان می‌دهد که کل سیستم وابسته به یک سیستم لاگ زدن نشده و امکان تغییر آن با کمترین مشکل وجود دارد. همانطور که عموباب هم در خاطراتش می‌گوید ما یک سیستم سمت سرور داشتیم بدون اینکه تا مدت‌ها دیتابیس خاصی برایش انتخاب کنیم و از فایل ساده استفاده می‌کردیم.نگاشت به منابع سخت‌افزاریجدا از مفاهیم منطقی و نرم‌افزاری، خوب است بررسی کنیم که به چه شکل این نرم‌افزار روی سخت‌افزار قرار می‌گیرد. در یکی از مطالب وبلاگ مهندسی توییتر این اطلاعات به ما داده شده است:همانطور که در تصویر مشخص است، دیتابیس بیشترین تعداد سرورها را دارد و در رتبه‌های بعدی ذخیره‌سازی کلید-مقداری و هادوپ هستند.اما علاوه بر این و توپولوژی‌های شبکه که در این مطلب اشاره شده، برخی درس‌های گرفته شده می‌تواند برای ما جالب باشد، چرا که می‌بینیم بهترین مهندسان دنیا هم چنین اشتباهاتی کرده‌اند و خوب است ما آن‌ها را تکرار نکنیم.راه میانی و تغییر موقت در کار نیست، بلکه هر کدام یک بدهی فنی هستند.با کمک متریک‌ها، تصمیمات طراحی مناسبی بگیرید. همچنین مطمئن شوید که ذی‌النفعان دیگر مخصوصا در محیط‌های ابری نیز آن‌ها را خوب درک کنند.خرید توسط ایلان ماسکاز زمانی که اقدام به جمع‌آوری اطلاعات و شروع این پروژه کردیم، متاسفانه توییتر با چالش‌هایی همراه شد که البته به درک بیشتر ما از معماری آن کمک کرد. شروع همه‌ی اتفاقات این بود که  توییتر پس از مذاکراتی توسط یکی از ثروتمند‌ترین افراد دنیا خریداری شد. پس از خرید به سرعت سیاست‌های مختلف شرکت تغییر کرد. از امکان دورکاری که پیش‌تر برای همه کارکنان به رسمیت شناخته شده بود تا هزینه‌ی سرور‌ها و چابکی ارائه تغییرات.چالش تعیین قیمت: قیمت‌گذاری روی یک نرم‌افزار با زیرساخت بزرگ، تعداد زیاد کاربران و محتوا و کد منبع بزرگ، قطعا نیاز به کار تخصصی دارد. (Due diligence). از طرفی با تحقیق روی زمینه‌ای که خریدار بیشترین چانه‌زنی را می‌کند شاید بتوان به هدف خرید نرم‌افزار پی برد. در این مثال، در بحبوحه‌ی مطرح شدن خرید شایعات خرید توییتر توسط آقای ماسک، خبرهایی روی چانه‌زنی‌ها و قیمت‌های مختلف هم مطرح شد. در نهایت ماسک به دلیل آمارهایی که مبنی بر وجود اکانت‌هایی که (به شکل غیرمجاز) توسط ربات‌ها کنترل می‌شوند توانست قیمت را پایین بیاورد و صحبتی از کدبیس نشد.خاموش کردن سرورها: شاید عنوان این قسمت خنده‌دار به نظر بیاید ولی واقعا این اتفاق افتاد و تجربه کاربری را نیز تحت تاثیر قرار داد. علت این ماجرا این بود که ایلان ماسک به عنوان یک مهندس(!) و بدون گرفتن مشاوره از مهندسان ارشد این شرکت، تشخیص داد که میکروسرویس‌های اضافی زیادی در حال اجرا روی سرورها هستند و تصمیم گرفت که بسیاری از آن‌ها را به همراه سرور‌هایشان خاموش کند. نتیجه البته قابل توجه بود. برخلاف انتظار بسیاری از کاربران و صاحب‌نظران، توییتر با وجود دشواری‌ها همچنان پایداری نسبی خودش را حفظ کرد. به شکل دقیق‌تر میکروسرویس‌هایی که هنوز روشن بودند به حالت degraded mode رفتند ولی همچنان سیستم در دسترس بود و «اکثر خدمات خود را ارائه می‌داد». برای مثال در مدت زمانی مشخص لاگین دو-مرحله‌ای کار نمی‌کرد ولی به شکل مناسب اخطار به کاربر داده می‌شد و کاربرانی که لاگین بودند هم می‌توانستند به شکل مناسب از سیستم استفاده کنند. یا به دلیل بالا‌ رفتن لود روی سرور‌ها برخی قابلیت‌ها از کار افتادند برای مثال «نمایشگر تعداد ناتیفیکیشن» ولی همچنان سرویس‌های اصلی پایدار بودند. این «در دسترس بودن بالا» و تطبیق‌پذیری بسیاری را شگفت‌زده کرد.اخراج مهندسان: گویا کاهش هزینه از طریق خاموش کردن سرور‌ها برای آقای ماسک کافی نبود و در همان روزهای اول اقدام به اخراج بسیاری از مهندسان کرد. این اخراج‌ها از دو جهت برای معماری نرم‌افزار مهم است، اول اینکه نرم‌افزار (حتی با وجود تهدید‌های زیرساختی مورد قبل) هم توانست با کمترین مشارکت مهندسان به کار خودش ادامه دهد. شایان ذکر است در حدی اخراج نیروها زیاد بود که در یکی از  مراکز حتی هیچ نیرویی نبوده که در را باز کند تا ایلان و هیئت اطرافش بازدید کنند. (متاسفانه آقای ماسک اکانت منبع این خبر را نیز تعلیق کرده است!) مورد دوم هم این است که مهندسان بسیار کم تعدادی که در شرکت ماندند، توانستند قابلیت‌های جدیدی به سرعت به نرم‌افزار اضافه کنند، پس می‌توان حدس زد که مستندات کافی و دقیقی از نحوه کارکرد این نرم‌افزار وجود داشته تا افراد در تیم‌های غیر تخصصیشان هم قادر باشند کد را کشف کرده و تغییرات نه چندان کوچکی بدهند.اضافه کردن قابلیت‌های پولی: با گذشت زمان خیلی کمی از اخراج مهندسان، ایلان ماسک تصمیم گرفت تیک آبی تایید هویت را پولی کند. در واقع یک سرویس تدارک دید که در آن کاربران با پرداخت هزینه به برخی امکانات بیشتر از جمله ادیت توییت و تیک آبی و ... دسترسی پیدا می‌کنند. پیاده‌سازی سریع و کم‌اشکال این قابلیت‌ها توسط تعداد کم مهندسان نشان از معماری تطبیق‌پذیر توییتر دارد. (البته کم اشکال به لحاظ فنی، وگرنه از نظر کاربری و تحلیل‌ها اتفاقا مشکلات بسیاری برای شرکت‌ها از جمله یک شرکت انسولین‌سازی ایجاد شد) البته آقای ماسک بارها گلایه کرده است که روند توسعه‌ی نرم‌افزار در توییتر کند است و نیاز دارد تسریع شود.منابعنگاشت نرم‌افزار به سخت‌افزار https://blog.twitter.com/engineering/en_us/topics/infrastructure/2017/the-infrastructure-behind-twitter-scaleلاگ‌ها در توییتر https://blog.twitter.com/engineering/en_us/topics/infrastructure/2021/logging-at-twitter-updatedزبان‌های مورد استفاده در توییترhttps://readwrite.com/twitter-java-scala/یک حدس از طراحی سیستمی مشابه توییترhttps://interviewnoodle.com/twitter-system-architecture-8dafce16aec4مشکلات شرکت انسولین سازی با تیک آبی:https://www.republicworld.com/business-news/international-business/us-pharma-giant-eli-lilly-sheds-billions-because-of-impostors-with-fake-twitter-blue-tick-articleshow.htmlگزارش‌هایی از افت تجربه کاربری:https://twitter.com/adambroach/status/1591120030887878656خرید توییتر توسط ایلان ماسک: https://en.wikipedia.org/wiki/Acquisition_of_Twitter_by_Elon_Muskhttps://edition.cnn.com/2022/04/25/tech/elon-musk-twitter-sale-agreement/index.htmlآمارهای توییتر:https://backlinko.com/twitter-users#twitter-usersافزایش تعداد کارکترهای توییتhttps://www.indy100.com/science-tech/twitter-blue-tweet-character-limitماشین مجازی جاوا (JVM)معرفی JVMماشین مجازی جاوا یا JVM را می‌توان موتوری در نظر گرفت که محیط زمان اجرا (runtim environment) را برای اجرای کدهای جاوا فراهم می‌کند. یعنی فایل‌های بایت کد جاوا را به زبان ماشین تبدیل می‌کند. در زبان‌هایی دیگری مثل خانواده‌ی c کامپایلر صرفا کد قابل خوانش برای ماشین را تولید می‌کند ولی در کامپایلر جاوا کدهای قابل خواندن برای JVM را تولید می‌کند و بایت کدهای تولید شده، برای هر ماشینی متناسب با آن ترجمه می‌شوند. تصویر زیر، به خوبی این فرایند را روشن می‌کند:معماری JVM اگر درشت‌دانه به معماری JVM نگاه کنیم از سه بخش اصلی class loader ، runtime data area و execution engine تشکیل شده است که در ادامه به بررسی هر بخش می‌پردازیم. نمای کلی این معماری در تصویر زیر قابل مشاهده است:معرفی بارگذار کلاس (class loader) در JVMهمانطور که اشاره شد هنگامی که فایل‌های نوشته شده به زبان جاوا کامپایل می‌شوند، به بایت‌ کد در فایل‌های .class تبدیل می‌شوند. سپس class loader آن‌ها را در حافظه‌ی اصلی بارگذاری می‌کند. (معمولا کلاسی که دارای تابع main باشد به عنوان اولین کلاس بارگذاری می‌شود.) در بخش اول یعنی class loader  سه بخش و مرحله اصلی دیگر داریم که در تصویر زیر قابل مشاهده هستند:الف ) بارگذاری یا loading : در این بخش بایت کد کلاس‌ها و اینترفیس‌ها گرفته می‌شود و کلاس یا اینترفیس اصلی از روی آن بازسازی می‌شود. متد classLoader.loadClass از توابع اصلی این بخش است که یک کلاس را در حافظه اصلی لود میکند و یکی از 2 اکسپشن NoClassDefFoundError و یا ClassNotFoundException در صورت بروز خطا هنگام لود شدن کلاس‌ها یا فرزندانشان در حافظه پرتاب میشوند. در جاوا سه نوع بارگذار داخلی داریم که عبارتند از :Bootstrap class loaderExtension class loaderApplication class loaderدر بخش اول که ریشه‌ی بارگذاران کلاس است، پکیج‌های استاندارد جاوا مثل util، lang، io و ... بارگیری می‌شوند .(موجود در پوشه‌ی JAVA_HOME/jre/lib) سپس extension class loader وارد کار می‌شود که از bootstrap class loader ارث بری میکند و خود سوپر کلاس applocation class loader است. در این بخش افزونه‌های کتابخانه‌های استاندارد زبان جاوا بارگیری می‌شوند. (موجود در پوشه‌ی JAVA_HOME/jre/lib/ext) و در اخر application class loader فایل‌های موجود در پوشه‌ی اپلیکیشن یا classpath را بارگذاری می‌کند.ب )پیوند یا linking : برای اینکه مروری کنیم در کجا قرار داریم، ابتدا گفتیم JVM در معماری خود 3 بخش اصلی داشت که به سراغ بخش اول یعنی بارگذار کلاس  رفتیم، سپس دیدیم در بارگذار کلاس 3مرحله اصلی داریم که عبارتند از بارگذاری، پیوند و مقدار دهی اولیه. همانطور که از تصویر پیداست در این بخش نیز 3 مرحله اصلی خواهیم داشت.    • تایید یا Verify     • آماده سازی یا Prepare     • حل وفصل یا resolveتایید یا Verifyآماده سازی یا Prepareحل وفصل یا resolveدر این بخش ابتدا ساختار فایل .class  طبق مجموعه قوانین بررسی می‌شود و اگر تایید شد به مرحله بعدی می‌رود در غیر این صورت یک اکسپشن VerifyException پرتاب می‌شود. مثلا تطابق ورژن‌ها یکی از مواردی است که بایستی تایید شود. برای مثال اگر از یک ساختار نحوی که در جاوای 11 ارائه شده استفاده شده باشد ولی جاوای سیستم ما 8 باشد در مرحله‌ی تایید با این اکسپشن مواجه خواهیم شد. سپس درمرحله‌ی آماده سازی JVM برای فیلدهای استاتیک یک کلاس یا اینترفیس حافظه اختصاص می‌دهد و آنها را با مقادیر پیش‌فرض مقداردهی اولیه می کند (متغییر استاتیک در جاوا با کلیدواژه‌ی static مشخص می‌شود.). در نهایت در اخرین مرحله‌ی پیوند یعنی وضوح یا Resolution، مراجع نمادین با مراجع مستقیم موجود در مخزن زمان اجرا جایگزین می‌شوند. برای مثال اگر در کد شما 2 کلاس وجود داشته باشد که در یکی ارجاعی از دیگری وجود دارد در این مرحله ارجاعات با مقدار حقیقیشان جایگزین می‌شوند. برای همین به آن وضوح میگویند. در اینجا پیوند یا linking تمام می‌شود.ج) مقدار دهی اولیه یا initialization  : در نهایت در آخرین بخش بارگذار کلاس، بخش مقدار دهی اولیه یا Initialization به چشم میخورد که شامل اجرای متد مقداردهی اولیه کلاس یا اینترفیس است که اگر با زبان جاوا آشنایی داشته باشید حالت های مختلفی دارد مثلا سازنده کلاس ( constructor) و یا بلوک های استاتیک اجرا می‌شوند و مقادیر متغیرهای استاتیک هم به آنها داده می‌شود. احتمالا به این فکر میکنید که مقداردهی متغیرهای استاتیک که انجام شده بود! اینجا من هم برای بار اول کمی گیج شدم و وقتی بیشتر جست و جو کردم متوجه تفاوت مهم شدم. فرض کنید یک متغیر استاتیک به شکل زیر در برنامه دارید:static boolean visited = true;ابتدا در مرحله‌ی پیوند بخش آماده سازی(preparation) به این متغیر حافظه اختصاص داده می‌شود و مقدار پیشفرض به آن داده میشود. یعنی در یک جای حافظه به متغیر visited مقدار false داده شده که پیشفرض برای boolean است. حال در مرحله‌ی اخر یعنی مقداردهی، مقدار true به این متغیر داده می‌شود.به این ترتیب به پایان بخش class loader میرسیم.معرفی بخش منطقه‌ی داده‌های زمان اجرا یا Runtime Data Areaدر این بخش پنج مولفه‌ی اصلی داریم که در تصویر زیر قابل مشاهده اند و در ادامه به شرح انها می‌پردازیم:بهتر است این بخش را با یک مثال پیش ببریم. تکه کد زیر را در نظر بگیرید:الف ) بخش Method Area یا ناحیه متد، هنگام راه اندازی ماشین مجازی ایجاد می شود و تنها یک ناحیه متد برای هر JVM وجود دارد. داده های سطح کلاس در این بخش ذخیره می‌شوند. برای مثال فیلدها.در مرحله‌ی اول از کلاس دانشجو 2 فیلد اسم و شناسه در Method Area ذخیره می‌شوند.ب) ناحیه‌ی هیپ (Heap) مخصوص ذخیره سازی تمام اشیا و نمونه‌های مربوط به آنهاست. همان طور که میدانید با کلید واژه ی new در جاوا یک نمونه ایجاد می‌شود.Student s = new Student();در اینجا نمونه ی s ساخته شده در هیپ ذخیره می‌شود. مانند ناحیه متد، هیپ هم هنگام راه اندازی ساخته میشود و فقط یک ناحیه هیپ برای JVM وجود دارد.ج) متناظر با هر thread ساخته شده در JVM یک ناحیه پشته یا stack area ایجاد می شود که مختص زمان اجراست و تمامی متغیرهای محلی، فراخوانی توابع و نتایجشان در این بخش ذخیره می‌شود. احتمالا هنگام کد زدن با اکسپشن StackOverflowError مواجه شده باشید که وقتی پرتاب می‌شود که این پشته پر شده باشد و فضایی برای ذخیره مابقی اطلاعات باقی مانده نداشته باشید. در واقع پشته‌ی بزرگتری نیاز باشد.نکته: برای هر فراخوانی متد یک stack frame روی پشته گفته شده تشکیل می‌شود که به 3 بخش اصلی تقسیم می‌شود: متغییرهای محلی، operand stack و frame dataمتغیرهای محلی همانطور که احتمالا آشنا هستید داخل بدنه‌ی تابع به وجود می‌ایند و عمرشان پس از پایان تابع به پایان می‌رسد. در هر فریم پشته، یک پشته با قانون LIFO داریم که به انجام عملیات کمک می‌کند. در نهایت بخش فریم داده، تمام نمادهای مربوط به متد و اطلاعات بلوک catch را در صورت پرتاب شدن اکسپشن ذخیره می‌کند.د) شمارنده برنامه یا program counter که به اختصار pc گفته می‌شود. همانطور که میدانید جاوا از برنامه نویسی multi-thread هم پشتیبانی می‌کند. هر thread دارای یک PC Register مخصوص خودش را دارد و ادرس خطی از برنامه که درحال اجراست را نگه داری میکند و بعد از اتمام آن دستور ادرس دستور بعدی را نشان میدهد.هـ) در نهایت در آخرین کامپوننت یعنی Native Method Stack از متدهای بومی پشتیبانی می‌شود که به زبان‌هایی غیر از جاوا نوشته شده اند(مثلا خانواده‌ی c)تا اینجا 2 بخش از 3 بخش اصلی معماری JVM یعنی بارگذار کلاس و ناحیه‌ی داده‌های زمان اجرا را بررسی کردیم. در نهایت به بخش آخر یعنی Execution Engine می‌رسیم.معرفی موتور اجرا یا execution engineبعد از اتمام مراحل قبلی، نوبت به اجرای برنامه می‌رسد. موتور اجرا این کار را با اجرای کدهای هر کلاس انجام می‌دهد. در این بخش از معماری 3 کامپوننت اصلی داریم که در تصویر زیر قابل مشاهده اند:الف ) مفسر یا interpreter : در این بخش مفسر دستورات را خط به خط میخواند و اجرا می‌کند. (بعضی از زبان ها کامپایلر دارند برخی مفسر جاوا به گونه ای طراحی شده که ترکیبی از هردو را برای اجرا دارد تا شعار write once run anywhere اتفاق بیفتد.) 2 ایراد اصلی در این بخش وجود دارد که یکی سرعت پایین مفسر به علت خط به خط خواندن و اجرا کردن است و ایراد دوم این است که اگر یک متد چند بار فراخوانی شده باشد هربار باید تفسیر شود و مجددا باعث اتلاف زمان و انرژی می‌شود.ب) کامپایلر JIT ایرادات مفسر که گفته شد را رفع می‌کند. به اینن صورت که موتور اجرا اول از مفسر برای اجرای بایت کدها استفاده میکند ولی هر بار کد تکراری دید به جای تفسیر از کامپایلر JIT کمک میگیرد. در نهایت هم کامپایلر JIT کل کد را کامپایل میکند و آن را به کد قابل خوانش برای ماشینی که روی آن قرار است اجرا شود تبدیل میکند. در انتها در بخش نکات تکمیلیبیشتر در این باره صحبت خواهیم کرد.ج) در اخرین کامپوننت از این بخش زباله روب یا garbage collector را داریم که مسئولیت پاک سازی پشته از اشیاء مرده را دارد و کمک میکند جاوا کارامد شود. علاوه بر بهبود سرعت و سبک کردن کمک میکند تا فضای جدید برای اشیاء جدید ایجاد شود. دو مرحله‌ی اصلی در این بخش وجود دارند:علامت گذاری : شناسایی اشیاء استفاده نشده در حافظهجارو زدن: اشیائی که مرحله قبل شناسایی شد در این مرحله حذف می‌شوند.در بخش نکات تکمیلی در باره‌ی مدل زباله روب بیشتر توضیح داده‌شده است.تا اینجا 3 بخش اصلی معماری JVM را با یکدیگر مرور کردیم و کامپوننت های اصلی آنهارا یک به یک بررسی کردیم.در نهایت اگر به تصویر اول یعنی نمای درشتگانه‌ی JVM برگردیم 2 کامپوننت دیگر یعنی JNI و کتابخانه‌های متدهای بومی را میبینیم.همانطور که قبل تر اشاره شد، گاهی لازم است از زبان‌هایی غیر از جاوا در برنامه استفاده شود. مثلا هنگامی که نیاز است با سخت افزار تبادل صورت بگیرد. در اینجا JNI به صورت یک پل ارتباطی بین جاوا و سایر زبان‌ها عمل می‌کند و کتابخانه‌های متد های بومی (Native Method Library) که به صورت فایل‌های .dllیا .so هستند را هم بارگیری می‌کند.نکات تکمیلیزباله روبقبل تر گفتیم فرایند زباله‌روب از دو مرحله اصلی علامت گذاری و حذف تشکیل میشود. اگر بخواهیم دقیق تر این مسئله را بررسی کنیم در واقع فرایند زباله روب از 3 بخش اصلی تشکیل میشود:علامت‌گذاری: شناسایی اشیایی که در حال حاضر در حال استفاده هستند و در حال استفاده نیستندحذف عادی: حذف اشیاء استفاده نشده و بازیابی فضای آزادحذف با فشرده سازی: انتقال تمام اشیاء باقی مانده به یک فضای بازمانده (برای افزایش عملکرد تخصیص حافظه به اشیاء جدیدتر)ولی مشکلاتی هم دارد از جمله اینکه خیلی از اشیا تازه به وجود آمده هستند که دیگر استفاده نمی‌شوند و یا خیلی از اشیا با عمر طولانی به احتمال زیاد قرار است وارد چرخه‌ی زباله روب شوند.برای حل این مشکلات، اشیاء جدید در فضاهای نسل بندی شده جداگانه Heap ذخیره می‌شوند به گونه ای که هر فضا نشان دهنده طول عمر اشیاء ذخیره شده در آن باشد. سپس جمع‌آوری زباله در 2 فاز اصلی به نام‌های Minor GC و Major GC انجام می‌شود و اشیا قبل از حذف کامل، اسکن شده و در بین فضاهای نسلی جابه‌جا می‌شوند.در واقع اگر به تصویر دقت کنیم متوجه می‌شویم که هیپ به دو قسمت حافظه جوان و حافظه قدیمی تقسیم شده است. (مقدار حافظه هیپ قابل تغییر است. با کمک دستورات Xmx و Xms برای مقدار دهی اولیه و تعیین مقدار ماکسیمم)بررسی تفاوت پشته و هیپ در حافظه جاواابتدا به تصویر زیر توجه کنید:به طور خلاصه، همانطور که بالاتر جداگانه گفته شد حافظه پشته جاوا برای اجرای یک thread استفاده می‌شود و حاوی مقادیر خاص متد و ارجاع به اشیاء دیگر در Heap است.(که مفصل بحث شد)همچنین یک مثال بسیار خوب از منبع آخر پیدا کردیم که بررسی آن کمک میکند فرق این 2 را بهتر درک کنیم. ابتدا این تکه کد را در نظر بگیرید.در تصویر زیر فریم‌های بحث شده در بخش پشته به خوبی مشخص شده اند و ارجاعات به هیپ هم با فلش نشان داده شده است.منابع :https://dzone.com/articles/jvm-architecture-explainedhttps://dzone.com/articles/a-detailed-breakdown-of-the-jvmhttps://medium.com/interviewnoodle/jvm-architecture-71fd37e7826ehttps://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/https://medium.com/platform-engineer/understanding-jvm-architecture-22c0ddf09722https://www.guru99.com/java-virtual-machine-jvm.htmlhttps://medium.com/platform-engineer/understanding-java-memory-model-1d0863f6d973https://medium.com/platform-engineer/understanding-java-garbage-collection-54fc9230659aانجین‌ایکس (Nginx)ابزار Nginx یکی از پرکاربردترین ابزارها در دنیای اینترنت و وب است. این برنامه‌ یک برنامه‌ی سیستمی است که روی وب سرور‌ها نصب می‌شود و عملیات خدمت‌رسانی به کاربران را انجام می‌دهد. این خدمت رسانی شامل ارائه محتوای وبسایت به کاربران، کش کردن برای دسترسی سریع‌تر، لود بالانس کردن و کنترل پهنای باند می‌شود. دور از ذهن نیست که چنین برنامه‌ای برای هندل کردن تعداد زیادی کاربر نیاز به روند مناسب استفاده از منابع دارد چرا که با افزایش کاربران سیستم، منابع مصرفی از جمله پردازنده و مموری زیادی مصرف می‌شود. رقیب اصلی انجین‌ایکس که اتفاقا پرکاربردترین وب سرور دنیای امروز است، Apache نام دارم. تمرکز معماری Nginx بر حل کردن مشکلاتی است که در نتیجه‌ی تصمیمات طراحی و معماری آپاچی به وجود آمده است. آپاچی برای تعداد اتصال‌های زیاد، مصرف منابع بسیار زیادی دارد (رشد مصرف منابع به نسبت تعداد کاربران تقریبا خطی است). این معماری اگرچه به توسعه‌پذیری محصول کمک کرده و باعث شده قابلیت‌های متفاوتی پیاده‌سازی شوند، اما به ازای هر اتصال جدید یک بار از پروسس فورک می‌شود که به مصرف خیلی زیاد منابع برای تعداد اتصالات بالا می‌انجامد.معماری پیشنهادیروش بدیهی‌ای که برای پیاده‌سازی یک وب‌سرور به ذهن می‌رسد این است که به ازای هر کاربر متصل، یکسری منابع در قالب یک ترد یا یک پروسس در نظربگیریم و با آن به کاربر خدمت‌رسانی کنیم. مثلا از چنین رویکردی در پروژ‌ه‌های درس برنامه‌نویسی پیشرفته که تعداد کاربران همزمان بسیار کم است یا در یک نرم‌افزار که سرعت پیاده سازی قابلیت‌های جدید را در اولویت قرار می‌دهد استفاده می‌شود. اما با افزایش تعداد کاربران، افزایش تعداد ترد/پروسس‌ها را خواهیم داشت که به مصرف زیاد پردازنده و مموری و البته ایجاد مشکلات برای سیستم‌عامل به عنوان هماهنگ‌کننده و مدیر این پردازه‌ها خواهد بود. برای مثال زمان مورد نیاز برای عملیات زمان‌بندی زیاد می‌شود و سیستم دچار ترشینگ می‌شود. نکته‌ای که ما را به سوی معماری بهتری راهنمایی می‌کند این است که کاربرهای فعال هر کدام نیاز به پیگیری مدام و جدی و صرف پردازنده ندارند بلکه عموما کاربرانی هستند که هر از گاهی با آن‌ها کار داریم یا با اتصال خیلی کند مشغول دریافت و ارسال هستند و در واقع بیشتر «محدود به ورودی/خروجی» هستند. با تجربه‌ی به دست آمده از مزایا و معایب آپاچی، تیم توسعه‌ی Nginx تصمیم گرفتند از رویکرد «رویداد-محور» استفاده کنند. در این روش به جای ساخت تعداد زیادی ترد، تلاش زیادی می‌شود تا ترد/پروسس‌های ساخته شده مدیریت شوند. در واقع یک‌سری المان کارگر (worker) داریم که تعدادشان بسیار کم‌تر از تعداد کاربران است و تلاش می‌کنند بیشترین تعداد کاربری که می‌توانند را در آن واحد سرویس‌دهی کنند.  این ورکر‌ها پروسس‌های تک‌نخی هستند که به شکل پیش‌فرض تعداد کمی مثلا یکی از آن‌ها داریم اما با افزایش لود کاربران می‌توانند  کمی بیشتر هم شوند مثلا ۳ یا ۵ تا. دقت داریم که این تعداد کم ترد باعث می‌شود عملکرد نهایی پردازنده به بیشترین حالت خود برسد. به عنوان یک نمونه می‌توان گفت هر پروسس Worker می‌تواند تا هزاران اتصال را با همان یک نخ خود سرویس‌دهی کند.همانطور که گفته شد پروسه‌های Worker مسئول پاسخ به درخواست‌های کاربران هستند اما هماهنگی و مدیریت آن‌ها میسر نمی‌شود، مگر با هماهنگی یک مسئول هماهنگ‌کننده‌ی مرکزی (Master).در این قسمت خوب است به مکانیسم‌های Cache هم اشاره کنیم که این امکان را می‌دهند که صفحات به جای بارگذاری از روی حافظه ی جانبی با سرعت کم و تاخیر زیاد از حافظه‌ی موقت سریع‌تری بارگذاری شوند و زمان تاخیر نهایی بسیار کمتر شود. پر شدن کش با اولین درخواست به هر صفحه انجام می‌شود.جزئیات یک ورکرتا اینجا دانستیم که یک ورکر باید تعداد زیادی کاربر را با فقط یک نخ پردازشی مدیریت کند. برای این منظور یک حلقه ی اصلی وجود دارد (run-loop) که بر اساس الگوی «حل و فصل تسک‌ها به روش ناهمگام» و به کمک callback‌ها عملیاتی که برای هر کاربر باید انجام شود را وقتی نوبت به ان رسید انجام می‌دهد و سپس به سراغ کاربر بعدی می رود. این لیست کاربرانی که باید توسط یک ورکر مدیریت شوند از قبل توسط مستر به ورکر ارجاع شده و در صورت نیاز هم بروزرسانی می‌شود. مزیتی که وجود دارد این است که این بروزرسانی کم‌هزینه است و نیازی به سیستم کال‌های زیاد مثلا برای ساخت و یا حذف یک ترد وجود ندارد و فقط همان عملیات شبکه که الزامی است را انجام می‌دهد.جزئیات مسترپروسس مستر وظایف متفاوتی را انجام می‌دهد که همگی باعث کارکرد صحیح پروسس‌های دیگر می‌شوند. برای مثال تنظیمات مورد نیاز ادمین را از فایل‌ها خوانده و تعداد و تسک‌های ورکر‌ها را مشخص می‌کند و البته سوکت‌های جدید برای کاربران جدید باز می‌کند و به تناسب آن‌ها را می‌بندد. جزئیات کشبرای نیل به پرفورمنس مناسب استفاده از کش همواره یکی از تکنیک‌های محبوب است. انجین‌ایکس نیز به خوبی این نیاز را درک کرده و به شکل مناسب کش را پیاده‌سازی کرده است. برای کش از دو پروسس متفاوت استفاده می‌شود. اول cache loader که به اندیس‌گذاری فایل‌های مختلفی که امکان کش دارند در مموری می‌پردازد و به این شکل در مموری یک دیتابیس از  متادیتای فایل‌های کش شده خواهیم داشت. پروسس دوم cache manager است که به زمان انقضای یک داده در کش توجه دارد و همواره در حال اجرا می‌ماند.نکته‌ی قابل توجه این است که تا این لحظه انجین‌ایکس کش داخل مموری ندارد و عملیات کش به جاسازی فایل‌ها در همان فایل‌سیستم می‌پردازد  اما به گونه‌ای بهینه‌ با کمک بهینه سازی‌های سیستم‌عامل. عملیات پر کردن کش به این شکل است که  انجین‌ایکس داده‌ها را از سرور منبع اطلاعات می‌خواند و در یک فایل موقتی ذخیره می‌کند. بعد از اتمام دریافت فایل آن را به پوشه ی مخصوص کش منتقل می‌کند و سپس در صورت نیاز بر اساس URL موجود به محتوای کش به جای درخواست مستقیم به سرور دسترسی پیدا می‌کند. زبان پیاده‌سازیانجین‌ایکس با زبان محبوب سی برنامه‌نویسی شده است که زبان بسیار پرکاربردی برای برنامه‌های سیستمی است و امکان بهینه‌سازی‌ها را تا حد بسیار خوبی فراهم می‌کند. این زبان سطح پایین، نقطه ضعف بزرگی دارد و آن هم عدم ایمنی حافظه و امکان وقوع مشکلات حافظه از جمله نشت حافظه، دسترسی به خارج از محدوده‌ی مجاز و ... است. انتخاب این زبان اما بسیار فکر شده بوده و با عنایت به زبان‌های برنامه‌نویسی بالغ زمان شروع پروژه (که مثلا Rust جزو آن ها نبوده است)، و دانش و توانایی برنامه‌نویسان احتمالی انتخاب شده است.یکی از مشکلات Nginx قابلیت اجرا روی سیستم‌عامل‌های مختلف است. این ابزار با صرف زمان زیاد قادر شده روی اکثر سیستم‌‌عامل‌های دنیا سرویس دهد ولی بروی برخی سیستم‌عامل‌ها مثل ویندوز با تلاش زیاد و با غیرفعال شدن برخی ویژگی‌ها امکان کار پیدا کرده و استفاده از آن اصلا توصیه نمی‌شود و بیشتر جنبه‌ی «اثبات عمل» دارد. ریشه‌ی این امر در امکانات ناکافی هسته‌ی ویندوز در پشتیبانی از امکانات مورد نیاز در Nginx است. ممکن است فکر کنیم که در صورتی که از زبان‌های سطح بالاتری مانند جاوا استفاده می‌شود این مشکل وجود نداشت ولی اتفاقا برعکس. در این زبان‌ها امکان بهینه‌سازی و استفاده از قابلیت های خاص سیستم‌عامل اصلا به وجود نمی‌آمد و در همه‌ی سیستم‌عامل‌ها کارایی پایینی را تجربه می‌کردیم.منابعhttps://www.aosabook.org/en/nginx.htmlhttps://medium.com/@premsuryamj/nginx-architecture-9f97cf7887e2https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/توییچ معرفی توییچ (twitch)توییچ یک سرویس پخش زنده ویدیویی است که امکان پخش زنده بازی‌های ویدیویی، مسابقات ورزشی الکترونیکی، موسیقی و سایر محتواهای خلاقانه را ارائه میکند و در حال حاضر توسط یکی از شرکت‌های تابع آمازون اداره می‌شود. بخش بزرگی از مطالبی که در ادامه می‌خوانید از جمع بندی نوشته‌های یکی از مهندسان فعال در این شرکت است که سرگذشت تغییر معماری توییچ را توضیح می‌دهد.معماری اولیه:توییچ ابتدا با معماری یکپارچه یا به عبارتی monolith با استفاده از Ruby on Rails ساخته شد. از آنجایی که یک استات‌آپ تازه کار بود محدودیتی ایجاد نمی‌کرد و کمک کرد تا به سرعت محصول ارائه شود. کم کم با رشد سازمان برای ایجاد هماهنگی بیشتر و انعطاف پذیری و مهم تر از همه مقیاس پذیری لازم بود تا کدها به بخش‌های کوچک تر تغییر کنند. همچنین با افزایش کاربران بخش‌های مختلف سیستم به مرور در حال رسیدن به گلوگاه‌های عملکردی کردند. (bottleneck) برای مثال پایگاه داده، APIها و بخش جست و جو و به خصوص بخش چت دچار کندی شدند. یکی از مهم ترین بخش‌های توییچ هم چت بود و حتما لازم بود یک ارتباط در زمان واقعی بین صاحب ویدیو و بازدید کنندگان اتفاق بیفتد. کاربران توییچ می‌دانند که روح توییچ، چت آن است و باید با تاخیر بسیار ناچیز عمل کند.به گفته مهندس مذکور، در آن زمان سرویس چت روی 8 ماشین اجرا و کانال‌های چت به طور تصادفی بر روی آنها توزیع می‌شده است و معماری مناسبی بوده تا زمانی که رویدادهای نمایش بازی کاربران بر روی توییچ محبوب شد و ویدیوها تا 20000 بازدید داشتند که در حدود سال 2010 عدد عجیب و بسیار بزرگی بوده است.این افزایش و محبوبیت ناگهانی سرعت چت را خیلی پایین اورد و حدود 1 دقیقه تاخیر در چت مشاهده می‌شد که روح توییچ را دچار مشکل کرده بود. اولین راه استفاده از ماشین‌های بیش‌تر بود که مشکل اصلی را حل نمی‌‌کرد و ی راه حل مقطعی به حساب می‌آمد.ورود زبان Go به توییچخلاصه در آن زمان کم کم توییچ تلاش کرد به سمت میکروسرویس‌ها برود تا گلوگاه‌های اینچنین که گفته شد را از بین ببرد. آزمایش و تست‌های زیادی در ان زمان انجام شد. حتی روی استک های مختلف تحقیق صورت گرفت(به خصوص برای سرویس چت). در نهایت حدود سال 2012 تعدادی از مهندسان توییچ در یک جلسه به نام GoSF شرکت کردند و بررسی کردند که آیا استفاده از Go میتواند به محصولشان کمک کند یا خیر. (البته گفته میشه در آن زمان خود Go هم محبوبیت خوبی پیدا کرده بود). خلاصه مدیرعامل توییچ هم از این انتخاب حمایت کرد و تصمیم گرفتند تا ازمایش‌هایی انجام شود که بررسی کنند Go تا چه حدی می‌تواند ترافیک‌های دنیای واقعی را مدیریت کند. در این آزمایش علاوه بر تایید ایمن بودن Go یکی از مشکلات سرویس چت توییچ هم شناسایی شد!اولین میکروسرویس توییچ با Goاولین قطعه کد Go که روی سرورهای توییچ قرارگرفت بخش کوچک ولی مهمی از سرویس چت بود که مرتبط با تبادل پیام است. به گفته خودشان با حدود فقط 100 خط کد توانستند هر پیامی را با یک thread مدیریت کنند. بعد از اینکه Go در این مرحله خودش را به طور کامل اثبات کرد و تیم سرور pubsub پس از این مرحله به سرعت شروع به افزودن Go به توییچ کرد. به سرعت میکروسرویس‌های بعدی Go هم اضافه میشد. سرویس دوم Jax نام گرفت که طوری طراحی شده بود تا پخش زنده برتر را برای هر دسته فهرست کند. چالش ایندکس کردن داده‌های زنده در زمان واقعی گریبان‌گیر بود که برای حل آن راه‌های مختلفی ارائه شد مثلا PostgreSQL، شاخص‌های درون حافظه و در نهایت الاستیک سرچ.مهاجرتی عظیم!در حدود سال 2015 پس از اینکه Goخودش را به درستی اثبات کرد یک کمپین در توییچ صورت گرفت که تیم‌های مختلف کدهای خود را به سمت میکروسرویس‌های Go ببرند و اسم رمز خود را Wexit گذاشتند.به جز برخی میکروسرویس‌هایی که گفتیم اکثر سیستم همچنان داخل بستر یکپارچه‌ی Rails بود و این موضوع خود چالش‌های زیادی داشت. که به گفته‌ی مهندسان توییچ عبارت بود از:استفاده از یک خط‌لوله‌ی واحد برای دیپلوی همه‌ی کدهااستفاده از صفحات گسترده (spreadsheets) 	برای	هماهنگی‌های محیط مرحله بندی (staging environments)صبر کردن برای سرعت پایین بیلدهاتلاش برای ردیابی اشتباهات و لینک کردن آنها 	به صاحبشانمسیریابی کند، به علت وجود نقاط پایانی زیاد در API (یا همان end pointها)کوئری‌های مداوم و کم سرعت دیتابیستضادهای زمان ادغام بسیار زیاد و دست و پا گیریکی از بزرگ‌ترین گام‌هایی که برداشته شد، بازکردن به اصطلاح اسپاگتی‌هایی بود که در کد به مرور زمان ایجاد شده بود! بعد از این مرحله طاقت فرسا کمک شد تا سرویس‌های بالقوه‌ای که در توییچ بود شناسایی شود.یکی دیگر از مهم‌ترین تلاش‌ها که به گفته مهندسان شاید مهم‌ترین گام برداشته شده باشد افزودن یک لایه‌ی اضافه‌ در سمت بک‌اند برای API، یک NGINX پروکسی معکوس بود و برای انتقال درصدی از ترافیک به API جدیدی که با Go نوشته شده بود.بعد از این گام چند فرایند مهم در شرکت تعریف شد:میکروسرویس‌های جدیدی تولید شوند.نقاط پایانی قدیمی در لبه‌های جدید تکرار شوند.درصدی از ترافیک ها برای تست به این بخش انتقال داده شود.بعد 	از تایید معیارها و تست‌ها 100% ترافیک به این بخش برود.کمپین گفته شده مهاجرت بسیار بزرگ و عظیمی بود و تقریبا 2 سال از 2016 تا 2018 مهندسان را درگیر کرد.زیرساخت پخش زنده ویدیو در Twitchاین بخش از منبع چهارم گرفته شده و از گفته‌های مهندس توییچ نیست.همانطور که گفته شد هدف توییچ ایجاد بستری برای پخش زنده با قابلیت مقیاس پذیری و دردسترس بودن بالا بود. راه حلی که توییچ ارائه کرد به عنوان یک سرویس برای جهانیان از طریق AWS IVS فراهم است که سازمان‌هایی که از آن استفاده می‌کنند را قادر می‌کند تا خدمات استریمی خود را با سایر ادغام کنند.نکات کلیدی زیرساخت توییچ که باید در نظر گرفته شود:صدها هزار پخش کننده همزمان روی پلتفورم توییچ ایجاد می‌شود.هر پخش زنده دارای 5 سطح کیفیت ویدیو است.هر استریم یک بافر چندثانیه ای برای دانلود ویدیو قبل از موعد دارد که پخش به خوبی اجرا شود.طی 	سال‌ها تلاش تاخیر از 15 ثانیه	به 3 ثانیه	رسیده است. البته در شرایط خوب شبکه و اینترنت در کشورهایی مثل کره جنوبی این تاخیر 1.5ثانیه است.هنگامی که یک پخش کننده جریان خود را شروع می‌کند ویدیو وی توسط سرورهای توییچ رمز میشود. سپس به فرمت‌های مختلف تبدیل می‌شود تا در دستگاه‌های بیننده ها تحت شرایط مختلف پخش شود. از آنجایی که این موضوع گران بود ابتدا توییچ فقط 2 تا 3 % از کانال‌ها را رمز کرد. بعدها با کمک رمزگذاری مبتنی بر سخت افزار این هزینه‌ها را مقدار قابل توجهی کاهش داد.برای اطمینان از پخش روان استریم، از شراکت چند ISP محلی استفاده می‌شود و چندین PoP حضور دارد که با کمک یک شبکه‌ی backbone تغذیه می‌شود. بینندگان از سراسر دنیا ویدیوهارا از PoP ها میگیرند.در ابتدا، Twitch با یک مرکز داده (data center) واحد شروع به کار کرد که در آن جریان های ویدیویی زنده را پردازش می‌کرد. PoP ها HAProxy را اجرا کردند و جریان ها را به مرکز داده مبدا هدایت کردند. همانطور که گفتیم وقتی پلتفرم مورد توجه قرار گرفت و تعداد مراکز داده افزایش یافت، چندین چالش را با رویکرد HAProxy به همراه داشت. PoP ها به دلیل پیکربندی HAProxy، به صورت ایستا جریان های ویدئویی زنده را تنها به یکی از مراکز داده مبدا ارسال می کردند که منجر به استفاده ناکارآمد از منابع زیرساختی می‌شد.برای مقابله با این چالش‌ها، HAProxy کنار گذاشته شد و Intelligest توسعه یافت که یک سیستم مسیریابی ورودی برای توزیع هوشمند ترافیک ویدیوی زنده از PoPs به مبدا است.معماری Intelligest از دو جزء تشکیل شده است:اول 	Intelligest Media Proxy در حال اجرا در هر PoPدوم Intelligest Routing Service (IRS) که در AWS اجرا می‌شودپروکسی رسانه، با کمک IRS، مرکز داده مبدا مناسب را برای ارسال ترافیک تعیین می کند و بر چالش‌های پیش روی HAProxy غلبه می کند. البته IRS دو سرویس فرعی دیگر نیز دارد، خازن و چاه!خازن بر منابع محاسباتی موجود در هر مرکز داده مبدا و چاه نیز بر پهنای باند شبکه backbone نظارت دارد. با کمک اینها، IRS می تواند ظرفیت زیرساخت را در زمان واقعی تعیین کند. این باعث شده است که Twitch در زیرساخت های خود دسترسی بالایی (HA) داشته باشد.نگاهی سریع بر تکامل فرانت‌اندابتدا بخش فرانت‌اند جدایی و استقلال مناسبی از سمت بک‌اند نداشته است و بخشی از یک برنامه استاندارد Rails بود که HTML را در سمت سرور رندر میکرد و برای تعاملات هم از jQuery استفاده می‌کرده است. با گذشت زمان تبدیل به یک برنامه Ember.js  تک صفحه‌ای شد. با این روش فرانت‌اند به طور کامل و واضح از API سمت بک‌اند جدا شد. حدود سال‌های 2017 تا 2019 هم به زبان React js باز نویسی شد.توییچ امروزامروزه در توییچ اکثر تیم‌ها دارای چندین سرویس هستند و زبان انتخابی Go است. البته در سایر پلتفورم ها از زبان‌هایی مثل جاوا، کاتلین، پایتون، c++ و تایپ اسکریپت پشتیبانی میکند.هر سرویس هم در یک جساب جداگانه AWS ایزوله شده این الگو &quot;حباب‌های کوچک&quot; نام دارد. امروزه تیم های مختلف به طور مستقل عمل میکنند و زیرساخت‌های خود را مدیریت می‌کنند.منابع :https://blog.twitch.tv/en/2022/03/30/breaking-the-monolith-at-twitch/https://blog.twitch.tv/en/2022/04/12/breaking-the-monolith-at-twitch-part-2/https://blog.twitch.tv/en/2015/12/18/twitch-engineering-an-introduction-and-overview-a23917b71a25/https://scaleyourapp.com/live-video-streaming-infrastructure-at-twitch/https://blog.twitch.tv/en/2021/09/07/guiding-a-monolith-with-a-gentle-touch-the-power-of-pairing-codeowners-and-lint-rules/بررسی و مقایسه‌ی تاخیر لود سایت‌های توییتر و توییچدر نهایت برایمان جالب بود تا با کمک ابزاری مانند Jmeter بتوانیم لود تست روی سرویس‌های گفته شده انجام دهیم. پس از جست و جو ابزار آنلاینی مشابه آن پیدا کردیم که Pingdom نام دارد. البته شایان ذکر است که این سایت ساده، زمان لود شدن صفحه‌ی اصلی سایت را اندازه‌گیری می‌کند و خبری از تست APIهای مختلف نخواهد بود. ابتدا روی توییچ از اروپا و کشور آلمان لود تست انجام دادیم:یکی از ویژگی‌های این ابزار این است که پیشنهاداتی برای بهبود سایت ارائه می‌دهد!برای توییتر هم همین روند را پیش بردیم:بخش جالبی که وجود داشت این است که با این که زمان لود توییتر کمتر بود امتیاز پرفورمنسش از توییچ کمتر شد که البته با توجه به پیشنهادات میتوان کمی این موضوع را درک کرد:در نهایت هم نشان میدهد از ریکوئست‌های زده شده هر کدام چه ریسپانسی دریافت کرده اند:پس از آن یک ابزار دیگر هم به نام gtmetrix استفاده کردیم که نتایج نسبتا مشابهی داشت. البته با توجه به ساختار شبکه و سرعت اینترنت ممکن است نتایج این تست ها متفاوت باشد. برای مثال این بار از سمت ونکور در کانادا تست توییتر انجام شد:از مزایای این ابزار قابلیت مقایسه بود برای استفاده توییچ و توییتر را باهم مقایسه کردیم :البته در این ابزار دوم امتیازات توییچ با ابزار قبلی بسیار متفاوت بود! برای همین تصمیم گرفتیم از سمت شهرها و کشورهای دیگر مثل توکیو در ژاپن هم روی توییچ با ابزار اول تست انجام دهیم:از لحاظ تاخیر تقریبا شبیه بودند ولی به نظر میرسد فرمول محاسبه امتیاز این دو ابزار متفاوت باشد. نگاهی هم به وضعیت ریسپانس ها بیندازیم:و در اخر نتیجه تست از سانفرانسیکو در آمریکا:در ترم گذشته، با همکاری سرکار خانم مهندس تارا برقیان، زیر نظر جناب آقای دکتر علی‌اکبری معماری این نرم‌افزارها را بررسی کردیم و این مطلب، بخشی از تمرینهای درس معماری نرم‌افزار در دانشگاه شهیدبهشتی است.#معماری_نرم_افزار_بهشتی</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Fri, 10 Feb 2023 11:10:33 +0330</pubDate>
            </item>
                    <item>
                <title>توضیح بیست عبارت قلنبه‌ی معماری نرم‌افزار</title>
                <link>https://virgool.io/@rsharifnasab/20-software-eng-buzz-words-aggwyqxjtkjj</link>
                <description>این مطلب به عنوان تمرین درس «معماری نرم‌افزار» دانشگاه شهید بهشتی نوشته می‌شود. در این مطلب ۲۰ عبارت مرتبط با مهندسی نرم‌افزار یا معماری نرم افزار بررسی می‌شود. هدف این بررسی ارائه دانش سطحی است و با خواندن همین یک مطلب نمی‌توان متخصص حوزه شد!یک: Domain-driven Designیکی از ملزومات  توسعه‌ی نرم‌افزار به کمک متودولوژی‌های چابک، پاسخ سریع به تغییرات است، و معماری نرم‌افزار نیز باید این مورد را رعایت کند. DDD در واقع پاسخی به این نیاز است، به این صورت که یک‌سری قواعد وضع می‌کند که به معمار کمک می‌کند تصمیمات طراحی درستی را به شکل سریع‌تر بگیرد. DDD برای ما ابزار‌های جدید یا تکنولوژی‌های جدید به ارمغان نمی‌آورد، بلکه مزیت اصلی‌اش کمک به طراحی در همان ابزارهای قبلی است. همانطور که از اسمش نیز بر می‌آید، روش کار به این صورت است که طراحی بر پایه‌ی دامنه‌ی مسئله صورت می‌گیرد. به شکل دقیق‌تر، نرم افزار طوری طراحی می‌شود که تا حد امکان اجزای نرم‌افزار با اجزای مسئله که متخصصان آن زمینه شرح داده‌اند مطابقت داشته‌باشد. این قوانین برای ما چیز تازه‌ای نیست البته، قبلا هم در طراحی شی‌گرا (Object-oriented Design) می‌خواستیم اشیایی داشته باشیم که در دنیای واقعی وجود داشته باشند، در اینجا نیز هدف همین است که ساختار‌های زبان برنامه‌نویسی مانند نام کلاس، متد‌های کلاس و متغیرهای کلاس‌ها، با دامنه‌ی مسئله مطابقت داشته باشند.یکی از مزایای مهم این روش، نزدیک شدن زبان توسعه‌دهندگان به متخصصان آن حوزه‌ی خاص است و برای مواردی که برنامه‌نویسان دانش زمینه‌ی کافی ندارند می‌تواند کمک کننده باشد. البته میکروسافت توصیه می‌کند که این رویه را برای جاهایی که هم با دامنه‌ی مسئله آشنا نیستیم و هم مسئله خیلی پیچیده است نگه داریم، چرا که در حالات دیگر ممکن است کار را بیش از حد لازم پیچیده کند. منابع:  https://en.wikipedia.org/wiki/Domain-driven_design  https://domaindrivendesign.org/ddd-domain-driven-design/  https://domaindrivendesign.org/value-of-domain-driven-design/ دو: Hexagonal Architectureبیاید به این معماری، بگوییم پورت و آداپتور چرا که چندان ارتباطی با  ۶ ضلعی ندارد و اسم غلط‌اندازی است! در واقع اسم ۶ضلعی از شکل زیر آمده که برای توضیح این معماری به کار برده شده اما ۶ در آن عدد خاصی نیست.یک مثال از مدل ۶ضلعی که از قضا ۶ پورت و آداپتور داردهدف این معماری کاهش جفت‌شدگی (coupling)  بین اجزای متفاوت سیستم است، در عین حال که به سادگی به هم متصل می‌شوند. برای همین منظور نیز پورت‌ها و آداپتور‌ها را پیشنهاد می‌کند. به این ترتیب ورودی و خروجی‌های سیستم به گوشه‌ی طراحی رانده می‌شوند و منطق اصلی برنامه (Core) از ورودی/خروجی ایزوله می‌شود و بدون توجه به آن‌ها طراحی می‌شود. این ایزوله بودن این امکان را می‌دهد که بین هندلر‌های مختلف ورودی/خروجی‌ها را داشته باشیم بدون اینکه منطق اصلی برنامه اصلا از آن‌ها خبر داشته‌باشد. مشکلی که به شکل طبیعی در طراحی‌های شی‌گرا پیش می‌آید این است که بین لایه‌های مختلف اپلیکیشن (مثلا در معماری ۳ لایه) وابستگی ایجاد می‌شود، مثلا لایه‌های بالا به لایه‌های پایین وابسته می‌شوند و یا کد رابط کاربری به منطق بیزینس آلوده می‌شود. یکی از مزایای بزرگ این معماری، ساده شدن تست کردن است، چرا که همانطور که گفته شد منطق برنامه از این هندلرها آگاه نیست و می‌توانیم زمان تست یک هندلر مناسب تست را به جای هندلر اصلی قرار دهیم. حتی می‌توان به عنوان قانون گفت که یک پورت همواره به دو جیز متصل است که یکی از آن‌ها برای تست استفاده می‌شود. تفاوت پورت و آداپتور به این ترتیب است که پورت یک مسیر ورود یا خروج اطلاعات است مثل پورت USB که مبرا از تکنولوژی است. با کمک پورت برنامه امکان ارتباط با اجزای خارجی مثل مسیج بروکرها، دیتابیس‌ها و .. را خواهد داشت. اما آداپتور، با کمک یک تکنولوژی امکان اتصال برنامه با بیرون از طریق پورت را فراهم می‌کند، مثلا یک کنترلر REST یک اداپتور  است که با یک تکنولوژی خاص نوشته می‌شود و امکان ارتباط برنامه با بیرون را می‌دهد. مقایسه با مدل سه لایهمنابع: https://medium.com/ssense-tech/hexagonal-architecture-there-are-always-two-sides-to-every-story-bc0780ed7d9c  https://blog.ndepend.com/hexagonal-architecture/ سه: ‌CQRSپترن CQRS یا همان Command Query Responsibility segregation به حالتی گفته می‌شود که سیستم را به دو قسمت «دستورات» و «پرس‌وجوها» تقسیم کنیم. به بیان دیگر می توانیم دو روش جدا برای «ذخیره‌ی اطلاعات» و «بازیابی اطلاعات» به کار بریم. در در حالی است که به شکل عادی سیستم‌ها ۴ عملیات مختلف کار با داده دارند که به عنوان CRUD شناخته می‌شود (Create/Read/Update/Delete) اما مدل ذهنی ما برای داده بین همه‌ی این ۴ عملیات با هم برابر است. و یا نهایتا می‌توانیم تسهیلاتی ایجاد کنیم، مثلا چند رکورد را به شکل تجمیع شده به عنوان یک رکورد ببینیم یا موقع ذخیره‌ی اطلاعات، چیزی متفاوت با چیزی که برای ذخیره فرستاده می‌شود ذخیره کنیم. حالت CRUD سنتی بازنویسی با علم به CQRSپترن معماری CQS از آن‌جا که CQRS حالت عمومی‌شده‌ای از CQS است، بهتر است اول CQS را خوب بشناسیم. CQS در واقع یک الگوی طراحی مخفف Command query separation است و همانطور که از اسم آن برمی‌آید دو قسمت مختلف برای «دستور» که یک عملیات را انجام می‌دهد و یک «پرس‌وجو» که اطلاعات برمی‌گرداند در نظر می‌گیرد. همچنین گفته می‌شود در این الگو هرگز نباید یک تابع هردو کار را انجام دهد. اما CQRS حالت عمومی‌تری است و می‌گوید بازیابی اطلاعات و ذخیره/تغییر اطلاعات باید به چشم دو امر جدا نگاه شود.از مزایای CQRS می‌توان به کاهش coupling، نزدیک‌تر شدن کد به کسب و کار (همانند آن‌چه در DDD داشتیم)، اسکیل راحت‌تر و هماهنگی با پترن‌های دیگر مثل Event sourcing و  Event-based programming اشاره کرد. منابع:  https://www.ibm.com/cloud/architecture/architectures/event-driven-cqrs-pattern/  https://en.wikipedia.org/wiki/Command%E2%80%93query_separation  https://www.ibm.com/cloud/architecture/architectures/event-driven-cqrs-pattern/  https://www.eventstore.com/cqrs-pattern  https://www.martinfowler.com/bliki/CQRS.html  https://www.ibm.com/cloud/architecture/architectures/event-driven-cqrs-pattern/ https://www.ibm.com/cloud/architecture/architectures/event-driven-cqrs-pattern/چهار: ‌MVVMپترن طراحی MVVM مخفف Model-View-viewmodel است. هدف این پترن کاهش وابستگی و جدا کردن رابط کاربری (مثلا گرافیکی) یا همان View و منطق برنامه یا همان Model است. همچنین امکان تست و نگهداری و اضافه کردن ویژگی به برنامه در طولانی مدت ساده می‌شود. Model وظیفه‌ی نگهداری داده‌ها و هندل کردن منطق کسب‌وکار را دارد. در دید شی‌گرایی می‌توان به آن domain model نیز گفت و در دید داده-محور به آن data access layer نیز می‌گویند. در طرف مقابل، View نهایتا اطلاعاتی را نگه می‌دارد که برای کاربر لازم است. در واقع خود view ساختار، شکل و چیزی است که کاربر می‌بیند، مشابه چیزی که در MVC و MVP نیز داریم. به این ترتیب وظیفه‌ی دریافت اطلاعات و eventها از سمت کاربر نیز هست. سپس این اطلاعات با کمک viewmodel یا همان binder به مدل انتخاب داده می‌شود. در این میان، viewmodel نیز یک ارتباط بین این دو جز برقرار می‌کند، در واقع مشابه یک تبدیل‌گر اطلاعات مورد نیاز هر یک را از فرمت دیگری تبدیل می‌کند، همچنین می توان گفت viewmodel به model نزدیک‌تر است چرا که از جنس منطق برنامه است و اکثر منطق گرافیک نیز در همین قسمت هندل می شود. به این قسمت binder نیز گفته می‌شود که در این صورت به کل مدل model-view-binder خواهیم گفت. این قسمت به جای Presenter در MVP و یا Controller در MVC می‌آید. منابع:  https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel  https://www.educba.com/what-is-mvvm/ پنج: ‌Event Sourcingیکی از روش‌های مرسوم نگهداری داده این است که حالت فعلی (یا همان آخرین اپدیت) را به شکل کامل نگهداریم، مثلا اگر یک حساب بانکی داریم، موجودی نهایی آن را نگه داریم، اما اگر گره‌های مختلفی داشته باشیم که داده را می‌نویسند دچار مشکل می‌شویم، چرا که ممکن است چندین نسخه‌ی آپدیت تقریبا همزمان داشته باشیم و نمی‌دانیم کدام نهایی است. (اصلا آیا فقط یکی نهایی است؟)‌برای حل این مشکل، «نگهداری حالت نهایی» را کنار می‌گذاریم و برای هر موجودیتی که می‌خواهیم ذخیره کنیم، لیستی از اتفاقاتی که روی آن افتاده را مبنا قرار می‌دهیم. (event sourcing یا همان مبنا قرار دادن eventها!) به این ترتیب با دنبال هم گذاشتن اتفاقات و دنبال کردن state بعد از هر کدام، می‌توانیم به حالت نهایی برسیم، در مثال حساب بانکی می‌توان تغییراتی که روی موجودی اتفاق افتاده مثل همه‌ی برداشت‌ها و واریز‌ها را دنبال کرد تا به موجودی نهایی رسید. به این ترتیب اگر دو اتفاق تقریبا همزمان بیفتد می‌توانیم آن‌ها را به ترتیب دلخواه اعمال کنیم و به نتیجه نهایی برسیم.یکی از بزرگ‌ترین چالش‌های این روش، زیاد شدن event‌ها و در نتیجه کند شدن سیستم است چون هربار بخواهیم وضعیت سیستم را به دست بیاوریم باید تعداد زیادی event  را دنبال کنیم.Model information about activity in the domain as a series of discrete events. Represent each event as domain object. Eric Evans, Domain-Driven Design Reference یکی دیگر از مشکلاتی که وجود دارد این است که اگرچه ما می توانیم به تاریخچه‌ی وضعیت‌ها و تغییرات در زمان‌های مختلف دسترسی داشته باشیم، اما اتفاقاتی که موجب این تغییر شده نمی‌دانیم و فقط می‌توانیم آن را حدس بزنیم، مثلا اگر موجودی حسابی کم شده باید حدس بزنیم بابت برداشت بوده یا پاس شدن چک یا قسط وام یا ...  یعنی می‌دانیم برداشتی انجام شده اما نمی‌دانیم این برداشت بابت چه بوده و جزئیات بیشتر را از دست داده‌ایم. منابع: https://www.eventstore.com/blog/what-is-event-sourcing  https://microservices.io/patterns/data/event-sourcing.html شش: ‌Micro Front-endsبه طور خلاصه، micro front-end، یک سبک طراحی و تلاشی برای پیاده‌سازی و گسترش الگوی  micro service‌ها در برنامه‌نویسی سمت کاربر است. اما ابتدا خوب است مزیت‌های میکروسرویس را در همان سمت سرور مرور کنیم. به کمک میکروسرویس کد‌ هر سرویس کوچک‌تر و قابل مدیریت‌تر بود و هر تیم می‌توانست برای خود و به شکل مستقل یک یا چند سرویس را نگهداری کند در حالی که بدون میکروسرویس، یک سایت یکپارچه‌ی کامل (یا همان monolithic) توسعه داده می‌شود اما نگهداری این تکه کد یکپارچه برای برنامه‌نویسان سخت بود.با میکروسرویس (و بدون میکروفرانت‌اند) به مزیت‌های گفته شده فقط در سمت بک‌اند دست یافته‌ایم اما تیم های فرانت‌اند هنوز مشکل کار بر روی یک سیستم بزرگ یکپارچه با مدیریت دشوار را دارند. در عوض میکروفرانت‌اند می‌گوید یک تیم که برنامه‌نویسانی با مهارت‌های گوناگون (مثلا فرانت‌اند و بک‌اند) را دارد داشته باشیم و در همین تیم یک قابلیت مدیریت و پیاده‌سازی شود. علاوه بر مزیت‌های گفته شده، در این حالت امکان این وجود دارد که قسمت‌های مختلف سایت با چارچوب‌های نرم‌افزاری متفاوتی نیز توسعه داده شوند، برای مثال یک تیم علاقه دارد vue.js را به کار برد و تیم دیگر انگولار را ترجیح می‌دهد. چالشی که در این حالت وجود دارد، حفظ یکپارچگی بین قسمت‌های مختلف وب‌سایت است. چرا که برای جدا طبق اصول میکروفرانت‌اند، تکه‌های کوچک باید حداقل کد ممکن را با یکدیگر به اشتراک بگذارند و از state سراسری باید پرهیز شود.منبع:   https://micro-frontends.org/ هفت: ‌Low code platforms بدون برنامه‌نویسی هم می‌توان برنامه‌نویسی کرد؟ پلتفرم‌های low code  تلاش می‌کنند به این سوال جواب مثبت دهند. به این ترتیب کسانی که تجربه برنامه‌نویسی کمی دارند یا حتی اصلا تجربه برنامه‌نویسی ندارند با کمک رابط کاربری گرافیکی (مثلا شکل‌ها و فلش‌ها و ارتباطشان) روندی که در نظر دارند را پیاده‌سازی کنند. شاید در ابتدا به نظر برسد که این روش‌ها کارآمد نیستند و در نهایت برای کودکان مناسب هستند (مثلا زبان برنامه‌نویسی scratch) اما اینطور نیست. با کمک همین ابزار‌های بدون کد و یا نهایتا با افزودن مقدار کمی برنامه‌نویسی، می‌توان به برنامه‌های کاربردی دست یافت. همچنین در این صورت محدود به نیروی‌انسانی برنامه‌نویس خبره برای توسعه‌ی سیستم نیستیم و برنامه‌نویسان مبتدی، متخصصان در حوزه‌های مختلف (مثلا آمار یا کشاورزی) که نیاز برنامه‌نویسی دارند یا حتی کاربران نهایی سیستم می‌توانند در ساخت سیستم مشارکت کنند و آن چه در ذهنشان است را وارد دنیای صفر و یک کنند. از مزایای سیستم‌های low-code، علاوه بر مشارکت نیروهای وابسته ولی غیر برنامه‌نویس، می‌توان به خلوت شدن سر برنامه‌نویسان برای قسمت‌های دیگر سیستم، تولید سامانه‌های تست‌شده و قابل اطمینان و امن اشاره کرد. همچنین یکی از مهم‌ترین ویژگی‌ها، هدر ندادن زمان  و انرژی است چرا که هرکس می‌تواند خودش پیاده‌سازی‌ای از ایده‌ی مدنظرش داشته باشد و دیگر نیاز نیست برنامه‌نویس استخدام کند و مدت زمان زیادی منتظر بماند. خوب است به عنوان مقایسه‌، پلتفرم‌های No Code را هم بشناسیم. در این سامانه‌ها همه‌ی کار طراحی توسط همان رابط گرافیکی انجام می‌شود و سامانه به کمک کار برنامه‌نویسان قابل گسترش نیست. اگرچه شاید این مورد ترس کاربران مبتدی را حل کند چرا که هیچوقت قرار نیست مجبور شوند تا کد بنویسند، ولی در عمل Low Codeها مشکل مهم قابل گسترش نبودن را حل کرده‌اند و سامانه‌ی طراحی شده به کمک Low Code آینده‌دار خواهد بود. شاید با اغراق بتوان گفت low code برای کسانی که آشنایی با برنامه‌نویسی دارند یا حداقل دسترسی به برنامه‌نویسی دارند کمک کننده خواهد بود که سرعت و کیفیت کارشان را در توسعه‌ی یک سیستم واقعی و پیچیده بهبود دهند ولی no code برای کسانی که اصلا تجربه برنامه‌نویسی ندارند مناسب است تا بتوانند یک چیز ساده پیاده کنند. منابع:  https://en.wikipedia.org/wiki/Low-code_development_platform  https://www.mendix.com/low-code-guide/#what-is-lowcode  https://www.mendix.com/blog/understand-no-code-vs-low-code-development-tools/  https://kissflow.com/low-code/low-code-vs-no-code/ هشت: ESBبرای فهمیدن ESB یا Enterprise System Bus اول باید خود مفهوم Bus را در کامپیوتر متوجه شویم.اولین تصویری که از Bus به ذهن خود من متبادر می‌شود اما اگرچه ذهن خلاق شما می‌تواند اشتراکات پیدا کند ولی این اتوبوس به باس در کامپیوتر ارتباطی ندارد.نمایی از یک Busاما دوستانی که دانش سخت‌افزاری بیشتری داشته‌باشند می‌دانند که Bus در کامپیوتر به تعدادی سیم می‌گویند که از همگی از یک مبدا واحد به یک مقصد واحد می‌روند. ممکن است در کامپیوتر تعدادی باس خارجی (مثل تصویر زیر) داشته باشیم که مثلا به کارت گرافیک و .. متصل شوند و یک با بین پردازنده و مموری اصلی باشد.اما باس نرم‌‌افزاری که اینجا مورد صحبت است این هم نیست! حتی به توپولوژی باس شبکه نیز ارتباطی ندارد. بلکه باس نرم‌افزاری بین دو یا چند نرم‌افزار ارتباط برقرار می‌کند. بهترین مثالش هم همان باس سرویس سازمانی است. این مدل از باس‌ها، زمانی به کار می‌آیند که چند نرم‌افزار مختلف داریم و می‌خواهیم آن‌ها را به هم متصل کنیم. مثلا در حالت سازمانی، چند سرویس متفاوت داریم که از خدمات یکدیگر استفاده می‌کنند، حالا به جای اینکه هر دوتای اینها، یا همه‌ی آنها که به هم نیاز دارند متصل به هم وصل باشند، هرکس به باس متصل است و همه‌ی اتصالات از این طریق مدیریت می‌شوند. مهم‌ترین کاربردی که می‌تواند داشته باشد این است که ارتباط مستقیم بین سرویس‌های مختلف از بین می‌رود و به این ترتیب دی‌کاپلینگ اتفاق می‌افتد. همچنین می‌تواند بین پروتکل‌ها نیز تبدیل انجام دهد، مثلا اگر یک سرویس به rest نیاز دارد ولی سرویس دیگر امکان عرضه‌ی soap را دارد، یک باس می‌تواند این تبدیل فرمت را انجام دهد، حتی بعضی باس‌ها امکان اسکریپت‌نویسی دارند. از خدمات دیگری که ممکن است باس‌ها ارائه دهند می‌توان به مدیریت ورژن‌های api ها و مانیتور کردن ارتباطات اشاره کرد. لازم به ذکر است که کاربرد عمده‌ی این باس‌ها در معماری مبتنی بر سرویس (service oriented) است که سرویس‌های متفاوتی در سیستم وجود دارند و هریک می‌توانند از دیگران خدمت بگیرند. منابع: https://turbofuture.com/computers/buses  https://en.wikipedia.org/wiki/Enterprise_service_bus نه: API Gateway یکی از ابزار‌هایی که به نیاز  API  Management پاسخ می‌دهد، API Gateway است. این ابزار اینقدر مهم است که کمتر سرویس سازمانی‌ای را می‌توان یافت که بدون یک API Gateway مستقر شده باشد. این ابزار به نیاز‌های مختلفی مثل محدود کردن سرعت درخواست‌ها، ارائه‌ی آمار و ارقام و بررسی و تایید هویت کاربر می تواند پاسخ دهد. حتی ممکن است این gateway به یک سیستم دیگر نظیر پرداخت متصل باشد و بررسی کند که آیا کاربر اعتبار لازم را دارد یا خیر. در واقع، یک نگاه به gateway، می‌تواند یک پروکسی معکوس باشد که تا حدی پیاده‌سازی پشت API‌ را از دید کاربر بیرونی مخفی کند، مثلا مانند مثال پرداخت که گفته شد ممکن است چند سرویس با هم ترکیب شوند یا اصلا پیاده‌سازی نسخه جدید عوض شده باشد ولی کاربر بتواند از نسخه‌ی قدیمی به همان شکلی که قبلا استفاده می‌کرد استفاده کند. همه‌ی این‌ها به این دلیل میسر می‌شوند که کاربران به جای کار کردن مستقیم با سروری که خدمت مورد نظرشان را ارائه می‌دهد، به یک واسط (پروکسی معکوس) متصل شوند و مدیریت لازم آن‌جا انجام شود. به عنوان جمع‌بندی می‌توان گفت یک api gateway، خدماتی نظیر مانیتورینگ را به کاربران بیزینسی سامانه می‌دهد و برای کاربران بیرونی سامانه، عملیات ترکیب نتیجه‌ی کار میکروسرویس‌ها، تبدیل پروتوکل پیام‌ها و ارائه‌ی خدمات مختلف برای فرانت‌های مختلف (مثلا وبسایت یا اپلیکیشن اندروید) اشاره کرد.منابع: https://www.redhat.com/en/topics/api/what-does-an-api-gateway-do  https://microservices.io/patterns/apigateway.html  https://en.wikipedia.org/wiki/Enterprise_service_bus ده: Business Process Management Systems (BPMS)سیستم BPMS ، یک سیستم برای اتوماتیک کردن و مستند کردن و آنالیز کردن پروسه‌های بیزینس است. اما در ابتدا باید دید BPM چیست که حالا یک سامانه برای مدیریت و خودکار کردن آن می‌خواهیم؟خود BPM عملیاتی است که شرکت‌ها انجام می‌دهند تا پروسه‌های کسب و کار خود را بهبود دهند. این عملیات شامل شناسایی اتفاقات، مستند کردن و بهبود دادن پروسه هاست. منابع: https://www.integrify.com/what-is-bpms/یازده: ‌Business Rules Management Systems (BRMS) یک سامانه‌ی BRMS، هدفش مدیریت قوانین سازمان است اما اصلا قوانین سازمان چه چیزهایی هستند؟ سازمان وقتی می‌خواهد تصمیمی بگیرد از اطلاعات زیادی استفاده می‌کند مثلا اطلاعات مشتریان و فروش و .. اما این اطلاعات به چه طریقی تبدیل به تصمیم می‌شوند؟ راه‌های مختلف وجود دارد مثلا استفاده از هوش مصنوعی یا قوانین سازمان (همین BRها)پسBRMS یک ابزار است که به سازمان این امکان را می‌دهد که قوانینش را تعریف کرده، نگهداری و مدیریت و بهینه و از همه مهم‌تر اجرا کند، یعنی با کمک منطقی که تعریف می‌شود تصمیمات به شکل خودکار گرفته شوند. منابع: https://research.aimultiple.com/business-rules-management-system/https://www.processmaker.com/blog/what-is-a-business-rules-management-system-brms/دوازده: Message Queue (e.g. Kafka &amp; RabbitMQ)مسئله‌ی تولیدکننده و مصرف‌کننده را حتما به یاد دارید. یک تولید کننده داریم که با نرخی پیام تولید می‌کند و یک مصرف‌کننده داریم که با یک نرخ دیگری پیام‌ها را مصرف می‌کند. در این بین یک صف داریم که پیام‌ها از زمان ارسال تا زمان مصرف شدن باید داخل آن قرار داشته باشند چرا که تولید کننده و مصرف کننده مستقیما به هم متصل نیستند. حالا حتی ممکن است چند مصرف کننده یا چند تولید کننده به جای یکی داشته باشیم. در این صورت که اصلا امکان اتصال به هم را نخواهند داشت! در این مثال صف و در دنیای واقعی یک مسیج کیو (که همان صف پیام‌هاست) بین سرویس‌های تولید کننده و مصرف کننده قرار می‌گیرد و مانند چسب آن‌ها را به هم متصل می‌کند. نکته دیگری که وجود دارد این است که چه می‌شود اگر یکی از دریافت‌کننده‌های پیام با مشکل مواجه شود؟ یک صف خوب باید پیام‌های آن را در خودش نگه دارد با بتواند به کار برگردد و بعد از آن پیام‌هایش را در اختیارش بگذارد،‌ یا مثلا بسته به قواعدی آن پیام‌ها را در اختیار یک مصرف‌کننده‌ی دیگر بگذارد. در تصویر بالا به طور مثال چند تولید کننده داریم که همگی پیام‌هایشان را در یک صف می‌ریزند تا بعدا توسط مصرف کننده استفاده شود. حالا اگر مصرف کننده کمی کُند کار کند یا اصلا مدتی در دسترس نباشد صف باید پیام‌ها را در خود نگه دارد یا دوباره شرابط عادی شود. در این بین پیام‌ها چه چیزهایی هستند؟ به طور کلی محدویت خاصی روی آن ها نیست به جز اینکه ترجیح می‌دهیم اندازه‌شان کوچک باشد، مثلا اندازه‌ی ۲۵۶ کیلوبایت برای یک پیام، یکی از قواعد رایج است، اما اگر نیاز به اطلاعاتی با حجن بالا داریم باید ابتدا آن‌ها را در محلی ذخیره کنیم و URI آن‌ها را به عنوان پیام به صف دهیم. یکی از مثال‌های صف‌ها، برای فرستادن کوئری ها به دیتابیس است، به این شکل که کوئری ها در صف قرار می‌گیرند تا به تعداد مشخصی برسند و سپس با هم ارسال می‌شوند با همه با هم پردازش شوند، در این صورت کارایی دیتابیس بالا می‌رود به نسبت حالتی که جدا جدا و تک‌تک کوئری ها را گرفته و پردازش کند. منابع: https://www.howtogeek.com/devops/what-are-messaging-queues-and-how-do-you-use-them/https://www.ibm.com/cloud/learn/message-queueshttps://aws.amazon.com/message-queue/سیزده: Docker and Containerizationتصویر زیر را ببینید، به یکی از مشکلات مهم توسعه‌ی نرم‌افزار اشاره می‌کند. برنامه‌نویس کدی را می‌نویسد که بالاخره روی سیستم خودش کار می‌کند اما آیا روی سیستم مشتری یا سرور‌های عملیاتی نیز کار می‌کند؟ باید کار کند ولی لزوما این اتفاق نمی‌افتاد! اما با کمک کانتینر کردن (که داکر یکی از ابزارهای مهم آن است) این مشکل حل می‌شود. یعنی برنامه‌نویس با نوشتن یک داکرفایل مشخص می‌کند دقیقا برنامه به چه محیط عملیاتی‌ای نیاز دارد و در سییستم خودش نیز در همان محیط اجرا می‌کند، در سرور نیز همان محیط فراهم می‌شود و برنامه (انشالا) کار می‌کند. ایده‌ی پشت داکر!اما مزیت بالا تنها مزیت این روش نیست. مزیت دیگر را باید در مشکلات مجازی‌سازی جست‌وجو کنیم. اما ابتدا خلاصه ای از مجازی‌سازی داشته باشیم. مجازی‌سازی به ما این امکان را می‌دهد که یک سرور فیزیکی قدرتمند را بین چند کاربر یا چند کار مختلف به اشتراک بگذاریم. کاربران خانگی معمولا در این حد از مجازی‌سازی بهره‌ می‌برند که در کنار سیستم‌عامل اصلی خود که بالاست و کار می‌کند یک سیستم عامل دیگر به شکل مجازی می‌آورند و از مزایای هردو کنار هم بهره می‌برند. مثلا یک کاربر ویندوز می تواند به شکل مجازی لینوکس نصب کند و از آن برای برنامه‌نویسی استفاده کند. در سرورها نیز یک سرور قدرتمند‌ را تبدیل به مثلا ۵ سرور مجازی با منابع محدود و مشخص می‌کنند و هر کدام را به یک کاربر اجاره می‌دهند. حتی ممکن است یک کاربر دو یا چند تا از آن‌ها را اجاره کند تا برنامه و کاربرهای متفاوت را روی آن‌ها اجرا کند. ویژگی خوب مجازی‌سازی این است که برنامه‌ها و اتفاقات روی سرور‌های مجازی مختلف اگرچه واقعا روی همان پردازنده و مموری یک سرور فیزیکی هستند، از یکدیگر جدا است و حتی از وجود همدیگر خبر ندارند.اما مشکل بزرگ مجازی‌سازی، این است که در کنار یک hypervisor مثلا vmware که وظیفه مدیریت ماشین‌های مجازی را دارد، به ازای هر ماشین مجازی یک سیستم‌عامل نیز در حال اجراست و سیستم‌عامل ذاتا سنگین و با سربار بالاست. کانتینریزشن در واقع تلاشی برای استفاده از یک هسته‌ی مشترک سیستم‌عامل و جدا کردن برنامه‌های مختلف در قالب کانتینر است. یعنی مثلا یک ماشین لینوکسی داریم و چندین کانتینر داریم که هر کدام از همین کرنل لینوکس استفاده می‌کند و با کمک امکانات هسته مثل فضای نام و cgroups، کانتینر‌ها از هم مستقل هستند و از وجود هم با خبر نیستند. مزیت کانتینر‌ها این است که با به اشتراک گذاشتن هسته‌ی سیستم‌عامل، سربار بسیار کمتری دارند و از چند گیگابایت به چند مگابایت حافظه رسیده‌ایم و سرعت راه‌اندازی آ‌ن‌ها نیز بسیار بالاتر است.داکر به عنوان بازیگر اصلی حوزه‌ی کانتینریزیشن، یک مکانیسم برای این مورد ارائه می‌کند که اگرچه تنها بازیگر نیست ولی رقایبش را تا حد زیادی کنار زده است. داکر با راه‌ندازی پلتفرم ابری و فراهم کردن بستری برای به اشتراک گذاشتن imageهای متفاوت، کار را برای برنامه‌نویسان راحت کرده است. منابع: https://www.ibm.com/cloud/learn/containerizationhttps://www.ibm.com/cloud/blog/5-benefits-of-virtualizationhttps://blog.adamchalmers.com/kubernetes-problems/#containershttps://matt-rickard.ghost.io/what-are-containers/https://www.ibm.com/cloud/learn/dockerچهارده: Container Orchestration (e.g. Kubernetes) فرض کنیم تقسیم برنامه‌مان به کانتینر‌ها را خیلی خوب انجام دادیم و یک برنامه‌مان تبدیل به ده‌ها کانتینر شد، اما حالا چی؟ برای استقرار به جای یک موجودیت که باید فعالش کنیم الان ده‌ها مشکل داریم! حالا تصور کنید که CI هم داریم و به ازای هر کامیت برای ما یک کانتینر آماده‌ی اجرا می‌سازد، حالا از کجا بفهمیم کدام مجموعه از کانتینرها با چه تریبی باید اجرا شوند؟ با کمک رهبر کانتینرها رهبر ارکستر چه می‌کند؟‌ تنظیم! اینجا نیز نیاز به رهبری برای تنظیم این همه کانتینر و سرور مختلف داریم. کوبرنتیز به عنوان یک ابزار، این کار را برای ما انجام می‌دهد، به او می‌گوییم چه ترتیبی از کانتینرها را در کجاها اجرا کن و این کار را می‌کند. حتی کارهای پیشرفته‌تر و کاربردی‌ای نیز انجام می‌دهد مثلا اینکه اگر یک کانتینر کرش کرد و دیگر کار نکرد آن را ریستارت می‌کند، یا مثلا با اضافه کردن replica از کانتینرهای موجود، موجب تقسیم بار کاری بین آن‌ها می‌شود. در اینجا خوب است به docker-compose نیز اضافه شود که یک ابزار بسیار ساده‌تر برای هماهنگ‌سازی است که در زمانی کاربرد دارد که در حال توسعه‌ی سیستم هستیم و می‌خواهیم فقط روی یک سرور سیستم را بالا بیاروریم و نه روی چندین سرور مجزا و به همین ترتیب سبک تر و بی‌امکانات تر است. منابع: https://blog.adamchalmers.com/kubernetes-problems/#container-orchestrationhttps://www.redhat.com/en/topics/containers/what-is-container-orchestrationhttps://www.ibm.com/cloud/learn/container-orchestrationپانزده: Log Management Tools (e.g. ELK)لاگ‌ها برای ما بسیار مهم و حیاتی هستند، مثلا با کمک آن‌ها می‌توانیم سریع‌تر بفهمیم که منبع یک مشکل کجا بوده تا راحت‌تر بتوانیم آن را حل کنیم. اما مشکلی که داریم این است که حجم لاگ‌ها زیاد است و به راحتی مثلا با فایل‌های متنی ساده و انتقال از طریق شبکه به روش معمولی نمی‌توان به شکل مناسب از آن‌ها استفاده کرد. لازم به یادآوری است که در معماری میکروسرویس، ما چندین سرویس ریزدانه‌ی مختلف خواهیم داشت که هر کدام برای خودش در کانتینر خودش لاگ می‌زند و پیدا کردن یک مشکل با بررسی همه ی لاگ‌ها که ترتیب زمانی هم لزوما ندارند اصلا آسان نخواهد بود. پس نیاز به یک مکانیسم مشخص برای بررسی لاگ‌ها خواهیم داشت. استک Elastic یا همان ELK یکی از راه‌های مدیریت لاگ است. ELK در واقع به شکل عمومی تر برای مانیتور و بررسی زیرساخت استفاده می‌شود و به ما این امکان را می‌دهد که به شکل real-time لاگ‌ها را مدیریت و آنالیز کنیم. ELK استکی متشکل از ۳ ابزار دارد که اسم مخفف آن را نیز تشکیل داده اند: Elastic search و Logstash و Kibana. در این کاربرد Logstash لاگ‌های ورودی را دریافت و ادغام کرده و به الاستیک سرچ می‌فرستد. سپس الاستیک سرچ آن‌ها را ایندکس می‌کند که قابلیت سرچ و آنالیز داشته باشند. سپس با کمک کیبانا و استفاده از یک زبان کوئری، می‌توانید اطلاعات را از الاستیک سرچ استخراج کنید، مثلا برای نمودارهای گرافیکی و ... منابع: https://www.educba.com/log-management-tools/https://www.cprime.com/resources/blog/log-management-elk-and-why-you-should-care/https://sematext.com/guides/elk-stack/شانزده:‌Monitoring tools (e.g. Prometheus)آیا سیستم‌ها همه سالم هستند و کار می‌کنند؟ نمی‌دانم اجازه بده تک تک همه آن‌ها را پینگ بگیرم. یا اصلا پینگ هم دارند، آیا واقعا زنده و عملیاتی هستند؟ بگذار یک درخواست هم با postman بفرستم.  شاید این رویه در یک شرکت کوچک امکان‌پذیر باشد ولی در سازمانی با تعداد کاربران بالا معموال امکان‌پذیر نیست، برای همین نیاز به ابزاری است که به شکل مداوم از فعال و در حال کار بودن سیستم‌ها اطمینان حاصل کند و در صورتی که مشکلی وجود دارد سریعا بتواند گزارش کند. برای مانیتور کردن معمولا موارد زیر مدنظر است: بالا بودن سرورها، کارایی شبکه، بالا بودن کانتینرها، بالا بودن زیرساخت‌های ابری (چه پابلیک و چه پرایوت) و در نهایت بالا بودن خود اپلیکیشن. در این بین ابزارهای زیادی وجود دارند که براساس معیارهای مختلف مانند اسکیلیبل بودن، رابط کاربری، real-time بودن و غیره می‌توانیم از بین آن‌ها انتخاب کنیم. به عنوان مثال prometheus یک ابزار متن‌باز برای آنالیز دیتای سری‌زمانی است که با کمک جعبه‌ابزار داخل خودش امکان مانیتورینگ سرور‌ها را می‌دهد.منابع:https://devopscube.com/best-opensource-monitoring-tools/https://prometheus.io/docs/introduction/overview/ هفده: Static Code Analysis (e.g. SonarQube)یکی از مراحل مفید و مهم در بررسی سورس‌کد یا همان تست جعبه‌ی سفید، اجرای یک ابزار بررسی کد به شکل ایستا روی سورس‌کد است. منظور از static یا همان ایستا این است که در حالتی که کد در حال اجرا نیست و فقط روی سورس کد این بررسی انجام می‌شود مثلا اطلاعی از مصرف مموری یا پردازنده در زمان اجرا نداریم. از مزیت‌های این روش می‌توان به اجرای نسبتا سریع (به نسبت انسان) و خودکار اشاره کرد که امکان اجرای مرتب مثلا شبانه را می‌دهد. همچنین در پیدا کردن برخی مشکلات مثلا SQL injection یا سرریز بافر می‌تواند خوب عمل کند. اما مشکلاتی که دارد این است که بسیاری از مشکلات را نمی‌تواند گزارش دهد مثلا استفاده‌ی نامناسب از امکانات رمزنگاری، کنترل نامناسب یا ناکافی دسترسی، و ... اگرچه این موارد در حال بهبود است ولی همچنان بسیار جای کار دارد. همچنین در طرف مقابل مقدار زیادی false positive وجود دادر که باعث می‌شود اعتماد برنامه‌نویس به این خطاها کاهش یابد. همچنین در مواقعی که نمی‌توانند کد را کامپایل کنند (چرا که مثلا کتاب‌خانه‌های صحیح را پیدا نمی‌کنند) دچار مشکل می‌شوند. سونارکیوب یکی از ابزارهای مطرح این حوزه است که به شکل آنلاین و سریع امکان بررسی سورس‌کد به زبان‌های مختلف را می‌دهد. همچنین مانند بسیاری از ابزارهای لینک دیگر امکان افزوده شدن به IDE‌ها و ادیتورها به عنوان پلاگین را دارد که چرخه‌ی بازخورد برنامه نویسان را سریع تر می‌کند. منابع:https://owasp.org/www-community/controls/Static_Code_Analysishttps://en.wikipedia.org/wiki/SonarQubeهجده: Continuous Deliveryیکی از اهدافی که برای DevOps ذکر می‌شود، کاهش زمان مابین توسعه‌ی نرم‌افزار توسط برنامه‌نویس و استقرار آن است، در واقع جدا از DevOps هم این یک هدف در توسعه‌ی نرم‌افزار است. مثلا در متودولوژی مدرن اجایل نیز گفته می‌شود که «به شکل مستمر ارزش آفرینی کنید». در دنیای واقعی با کمک ابزارهای CI/CD این اتفاق می‌افتد. CI یا همان continues integration بخش اول این روال است که باعث می‌شود همه‌ی کد کنار هم قرار گرفته کامپایل و بیلد و البته تست شود. تست‌ها می‌توانند از یونیت‌تست تا تست‌های پیشرفته تر باشند. در اینجا اما تمرکزمان روی CD (در اینجا مخفف continues delivery) است. CD یک تکرار و هدف است برای از اتوماتیک‌سازی برای افزایش سرعت و کاهش تناوب عرضه‌ی نسخه‌ی جدید کد به مشتری. به نوعی دنباله‌ی روال CI است که کد جدیدی که نوشته شد و از هر نظر بررسی شد، تبدیل به یک artifact قابل deploy شود، در مثال جاوایی یک jar همراه با همه‌ی پیشنیازها ایجاد شود تا به راحتی قابل استقرار باشد. در اینجا خوب است اشاره شود که CD می‌توانند به معنی continues deployment نیز باشد که هدف والاتری است و رواج کمتری دارد. این هدف به این شکل است که علاوه بر تست نرم افزار (در CI) و ساخت artifact قابل دیپلوی (در مرحله‌ی continues deiivery) واقعا نسخه‌ی جدید به دست مشتری هم برسد. این مورد اگرچه جالب به نظر می‌آید اما می‌تواند برای کسب‌وکار ترسناک باشد و برای همین رواج کمتری دارد. همچنین راه‌های بینابینی مثلا استقرار روی یک سرور staging نیز راه حل مناسبی به نظر می‌رسد. منابع:https://medium.com/agile-insights/deliver-value-continuously-a-modern-agile-guiding-principle-712ef9db2225https://www.redhat.com/en/topics/devops/what-is-continuous-deliveryhttps://learn.microsoft.com/en-us/devops/deliver/what-is-continuous-deliveryنوزده: Single Sign on (SSO) &amp; Identity Managementحتما پیش آمده که برای لاگین در یک سایت، ایکون لاگین با گوگل را دیده‌اید  و روی همان زده‌اید، چرا که دیگر نیاز نیست در این سایت هم یک اکانت بسازید و رمز قوی بگذارید و ... حتی خود همین ویرگول با همه‌ی نقص‌هایش (مثل سرعت پایین ادیتور بعد از طولانی شدن مطلب) این امکان را دارد. به شکل فنی، روال کار به این صورت است که وقتی کاربر در یک سرویس مرجع که SSO یا فراهم می‌کند مثل گوگل یا گیتهاب لاگین می‌کند، می‌تواند به سایت‌های مستقل با همان ID لاگین کند بدون اینکه پسور خاصی برای آن سایت جدید داشته باشد. در ساده‌ترین حالت ممکن، می‌توانیم با اشتراک گذاشتن کوکی‌ها در یکسری سایت با دامنه‌ی یکسان،  این قابلیت را عملی کنیم ولی در حالت ایده‌آل نباید سایت‌ها به هم ارتباط خاصی داشته باشند. از مزایای این روش می‌توان به حذف شدن مشکلات پسورد‌ها اشاره کرد (مثلا فراموشی پسورد، لو رفتن دیتابیس پسورد‌ها، یکسان بودن پسورد کاربران بین سایت‌های مختلف) همچنین بهبود تجربه کاربری و صرف زمان کمتر برای کاربر و همچنین افزایش امنیت کلی سایت اشاره کرد. منابع: https://en.wikipedia.org/wiki/Single_sign-onhttps://cloud.google.com/architecture/identity/single-sign-onبیست:‌ Service Mesh در یک معماری سرویس‌گرا یا میکروسرویس، تعداد سرویس داریم که برای عملکرد صحیح نیاز به سرویس‌های دیگر دارند، پس طبیعتا باید هر سرویس با سرویس‌های دیگر (یا تعدادی سرویس دیگر) بتواند ارتباط داشته باشد. service mesh یک ابزار برای کنترل و مدیریت این است که سرویس‌های مختلف چطور دیتا با هم تبادل می‌کنند. در واقع service mesh به شکل یک لایه‌ی زیرساختی است که در کنار برنامه قرار می‌گیرد. این لایه‌ی زیرساختی همچنین می‌تواند مشخص و مستند کند که قسمت‌های مختلف چطور با هم در ارتباط هستند در نتیجه مدیریت و بهینه کردن آن‌ها ساده تر می‌شود. اما چرا اصلا نیاز به چنین چیزی داریم؟ مگر خود هر سرویس نمی‌تواند به شکل مستقیم با سرویس مقصد خود ارباط برقرار کند؟ البته که می‌تواند، در این صورت باید مشخصات سرویس مقصد را در خود سرویسِ سرویس‌گیرنده داشته باشیم تا بتواند به شکل مناسب متصل شده و اطلاعات تبادل کند ولی با زیاد شدن میکروسرویس‌ها و پیچیده شدن ارتباطشان، ترجیح داده می‌شود که از چیزی برای مدیریت این پروسه استفاده شود. برنامه‌های بزرگ ابری معمولا از این راهکار برای مدیریت استفاده می‌کنند تا میکروسرویس‌های مجزا به عنوان یک کل یکپارچه سرویس دهند. نمایی از نحوه مدیریت service mesh منابع: https://www.redhat.com/en/topics/microservices/what-is-a-service-mesh#don%E2%80%99t-microservices-already-do-thishttps://sweetcode.io/service-mesh-service-fabric-service-bus-what-does-mean/</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Sun, 27 Nov 2022 15:48:12 +0330</pubDate>
            </item>
                    <item>
                <title>معرفی qutebrowser: مروگری بر پایه کرومیوم برای دوست‌داران ویم</title>
                <link>https://virgool.io/@rsharifnasab/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-qutebrowser-%D9%85%D8%B1%D9%88%DA%AF%D8%B1%DB%8C-%D8%A8%D8%B1-%D9%BE%D8%A7%DB%8C%D9%87-%DA%A9%D8%B1%D9%88%D9%85%DB%8C%D9%88%D9%85-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AF%D9%88%D8%B3%D8%AA-%D8%AF%D8%A7%D8%B1%D8%A7%D9%86-%D9%88%DB%8C%D9%85-rtyk80g2ofr4</link>
                <description>چند وقت پیش، یکی از دوستانم پیامی کوتاه اما موثر به من داد: «آقا شما که خیلی اهل vim هستی حتما qutebrowser رو یه نگاهی بنداز ، پشیمون نمیشی ?»با یک جست و جو، متوجه شدم که qutebrowser یک مرورگر کامل است که keyهای ویم را دارد. قبلا چند افزونه برای فایرفاکس  نصب کرده بودم که می‌خواستند پشتیبانی از کلیدهای ویم را به مرورگر بیاورند. اما مشکل این بود که در چیزی که وعده می‌دادند خوب عمل نمی‌کردند. فکر می‌کنم ریشه مشکل این باشد که کاربران دوست ندارند به خاطر یک قابلیت مرورگر محبوبشان با همه امکانات، تنظیمات و bookmarkها را رها کنند و سراغ یک مرورگر دیگر بروند، و چه بهتر که با نصب یک افزونه، قابلیت مربوطه را به همان مرورگر اضافه کنند. اما این انگیزه فقط توجیه می‌کند چرا افزونه های مختلفی برای این کار ساخته شده اما جواب این سوال که «چرا در کارشان موفق نیستند»؟ می‌تواند این باشد که افزونه‌های مرورگر کنترل خیلی کمی روی عملکرد مرورگر دارند تا کاربران را با خطر امنیتی کمتری مواجه کنند. در واقع apiهایی که مرورگر به افزونه‌ها می‌دهد کم و ضعیف اند. ساخت یک مرورگر جدیددر اینجا می‌رسیم به ابزارهای قدرتمند تر، اگر یک افزونه برای ما کافی نیست، چرا مرورگر خودمان را نسازیم؟! فلسفه پشت ساخت qutebrowser (مرورگری که در حال معرفی آن هستم) نیز همین است. هیچ کدام از افزونه‌ها (و البته مرورگرهای vim-like کنونی) پاسخگوی نیاز آقای the-compiler نبودند، پس تصمیم گرفت برای خودش مرورگر بنویسد. البته اگر اندکی با سازوکار وب و مرورگرها آشنا باشید می‌دانید که نوشتن یک مرورگر کامل و قابل استفاده‌ی وب، توسط یک شخص با انگیزه‌ی نه‌چندان بزرگی مثل این اصلا امکان پذیر نیست. مرورگرها دارای بخش‌های بسیار متنوعی هستند (کار با شبکه، پارس کردن html، پردازش css، موتور اجرای javascript، تشکیل dom، امنیت اطلاعات کاربر با تکنیک‌هایی مثل sandbox و مخافظت کوکی‌ها و ...)‌ که همگی باید به شکل همگام و البته با پرفورمنس خیلی عالی کار کنند. اما خوشبختانه، جنبش نرم‌افزار آزاد، به ما کمک می‌کند که چرخ را از اول توسعه ندهیم. همه مواردی که گفتم پیاده‌سازی سختی دارند در زیربخش «موتور» مرورگر قرار می‌گیرند. از شانس خوب ما مرورگرهای کنونی مطرح (کروم و فایرفاکس) موتورشان را به عنوان نرم‌افزار آزاد منتشر کرده‌اند و امکان توسعه‌ی مرورگرهای دیگر با موتور آن ها را می‌دهند. نمونه‌ی این مرورگرها brave و microsoft edge هستند که از موتور کرومیوم استفاده می‌کنند. مرورگر qutebrowser ما نیز چرخ را از اول اختراع نکرده، بلکه از QtWebEngine استفاده کرده. امکان QT (که یک کتاب‌خانه‌ی توسعه‌ی برنامه‌های گرافیکی است) برای ساخت مرورگر. خود QT هم البته چرخ را از اول اختراع نکرده بلکه از موتور کرومیوم استفاده می‌کند. هر چند ماه یک بار تغییرات آخرین نسخه‌ی کرومیوم را در QtWebEngine مرج می‌کنند. پس به طور خلاصه مرورگری داریم که از QT استفاده کرده برای گرافیک (در واقع pyqt چون با پایتون است) و موتوری که QT برای توسعه ی مرورگر وب می‌دهد، کرومیوم است اما نه لزوما آخرین نسخه.رابط گرافیکی این برنامه را python3 و کتاب‌خانه‌ی pyqt5 نوشته اند، شاید به نظر بیاید که پایتون کند است و برای برنامه‌‌ای مثل مرورگر مناسب نیست. نکته این است که فقط المان‌های گرافیکی مرورگر (که چندان هم زیاد نیستند!) توسط پایتون مدیریت می‌شود و هسته‌ی آن که کار سخت‌تر را بر عهده دارد با سی‌پلاس‌پلاس است. البته نه به این معنی که هیچ مشکل پرفورمنسی ندارد اما کاملا قابل استفاده است. امکانات ارائه شدهتا اینجا موتور مرورگر را بررسی کردیم، اما با فرض اینکه موتور به شکل مناسب کار می‌کند، این مرورگر چه چیز جدیدی ارائه می‌دهد که بخواهیم با آن کار کنیم؟ در واقع مرورگر ادعا می‌کند که رابط کاربری‌ای شبیه ویم دارد اما مرورگر را چه به تکست‌ادیتور! در واقع بگذارید اصلاح کنم، این مرورگر با الهام از ویم، محیطی را فراهم می‌کند که با کمترین استفاده از موس (بدون استفاده از موس؟) بتوانید کارهایی که با مرورگر خود انجام می‌دادید را انجام دهید. در این مسیر برخی کلید‌های مورد استفاده، کلید های مورد استفاده از ویم است مثلا d برای بستن یک تب یا / برای سرچ یا kj برای اسکرول کردن. اما انگیزه استفاده نکردن از موس چیست؟ مواردی که مطرح می‌شود این است که استفاده از موس باعث می‌شود سرعت شما پایین بیاید چرا که باید مدام دست راستتان را بین کیبورد و موس جا به جا کنید. همچنین استفاده از موس باعث می‌شود که مچ دست شما دچار ناخوشی بشود، حداقل در طولانی مدت. البته این موضوع جای بحث خودش را دارد ولی اینجا جایش نیست! کلید‌های پیشفرضی که ارائه شده برای کارهای اصلی خوب هستند، سازنده می‌گوید که خواسته با dwb که یک مرورگر دیگر با استفاده از کلید های ویم است یکپارچه بماند، شاید خود سازنده از dwb استفاده می‌کرده قبل از اینکه ناامید شود و یک مرورگر جدید بنویسد! این مرورگر تقریبا تمام امکاناتی که انتظار می‌رود را دارد، تب‌های متفاوت دارید که به خوبی پشتیبانی می‌شوند، سرعت قابل قبولی دارد، امکان باز کردن تب‌هایی که دفعه قبل باز بودند را به شما می‌دهد، از موس پشتیبانی می‌کند، بوکمارک و کوکی و جاوااسکریپت و ... دارد و تا حدی هم سریع باز می‌شود.مشکلات این مرورگر چیست؟طبق تستی که داشتم، این مرورگر برای تنها ۸ تب باز (که برای من خیلی کم محسوب می‌شوند!) ۸۰۰ مگابایت رم مصرف کرد، اما موتور آن (qt web engine process) نیز حدود ۱.۲ گیگ رم مصرف کرد که جمع این دو عدد بسیار زیادی می‌شود. سرعت باز شدن صفحه‌ها بین مرورگرها بالاترین نیست اگرچه قابل قبول است. جا به جا شدن بین مد‌های مختلف و شورت‌کات ها مخصوص کیبورد انگلیسی هستند و اگر کیبوردتان فارسی باشد اذیت می‌شوند. امکان ادیت متنی که نوشتید با استفاده از امکانات ادیت متن ویم را ندارید! یعنی اگر در normal mode باشید d باعث بسته شدن تب می‌شود و نمی‌توانید مثلا یک خط را پاک کنید. (یا حداقل من بلد نیستم!)همچنین private window هم اگرچه وجود دارد اما چندان بدیهی نیست. مشکل مهم دیگر که به نظرم اتفاقا تا حدی هم خوب حل شده، این است که کلیک کردن روی یک لینک بدون موس سخت است. قبل اینکه manual مرورگر را کامل بخوانم، با /  متن دکمه را سرچ می‌کردم و سپس اینتر می‌زدم. اگرچه تا حدی کار می کند اما اصلا بهینه نیست! راهی که خود مرورگر پیشنهاد کرده، استفاده از کلید f است. وقتی f را بزنید روی هر لینک از صفحه یک حرف ظاهر می‌شود که اگر آن حرف را وارد کنید روی آن لنیک کلید می‌شود. به طور مثال این یک تصویر پس از فشردن کلید f است که به من لینک های موجود را نشان می‌دهد. اما چندان روش بهینه‌ای نیست. چرا که در یک صفحه شلوغ مثل صفحه‌ی سرچ، وارد کردن f و پیدا کدن لینک مورد نظر و حرف مربوطه و تایپ آن به نظر بیشتر از موس زمان می‌گیرد. هرچند به نظرم این مشکل برای ابزاری که می‌خواهد یک رابط بدون موس برای وب که این روز ها برای صفحات تاچ بهینه شده ارائه دهد وجود دارد و مختص این ابزار نیست.در تصویر بالا به حروف زرد و داخل هم رفتن آن‌ها دقت کنید. در حالی که مثلا می‌شد چند تا از آن‌ها که مربوط به یک لینک واحد هستند را ادغام کرد. جمع بندیدر مجموع ابزار قبل استفاده‌ای است. اگر با صفحات فارسی کار نداشته باشید و بیشتر مشغول گشت و گذار باشید تا تولید محتوا، جوابگو است.اما به عنوان مرورگر اصلی مخصوصا برای فارسی‌زبان ها توصیه نمی‌کنم اما می‌تواند به عنوان مرورگر دومم جایگزین کرومیوم باشد. </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Fri, 24 Dec 2021 17:54:57 +0330</pubDate>
            </item>
                    <item>
                <title>چطور همه‌ی چیزهای زیبا با صفر و یک ساخته می‌شوند؟ قسمت دوم: پردازنده</title>
                <link>https://virgool.io/@rsharifnasab/computer-2-ahdvphtxxta7</link>
                <description>(ویرگول عزیز درفت ۲۰۰۰ کلمه‌ای این مطلب رو پاک کرد، اما تسلیم نشدم و از نو نوشتمش!)در این مطلب چند قسمتی می‌خواهم نشان دهم کامپیوتر چگونه از ۰ و ۱ برای نمایش و پیاده‌سازی همه‌چیز استفاده می‌کند. قسمت اول را اینجا بخوانید.در قسمت قبل کمی در مورد صفر و یک و اینکه لزوما هر صفر و یکی معنی عدد مبنای دو نمی‌دهد خواندیم. همچنین چند قرارداد مختلف برای معنی کردن رشته‌هایی از صفر و یک را دیدیم. مثلا ASCII یک استاندارد است که در آن هر ۸ بیت معنی یک کاراکتر خاص را می‌دهد.همچنین در مورد تفاوت دستور و داده خواندیم. پردازنده یکسری دستور دارد که براساس آن‌ها داده‌های ورودی را به داده‌های خروجی تبدیل می‌کند. در این قسمت در مورد پردازنده بیشتر می‌خوانیم.منطق در صفر و یکدر کامپیوترهای دیجیتال، فقط دو مقدار صفر منطقی و یک منطقی داریم، مثلا ۰ ولت برابر ۰ منطقی و ۵ ولت برابر یک منطقی است.اگر بخواهیم با این دو مقدار، عددی یا حرف الفبایی را نشان دهیم از استانداردهای مخصوص خودش استفاده می‌کنیم مثلا مکمل دو یا IEEE 754 یا ASCII.اما برای اینکه منطق و محاسبات را با این دو مقدار نشان دهیم باید چه کار کنیم؟ ابزاری که در اختیار داریم جبر بول یا boolean algebra است. اگر با جبر بولی آشنا نیستید، ویکی‌پدیای آن را مطالعه کنید. در این مدل از ریاضیات، ۲ مقدار ۰ و ۱ (یا درست و  غلط) را داریم و روی آن‌ها عملیات‌های مختلفی تعریف می‌شود. مثلا and و or و not.برای توضیح این ۳ عملگر، می‌توانیم از اعمال آشنای ریاضی استفاده کنیم، and معادل min است، اگر هردو عملوند ۱ باشند نتیجه ۱ می‌شود وگرنه صفر می‌شود. or هم max است، اگر هردو یا حداقل یکی برابر ۱ باشند نتیجه یک می‌شود وگرنه صفر. عملگر not هم برابر نقیض می‌شود. یعنی نقیض true می‌شود false و نقیض false می‌شود true.در منطق بولی، برای نشان دادن محاسبات از نشانه‌گذاری خاصی استفاده می‌شود.مثلا:این نشانه‌گذاری اگرچه برای منطق و ریاضی بسیار کاربردی و خوب است، اما در کامپیوتر با توجه به نیاز‌های خاص معمولا از یک شیوه دیگر استفاده می‌شود.جدول درستیبیان منطق ریاضی همیشه ساده نیست، مثلا یک تابع ریاضی در ذهنم دارم و به ازای ورودی‌های مختلف می‌خواهم خروجی‌های مختلف داشته باشد و مدام تغییرش دهم. از طرف دیگر برایم مهم نیست از چه ترکیبی از and و or ساخته شده. برای این منظور از جدول درستی یا Truth Table استفاده می‌شود.این یک نمونه ساده از جدول درستی است، به ازای یک تابع دو ورودی و یک خروجی آن را رسم کرده‌ایم. تابع رسم‌شده همان and است. دو ورودی p و q دارد و خروجی p^q (که نماد ریاضی برای همان p and q است) را بررسی کرده‌ایم.همانطور که مشخص است هر ستون نشان دهنده‌ی یک ورودی یا خروجی تابع ماست. هر سطر نیز یک حالت ممکن است مجموعه‌ی حالات ممکن را نشان می‌دهد. دو ورودی داریم، هر کدام نیز دو حالت می‌توانند داشته باشند بنابراین جمعا ۴ حالت داریم برای همین ۴ سطر را در نظر گرفته‌ایم. در هر سطر مقدار‌های ورودی مشخص هستند، مقدار خروجی تابع را نیز مشخص می‌کنیم.به این ترتیب به ازای همه حالات ورودی، مقدار خروجی را داریم پس می‌توان گفت جدول درستی می‌تواند یک تابع بولی را کاملا نشان دهد.اما این روش هم یک عیب بسیار بزرگ دارد، با زیاد شدن تعداد ورودی‌های سیستم، تعداد سطرها به شکل نمایی افزایش پیدا می‌کند. مثلا برای ۳۲ ورودی، دو به توان ۳۲ یا ۴ میلیارد سطر خواهیم داشت. در حالی که برای یک چیپ دیجیتال ۳۲ ورودی اصلا تعداد ورودی‌های زیادی نیست (نگران نباشید، در ادامه ارتباط منطق و چیپ می‌رسیم.)پیاده‌سازی سخت‌افزاری توابع بولیتا اینجا دیدیم که توابع جبری را چطور می‌توان نشان داد، مثلا از جدول درستی استفاده کردیم تا یک تابع جبری ساده را نشان دهیم، اما جبر بولی و جدول درستی همه روی کاغذ و در تئوری هستند، ارتباطشان به دنیای دیجیتال چیست؟دنیای دیجیتال به کمک ترانزیستورها، می‌تواند هر تابع بولی‌ای را پیاده‌سازی کند. پیاده‌سازی یعنی می‌توانیم یک جسم داشته باشیم که چند تا پایه فلزی ورودی و خروجی دارد و طبق جدول درستی داده شده کار کند. مثلا اگر دو تا ۵ ولت در ورودی دهیم، ۵ ولت در خروجی دهد وگرنه ۰ ولت دهد (تابع and)شکل بالا یک نمونه پیاده سازی فیزیکی (البته برای اسباب بازی و نه کار صنعتی) را نشان می‌دهد. این اسباب‌بازی‌ها از اینجا قابل خریداری هستند. (اگر با قیمت ۴۷.۵ دلار راحت هستید، لطفا برای من هم بخرید.)اما از این چیپ ها در این اندازه در صنعت استفاده نمی‌شود، چرا؟ چون به دنبال کوچک کردن چیپ‌های ساخته شده هستیم و چیپ‌هایی که واقعا در پردازنده‌ها استفاده می‌شود چندین میلیارد ترانزیستور دارند، و برای اینکه ساختشان ممکن باشد ترانزیستورها را به ابعادی در حدود چند نانومتر در می‌آورند (این روزها ۴ یا ۷ نانومتر لبه‌ی تکنولوژی است) یعنی در حدود ابعاد اتم!حتی اگر پردازنده‌های لبه‌ی تکنولوژی اپل و اینتل و AMD را هم هدف قرار ندهیم، در یک تکنولوژی به کار رفته در یک موس لیزری هم استفاده از گیت‌هایی با این ابعاد بزرگ میسر نیست! در دنیای واقعی برای تهیه‌ی یک گیت and می‌توانیم یک چیپ بخریم که ۴ یا ۸ تا گیت and داخلش دارد و مثلا ۱۴ پایه دارد. این چیپ‌ها قیمت معقولی دارند و در کشور عزیزمان هم به راحتی قابل خریداری هستند.تفاوت گیت (Gate) و ترانزیستور و چیپدر میانه‌ی پاراگراف بالا صحبت از گیت شد، تفاوتش با یک تابع بولی که می‌توانستیم با ترانزیستور پیاده سازی کنیم چیست؟همانطور که گفتم ترانزیستور کوچک‌ترین جزء سازنده‌ی دیجیتالی است، ما می‌توانیم هر تابعی بولی را با ترانزیستور پیاده‌سازی کنیم، اما پیاده‌سازی با ترانزیستور چندان کار ساده‌ای نیست، مخصوصا برای توابع بزرگ.برای همین با ترانزیستورها، توابع پایه‌ای که بسیار مورد نیاز هست مثلا همان and و or و not را پیاده سازی می‌کنند، سپس با این گیت‌ها باقی توابع را پیاده‌سازی می‌کنند. گیت‌ها معمولا با تعداد کمی ترانزیستور ساخته می‌شود.گیت‌ها برای ساخت چیزهای دنیای دیجیتال کافی هستند، مثلا اثبات می‌شود که با داشتن مجموعه‌ی and و or و not می‌توانیم هر جدول درستی‌ای را پوشش دهیم. حتی با فقط گیت nand یا فقط گیت nor هم می‌توان این کار را کرد. به گیت‌های nand و nor گیت‌های universal می‌گویند.در این تصویر گیت‌های اصلی را به همراه جدول درستی‌شان می‌بینیم.شاید بگویید که اگر به هر زحمتی هست با خود ترانزیستورها بتوانیم توابعمان را پیاده‌سازی کنیم، نتیجه بهینه‌تر (با تعداد ترانزیستور کمتر) باشد، درست است اما این یک مصالحه است! ما به دنبال راحت‌تر کردن طراحی خودمان هم هستیم.در مورد اینکه چطور با خود گیت‌ها می توان هر تابعی را پیاده سازی کرد، می‌توانید از این لینک مطالعه کنید.اما چیپ، یک قطعه ساخته‌شده است که پایه‌های رسانا دارد و براساس ورودی‌هایش، عملیات انجام می‌دهد و خروجی می‌دهد. تعریف خیلی دقیق نیست اما تمرکزم بر قطعه فیزیکی بودنش است. (بیشتر بخوانید.)انجام محاسبات با صفر و یکتا اینجا یاد گرفتیم که توابع جبر بولی را می‌توان به شکل فیزیکی با کمک ترانزیستورها پیاده‌سازی کرد، اما این چه کمکی به ما در دنیای واقعی می‌کند؟ ما در دنیای واقعی می‌خواهیم از کامپیوتر‌ها استفاده کنیم تا برایمان محاسباتمان را انجام دهند، محاسباتی که ما نیاز داریم، نه هرچیزی که زبان کامپیوتر هست!کامپیوتر برای اینکه محاسبات دلخواه ما را انجام دهد، باید محاسباتمان را به زبان کامپیوتر بیان کنیم، تا اینجای بحث یعنی بتوانیم هر عملیاتی که می‌خواهیم انجام دهیم را به زبان کامپیوتر بگوییم، بعد ورودی را به شکل مناسب بدهیم و خروجی را بگیریم. قاعدتا یادتان هست که شکل مناسب برای کامپیوتر یعنی به شکل ۰ و ۱ی باشد که روی ورودی چیپمان اعمال می شود.بیایید با یک مثال جلو برویم، فرض کنیم می‌خواهیم دو عدد یک رقمی را با هم به کمک کامپیوتر جمع کنیم. اما یک رقمی در دنیای کامپیوتر یعنی یک بیتی! به بیان کامپیوتر یعنی می‌خواهیم یک half-adder بسازیم.اسم half adder در مقابل full adder است، half-adder نسخه‌ی ساده شده است که فقط ۲ ورودی دارد، دو تا عدد یک بیتی A و B و دو تا خروجی دارد.خروجی اول S یا Sum است که مجموع را نشان می‌دهد، اما اگر هر دو عدد یک باشند جمعشان در یک بیت sum جا نمی‌شود، بنابراین یک خروجی دیگر به نام C یا همان Carry هم دارد، به نوعی می‌توان گفت ۱۰ بر ۱ است که وارد رقم بعد می‌شود. در اینجا رقم سمتِ چپِ جمعمان را نشان می‌دهد. مثلا اگر هر دو ورودی یک باشند جواب ما می شود ۲ یا 10 در باینری، پس carry برابر ۱ می شود و sum برابر ۰ می شود.سمت چپ تصویر نیز جدول درستی آن را می‌بینید که باز هم به علت اینکه دو ورودی دارد فقط شامل ۴ سطر است.سمت راست تصویر پیاده‌سازی آن را با گیت‌های AND و XOR را نشان می‌دهد.شاید بگویید که جمع کردن دو عدد یک بیتی هم که دردی از ما دوا نمی‌کند، اما قدم به قدم باید دنیایمان را بسازیم. قدم بعدی این است که دو عدد ۳۲ بیتی بدون علامت را جمع کنیم. برای این منظور دو راه داریم، راه اول است که یک جدول درستی با ۶۴ ستون (۲ تا ورودی داریم که هر کدام ۳۲ بیت ورودی دارد) و دو به توان ۶۴ سطر را رسم کنیم که چندان کار ساده‌ای نیست، راه دوم این است که از بلوک‌های کوچک‌تری که ساختیم استفاده کنیم. مثلا به نحوی ۳۲ تا half adder را با هم ترکیب کنیم. البته راه‌های دیگر مثل طراحی کامپیوتر هم هست، چرا که نه.این یک جمع کننده‌ی ۳۲ بیتی است که از ۳۲ تا جمع کننده‌ی full adder (همان FA در شکل) استفاده کرده. تفاوت full adder با half adder در این است که full adder توانایی ترکیب شدن برای ساخت ساختار‌های بزرگ‌تر را دارد، در واقع ۳ ورودی دارد که سومین ورودی carry in است، یعنی اگر جمع قبلی دارای carry بود، انتظار می‌رود carry in این بیت یک شود تا بتوانیم جمع را درست حساب کنیم. (اگر متوجه نشدید اشکالی نداره.)گیت NOT چطوری ۰ ولت رو به ۵ ولت تبدیل می‌کنه؟گفتیم که not یک تابع تک ورودیه و ورودی برعکس می‌کنه، یعنی یک منطقی رو تبدیل به صفر منطقی می‌کنه و صفر منطقی رو تبدیل به یک منطقی می‌کنه.وقتی ما می‌تونیم گیت not رو در دنیای واقعی داشته باشیم یکی یه شی‌ای داریم که 0 ولت رو به عنوان ورودی می‌گیره و تبدیل به ۵ ولت می‌کنه. عجیب نیست؟ پایستگی انرژی نقض نمی‌شه؟واقعیت اینه که این گیت‌ها به عنوان ورودی و خروجی منطقی‌شون ۰ و ۵ ولت می‌گیرن، اما جدا از این داستان، نیاز دارند که به منبع انرژی هم متصل باشند، مثلا همین گیت not یک ورودی و یک خروجی منطقی داره اما یک جفت سیم فاز و نول هم داره و انرژیش از اونجا تامین می‌شه. (همه‌ی گیت‌ها به همین صورت هستند.) اما برای سادگی توی طراحی اون منبع انرژی رو اصلا رسم نمی‌کنیم و فقط به قسمت منطقیش می‌پردازیم.حل مشکل تک کارایی بودندر اینجا یک سخت افزار ساختیم که جمع دو عدد را حساب می‌کرد، اما مشکلی که دارد است است که فقط یک کار را انجام می‌دهد. اگر من  فردا به جای جمع بخواهیم منها کنم باید از اول طراحی کنم و بدهم برایم بسازند. این که صرفه ندارد!برای اینکار چیزی به اسم ALU ساخته می‌شود.واحد محاسبه و منطق یا ALU، یک چیپ است که چندین عملیات را در خودش جا داده است، با دادن ورودی‌ها به عنوان operand1 و operand2 و تنظیم عملکرد (function) می‌تونیم نتایج مختلفی را ببینیم و عملیات‌های مختلفی را برایمان انجام دهد. مثلا ALU بالا، به ازای opcode برابر ۰۰ عملیات جمع را انجام می‌دهد. opcode یک کد باینری است که کارکرد ALU را مشخص می‌کند. با تغییر opcode کارکرد آن هم عوض می‌شود، مثلا تا الان جمع می‌کرد ولی از این به بعد ضرب می‌کند!برای اطلاع از نحوه‌ی کار مثلا عملکرد هر opcode یک alu یا کلا هر چیپی باید Datasheet مخصوص آن را بخوانیم.حل مشکل عملیات‌های ثابتتا اینجا یک پردازشگر (همان واحد منطقی) ساختیم که چند عملیات می‌توانست در خودش داشته باشد و در زمان استفاده هر کدام از عملیات‌ها را که می‌خواستیم انتخاب می‌کردیم.مشکل اینجاست که این عملیات‌ها محدود هستند، اگر فردا یک عملیات دیگر خواستیم باید چیکار کنیم؟ یک ALU جدید بسازیم؟ البته این هم یک راه است اما برای کاربر نهایی آنچنان اهمیتی ندارد.تا اینجا هر محاسبه‌ای که داشتیم را مستقیم به زبان سخت‌افزار بیان می‌کردیم و یک سخت‌افزار می‌گرفتیم که دقیقا آن کار را برای ما انجام می‌داد. این سخت‌افزار دقیقا یک هدف داشت و آن را سریع و بهینه انجام می‌داد. به این حالت ASIC می‌گویند. برای وقتی که کامپیوتر ما فقط و فقط یک هدف دارد (مثلا پردازش گرافیکی کند یا بیت کوین استخراج کند!) مناسب است، چرا که آن کار ارزش دارد که برایش زمان بگذاریم و یک سخت‌افزار بسازیم که دقیقا همان یک کاری که ما می‌خواهیم را انجام دهد و نه هیچ کار دیگر. (البته جزئیات زیادتری در این زمینه وجود دارد که من مسلط نیستم.)اما مساله این است که ما گاهی یک کامپیوتر عام منظوره می‌خواهیم، یک کامپیوتر که هر محاسبه‌ای داشتیم برای ما انجام دهد، حتی برای کارهایی که از قبل برایشان برنامه‌ریزی نشده. در واقع ما یک کامپیوتر قابل برنامه‌ریزی می‌خواهیم.کامپیوتر‌های قابل برنامه‌ریزیکامپیوتری که با آن این متن را می‌خوانید یک کامپیوتر قابل برنامه‌نویسی است، یکسری مهندس سخت افزار آن را طراحی کرده‌اند، سپس یکسری برنامه‌نویس برنامه‌های مورد نیاز را نوشته‌اند و نهایتا برنامه‌ها روی سیستم شما اجرا می‌شود و متن من را می‌خوانید.برای اینکه یک کامپیوتر قابل برنامه‌ریزی بسازیم دو راه وجود دارد. (من فقط دو راه بلدم حقیقتش!)راه اول اینکه یک سخت افزار داشته باشیم (مثلا یک FPGA) که هر موقع ما خواستیم به شکل سخت‌افزار‌های دیگر در بیاید. مثلا وقتی می‌خواهیم جمع انجام دهیم به شکل جمع‌کننده در بیاید، وقتی می‌خواهیم ضرب کنیم به شکل ضرب کننده در بیاید، وقتی می‌خواهیم تلگرام را باز کنیم به شکل پردازنده‌ی مخصوص تلگرام در بیاید! این روش اگرچه هنوز چندان فراگیر نشده اما وجود دارد. توجه داریم که برنامه‌ریزی سخت‌افزار که به شکل برنامه مورد نظر ما عمل کند اندکی زمان‌بر است، همچنین باید سخت‌افزار به اندازه‌ی کل برنامه مقصد مثلا تلگرام، المان پردازشی داشته باشد. (اگر متوجه نشدید مشکلی نیست.)کار دیگری که می‌توانیم انجام دهیم این است که یک کامپیوتر با سخت‌افزار ثابت و غیرقابل تغییر اما کاربردی داشته باشیم مثلا همان  ALUمان یکسری عملیات پایه که برای همه کاربردی است مثل ۴ عمل اصلی و and و or و ... داشته باشد.حالا هر محاسبه‌ای که داریم را بشکنیم به تعداد جمع و ضرب و .. تا توسط این ALU قابل انجام باشد. مثلا محاسبه‌ی توان را تبدیل کنیم به چند عمل ضرب یا ...کامپیوتر‌های امروزی ما از روش دوم استفاده می‌کنند، یعنی یک سخت‌افزار (یک پردازنده) معین داریم که چند کار اصلی را می‌تواند انجام دهد. حالا برنامه‌نویس‌ها برای این کامپیوتر برنامه می‌نویسند که عملیات‌های مدنظر را به کمک توانایی‌های محدودش انجام دهد.اینکه یک کامپیوتر دقیقا باید چه کارهایی را بتواند کند (مثلا همین جمع و ضرب) تا بتوان با آن هر محاسباتی را انجام داد، به بحث‌های «تئوری محاسبات» برمی‌گردد که بحث جذابی است ولی بیشتر از مهندسی، جنبه‌ی علمی دارد.چطور زنجیره‌ی عملیات‌ها را داشته باشیم؟وقتی از ALU استفاده کردیم، فقط یکبار ورودی به آن دادیم و یک خروجی گرفتیم، اما وقتی قرار است برنامه‌نویس برای ما برنامه‌ای بنویسد که چندین عملیات دارد و کامپیوتر‌ آن‌ها را اجرا کند تا نتیجه‌ی دلخواه ما به دست بیاید، چند سوال پیش می‌آید:لیست دستورات را چطور به کامپیوتر بدهیم؟کامپیوتر لیست دستورات را چطوری اجرا می‌کند؟نتیجه‌ی محاسبات میانی کجا قرار می‌گیرند؟به این سوال‌ها جواب خواهیم داد اما نیاز است قبلش چند مفهوم را با هم مرور کنیم.اجزای سازنده‌ی پردازندهدر نوجوانی وقتی می‌پرسیدم پردازنده چطوری کار می‌کند، می‌گفتند پردازنده از ALU و CU تشکیل می‌شود، ALU واحد محاسبات است و CU واحد کنترل، یعنی CU کنترل می‌کند که ALU چه کار انجام دهد. این توضیح اگرچه صحیح است اما به نظرم کمک‌کننده نیست!اگرچه ALU نیاز به کنترل شدن دارد تا متوجه شود کدام عملیات را باید انجام دهد (opcode) اما این تمام ماجرا نیست، خود اینکه چه داده‌ای باید وارد ALU شود و نتیجه‌ش کجا برود هم باید کنترل شوند. پس سوال‌ جدید این است که اصلا داده‌هایی که قرار است با آن‌ها کار شود از کجا می‌آیند؟از این شکل نترسید، اگرچه این شکل واقعا ساده‌شده‌ای از یک پردازنده‌ی ساده‌ است، اما هنوز هم ترسناک است اما نترسید، بلکه با صبر به یادگیری ادامه دهید!به خط‌های سبز، مسیر داده (Datapath) می‌گویند، یعنی داده‌ی ما این مسیر را طی می‌کند و در پردازنده می‌چرخد. مثلا با ALU آشنا هستیم، یک جفت داده می‌گیرد و یک نتیجه می‌دهد که همگی از جنس داده هستند.اما ALU علاوه بر داده‌ها نیاز به یک opcode هم داشت، یعنی غیر از داده باید به او بگوییم چه عملیاتی انتظار داریم انجام دهد، وظیفه‌ی واحد کنترل (Control Unit یا همین CU) است که به ALU بگوید باید چه عملیاتی انجام دهد، یعنی اصطلاحا آن را کنترل کند.در سمت چپ تصویر یک یا دو مموری را می‌بینیم: Instruction Memory و Data Memory که ممکن است واقعا یک حافظه باشند یا دو حافظه. به طور خلاصه منظور این است که یک حافظه داریم که داده در آن قرار می‌گیرد و یک حافظه داریم که دستورات در آن قرار می‌گیرند.در وسط تصویر هم رجیسترفایل را داریم که یک فایل نیست! بلکه آن هم یک حافظه است که تعداد رجیستر دارد (مثلا ۱۶ تا) هر رجیستر می‌تواند یک واحد داده را نگه دارد. (واحد داده یا word size منظور اندازه‌ی معمول داده‌ها در پردازنده است مثلا ۶۴ بیت یا ۳۲ بیت، این اندازه در تعداد بیت‌های ورودی و خروجی ALU و خیلی‌ جاهای دیگر هم تاثیر دارد.)رجیستر‌ها حافظه‌های بسیار پرسرعت اما کم حجمی هستند که وظیفه‌ی نگهداری مقدار‌های میانی محاسبات را برعهده دارند.خطوط آبی خط‌هایی هستند که اجزای مختلف پردازنده را کنترل می‌کنند. مثال خوبش همان opcode است. اما خود واحد کنترل از کجا می‌داند که چطوری باید پردازنده را کنترل کند؟ واحد کنترل از روی دستوری که به پردازنده می‌دهیم متوجه می‌شود که دقیقا چه عملیاتی باید انجام شود. نمونه‌ی یک دستور برای پردازنده این است:add R0, R1, R2این دستور با دستورهای رایج در زبان‌های برنامه‌نویسی فرق دارد، در واقع این دستور اسمبلی است. (جلوتر بیشتر آشنا می‌شویم.)بر اساس این دستور که اینجا add است، واحد کنترل متوجه می‌شود که باید opcode مربوط به ALU را چطور تنظیم کند. اما همچنین می‌فهمد که مبدا و مقصد داده کجاست. R0 تا R15 رجیسترهای داخل رجیسترفایل هستند، از روی این دستور متوجه می‌شویم که باید دو مقدار R1 و R2 خوانده شوند (ورودی‌ها) و نتیجه‌ی جمعشان در رجیستر R0 نوشته شود. این اطلاعات باعث می‌شود که control unit به register file دستور دهد که دو داده از دو رجیستر گفته شده را لود کند و سپس نتیجه‌ای که به آن داده می‌شود را در کجا ذخیره کند.رجیسترفایلیک حافظه سریع است که تعداد بسیار کمی خانه دارد، هر خانه یک رجیستر نام دارد و مثلا از R0 تا R15 نام‌گذاری شده‌اند. البته نام‌گذاری‌های دیگر مثل ax, bx, cx, dx هم ممکن است و بستگی به معماری پردازنده دارد.هدف این حافظه نگهداری مقدارهای میانی عملیات‌ها در پردازنده است، بنابراین برخلاف حافظه‌های دیگر داخل خود پردازنده تعبیه شده و پردازنده برای هر دستور به راحتی می‌تواند به آن دسترسی داشته باشد و از آن بخواند و در آن بنویسد.حافظه‌ی دادهحافظه‌ی داده یا Data memory حافظه‌ای است که داده‌های مورد نیاز برای کارهای کاربر در آن قرار دارد. این داده‌ها برای اینکه توسط پردازنده مورد استفاده قرار بگیرند باید یکبار از حافظه داخل یک رجیستر از رجیسترفایل لود شوند و در مرحله‌ی بعدی مورد استفاده قرار بگیرند، بنابراین گاهی از اوقات پردازنده مجبور است به حافظه دستور دهد که یک داده را از حافظه لود می‌کند.این حافظه از رجیستر و از خود پردازنده کندتر است بنابراین وقتی پردازنده گذرش به این حافظه می‌افتد باید چند مدتی معطل شود. برای این منظور برنامه‌نویسان سعی می‌کنند کمترین نیاز به این حافظه در برنامه وجود داشته باشد اما همیشه این امر ممکن نیست.پس از انجام محاسبات هم باید نتیجه‌ی محاسبات داخل حافظه‌ی داده نوشته شود.هر خانه از حافظه، بر خلاف رجیسترفایل که یک اسم داشت، یک آدرس دارد. آدرس هر خانه از حافظه یک عدد ۳۲ بیتی یا ۶۴ بیتی است. این عدد همان چیزی است که «پردازنده جدید من ۶۴ بیتی است» را مشخص می‌کند. امروزه اکثر کامپیوترهای خانگی آدرس‌های ۶۴ بیتی دارند چرا که یک سیستم ۳۲ بیتی، با آدرس ۳۲ بیتی فقط ۲ به توان ۳۲ خانه‌ی مختلف یعنی ۴ میلیارد آدرس مختلف را می‌تواند آدرس دهد: یعنی ۴ گیگابایت.حافظه‌ی دستوراتدستوراتی که برنامه‌نویس می‌نویسد، باید به نحوی به دست پردازنده برسد تا بتواند آن‌ها را اجرا کند، حافظه‌ی دستورات برای همین کار است. دستورات داخل حافظه به ترتیب اجرا می‌شوند، برای اینکه کامپیوتر بداند در لحظه باید کدام دستور را اجرا کند، یک شمارنده دارد (Program counter) که بعد از اجرای یک دستور مقدارش یکی زیاد می‌شود، یعنی به دستور بعدی می‌رویم. همچنین در مواقعی مثل اجرای یک حلقه ممکن است Program counter چند واحد به جلو یا عقب هم برود.مموریحافظه‌ی اصلی یا مموری یا حافظه‌ی موقت یا Random Access Memory یا RAM همگی به یک چیز اشاره دارند، جایی که داده‌هایی که در حال کار با آن‌ها هستیم را در آن بریزیم.عبارت رندوم-اکسس که قسمت مهمی از اسم است، به فارسی ترجمه می‌شود:‌«دسترسی تصادفی». اما دسترسی تصادفی یعنی چی؟ مگر در کامپیوتر کارها شانسی انجام می‌شود؟ در اینجا تصادفی به معنی شانسی نیست، بلکه به این معنی است که هر زمان بخواهیم می‌توانیم از قسمت از داده در حافظه را بخوانیم. دسترسی غیر تصادفی مثلا یک نوار کاست که فقط به داده در جایی که هستیم دسترسی داریم.در کامپیوترهای امروز حافظه‌ی داده و دستور هردو در یک حافظه بیرون از پردازنده قرار دارند. این حافظه از حافظه کندتر است، یعنی مثلا پردازنده ممکن است ۱۰۰ عملیات انجام دهد اما حافظه فقط یک عملیات خواندن انجام داده باشد.البته همیشه دقیقا یک نیست، مثلا گاهی برای افزایش کارایی دو مموری هم‌اندازه و هم‌فرکانس استفاده می‌کنند تا در آن واحد خواندن/نوشتن دو آدرس ممکن باشد.تصویر بالا یک مموری را به تنهایی نشان می‌دهد که از قسمت رسانا داخل مادربورد قرار می‌گیرد.تصویر پایین دو ماژول رم که داخل شیار مخصوص روی مادربورد قرار گرفته‌اند را نشان می‌دهد.حالا می‌توانیم به راحتی پاسخ سوالات قبلیمان را بدهیم:لیست دستورات را چطور به کامپیوتر بدهیم؟لیست دستورات را باید به شکلی خاص در حافظه‌ی دستورات بنویسیم. همچنین باید program counter که داخل خود پردازنده است) به اولین دستور اشاره کند یعنی آدرس آن را داشته باشد. سپس پردازنده دستور به دستور اجرا می‌کند و قبل از اجرای هر دستور نیز مقدار program counter را یکی زیاد می‌کند.اینکه چطور برنامه‌ای قابل فهم برای پردازنده بنویسیم را جلوتر بررسی میکنیم.کامپیوتر لیست دستورات را چطوری اجرا می‌کند؟پردازنده دستوری که در نقطه‌ی program counter وجود دارد را از حافظه‌ی دستورات می‌خواند (fetch)، سپس آن را بررسی می‌کند تا بداند کدام سیگنال‌های کنترلی را باید مقداردهی کند (decode)، سپس در مرحله‌ی بعد واقعا دستور را اجرا می‌کند (execute). در همین زمان program counter هم یکی (در واقع ۴ تا، بعدا می‌خوانیم چرا) زیاد می‌شود. (بیشتر بخوانید.)در سایکل بعدی، یعنی چند نانوثانیه بعد، پردازنده دستور واقع در program counter بعدی را اجرا می‌کند.گاهی اوقات نیز program counter به جای دستور بعدی چند تا جلو یا عقب می‌پرد که باز هم تفاوتی نمی‌کند.نتیجه‌ی محاسبات میانی کجا قرار می‌گیرند؟نتیجه هر محاسبه باید در یک رجیستر از رجیسترفایل قرار بگیرد، اما برنامه‌نویس می‌تواند بعد از خود محاسبه، نتیجه را در حافظه اصلی نیز نگهداری کند. معمولا روال به این صورت است که تا جای ممکن نتیجه محاسبات میانی در رجیسترفایل حفظ می‌شود، اگر داده‌ای برای کاربر مهم است یا نتیجه نهایی است یا تعداد رجیسترها جوابگوی کار نیست، داده در حافظه‌ی اصلی نوشته می‌شود و از آن رجیستر استفاده دیگری می‌شود.معماری پردازنده چیست؟شاید بگویید تصویر ترسناکی که بالاتر از نحوه کار پردازنده دیدم، معماری پردازنده حساب می‌شود اما در کمال تعجب نه! آن تصویر ریزمعماری پردازنده است، ریزمعماری چیزی است که برای هر پردازنده مثلا intel core I7 6700K مهندسان طراحی می‌کنند، ریزمعماری سرعت پردازنده و مصرف انرژی و غیره را مشخص می‌کند. درواقع به ریزمعماری می‌توانیم بگوییم «پیاده‌سازی»اما پیاده‌سازی چی؟ چه قواعد و قوانین یا استانداردی را پیاده سازی می‌کنند؟ امروزه ساختن پردازنده اینطوری نیست که یک تیم بیاید برای خودش با هر قاعده‌ای که خواست یک قطعه تولید کند که پردازش کند، شاید اوایل دوره کامپیوتر بود، اما الان پردازنده باید طبق یکسری اصول کار کند، به این اصول معماری پردازنده می‌گویند. هدف ریزمعماری هم پیاده‌سازی سخت‌افزاری آن معماری است، مشخص است که با تغییر معماری باید ریزمعماری متفاوتی را نیز طراحی کنیم.معماری پردازنده در واقع دید استفاده‌کننده از پردازنده است، یعنی کاربر و برنامه‌نویس که می‌خواهند با پردازنده کار کنند مثلا برایش برنامه بنویسند، باید از پردازنده اطلاعات داشته باشند، مثلا اینکه چه رجیسترهایی دارد و کلا چند رجیستر دارد، چه عملیات‌هایی را پشتیبانی می‌کند و ... اما ریزمعماری که جزئیات پیاده‌سازی است معمولا توسط شرکت‌های بزرگ به صورت عمومی منتشر نمی‌شود و به نوعی اسرار شرکت است.همچنین یکی از مهم‌ترین چیزهایی که در معماری مشخص می‌شود، دستورات پشتیبانی شده‌ی آن پردازنده است. مثلا کد add R0, R1, R2 که در بالا دیدیم یک کد استاندارد در ARM است.از معماری‌های معروف می‌توان به MIPS و ARM و x86 اشاره کرد.مجموعه‌ی دستوراتهمانطور که گفته شد هر معماری، علاوه بر مشخصات پردازنده، زبان آن پردازنده که همان دستورات مورد پشتیبانی است را نیز مشخص می‌کند. به این دستورات زبان ماشین می‌گویند. (مطالعه بیشتر)دستورات می‌توانند به دو شکل نمایش داده شوند، نمایش اول که تا کنون با آن آشنا شدیم، شکل اسمبلی است، به تصویر زیر نگاه کنید.در تصویر بالا، کلمات انگلیسی به چشم می‌خورد که با اعداد (به شکل #1 و #2) و رجیسترها (به شکل R4 و R5) کار کرده است. هرچند دنبال کردن این کد سخت است و نیاز به کامنت دارد اما نمایش نوشتاری دارد و بیشتر برای کاربر و برنامه‌نویس مناسب است تا کامپیوتر.در مقابلِ اسمبلی یک نگاه دیگر به دستورات هم داریم که به آن زبان ماشین می‌گویند. زبان ماشین ترجمه‌ی نعل به نعل اسمبلی است برای اینکه توسط کامپیوتر قابل فهم باشد. این تبدیل به کمک اسمبلرها انجام می‌شود.پس اسمبلر کد به زبان اسمبلی را می‌گیرد و کد زبان ماشین تولید می‌کند. اسمبلر برخلاف کامپایلر کار پیچیده‌ای نمی‌کند چرا که کامپایلر برنامه‌ی سطح بالا مثل سی پلاس پلاس یا گولنگ را می‌گیرد و تبدیل به کد ماشین می‌کند اما اسمبلر همان برنامه زبان ماشین که نمایش اسمبلی دارد را می‌گیرد و قرار نیست کار محاسباتی خاصی انجام دهد.اسمبلر برای تبدیل اسمبلی به زبان ماشین، از اطلاعاتی که داخل معماری آمده استفاده می‌کند. در معماری دقیقا مشخص است که این دستور اسمبلی به چه ترکیبی از صفر و یک تبدیل می‌شود.قبلتر که اسمبلرها وجود نداشتند، برنامه‌نویس‌ها مجبور بودند خودشان دست به کار شوند و با زبان ماشین برنامه بنویسند، یعنی باید معماری را با دقت بررسی می‌کردند. برای وارد کردن صفر و یک‌ها به کامپیوتر از کارت پانچ استفاده می‌شد!برنامه‌نویس چطوری برنامه می‌نویسد؟با فرض اینکه اسمبلر و کامپایلر وجود نداشته باشد، باید برنامه‌نویس رشته‌ای از صفر و یک بنویسد و به عنوان برنامه به کامپیوتر تحویل دهد. کامپیوتر بر اساس معماری مشخصش، این رشته را تحلیل می‌کند و کارهای لازم را انجام می‌دهد. از آنجا که نوشتن برنامه به این روش بسیار سخت است و برنامه‌های طولانی هم مشکل‌زا هستند، برنامه‌نویسان ترجیح می‌دادند پردازنده دستورات پیچیده‌ای را پشتیبانی کند و با یک دستور بتوانند بخش زیادی از کار را جلو ببرند.سپس اسمبلرها به وجود آمد، حالا برنامه‌نویس‌ها برنامه را به شکل متنی و با زبان اسمبلی می‌نوشتند و سپس اسمبلر را اجرا می‌کردند تا کد زبان ماشین را تولید کند. کد زبان ماشین را به عنوان برنامه به کاربر می‌دادند و کاربر آن را اجرا می‌کرد.اما اسمبلی هم چند اشکال داشت، اشکال اول اینکه کدهای ساده هم در آن پیچیده بودند و برنامه‌نویسی بازهم سخت بود، اشکال دوم اینکه اگر می‌خواستیم از یک معماری به دیگری برویم باید برنامه را از اول می‌نوشتیم! برای همین زبان های برنامه‌نویسی مثل fortran و Cobol به وجود آمدند. این زبان‌ها از اسمبلی سطح بالاتر بودند و به کمک کامپایلر به زبان ماشین کامپایل می‌شدند. کامپایلر کارش این است که کد به زبان برنامه‌نویسی (امروزه مثل سی پلاس پلاس و گولنگ و راسط و ...) را بگیرد و کد زبان ماشین تولید کند.انواع معماری‌هامعماری‌های پردازنده‌ها به دو دسته RISC و CISC تقسیم می‌شوند.به شکل ساده RISC معماری کاهش‌یافته است، دستورات هر کدام کار کمی انجام می‌دهند و برنامه‌نویس مجبور است برای یک کار ساده چندین دستور بنویسد. در عوض پردازنده کوچک می‌شوددر مقابل CISC معماری پیچیده‌تر با دستورات غنی است، یعنی یک دستور به ظاهر ساده ممکن است مقدار زیادی کار انجام دهد. اگر پردازنده CISC باشد برنامه‌نویس می‌تواند با تعداد دستورات کمتر کار بیشتری انجام دهد و به نوعی پردازنده با داشتن دستورهای مختلف جای خالی کامپایلر را تا حدی پر می‌کند و کار برنامه‌نویس را کمی آسان می‌کند.همانطور که متوجه شده‌اید در ابتدا کامپیوترها را CISC می‌ساختند تا کار برنامه‌نویس‌ها راحت‌تر باشد، معماری x86 یادگار همان دوره است.اما با گذشت زمان، دیگر کسی به صورت مستقیم اسمبلی یا زبان ماشین نمی‌نوشت (یا خیلی به ندرت این اتفاق می‌افتاد) بنابراین دلیلی نداشت که پردازنده‌ها دستورات ترکیبی و پیچیده را پشتیبانی کنند، در مقابل تمرکز بر مصرف انرژی پایین‌تر و هزینه پایین‌تر ساخت اهمیت یافت چرا که کامپیوترها همه‌گیر شده بودند، پس معماری RISC رواج یافت. معماری ARM نیز شاخص‌ترین معماری از دسته‌ی RISC است.مزایای RISCاستفاده از یک معماری ساده‌تر مزیت‌های زیادی دارد. در RISC لزوما تعداد دستورات کمتری وجود ندارد اما دستوراتی که وجود دارند پیچیدگی کمی دارند و انجام این دستورها برای پردازنده سخت نیست.با توجه به نبودن دستورات پیچیده، پیاده‌سازی کلی پردازنده راحت‌تر می‌شود و با تعداد ترانزیستورهای کمتر می‌توان دستورات موجود در RISC را پشتیبانی کرد.تعداد ترانزیستور کمتر به معنی مصرف انرژی کمتر و مساحت کمتر چیپ و دمای کمتر است برای همین استفاده از RISC مثلا arm در موبایل‌ها و تبلت‌ها اولویت دارد. حتی اخیرا اپل با آوردن پردازنده M1 به لپتاپ‌هایش عمر باتری‌هایش را زیاد کرده. (امیدوارم وقتی این مطلب را می‌خوانید ARM فراگیرتر شده باشد.)مزایای x86اینجا به جای مزایای CISC در مورد مزایای x86 می‌نویسم چرا که به نظرم معماری CISC قالب x86 است.کلا در RISC دستورات توانمندی داریم، اگرچه پیاده‌سازی این دستورات از دید پردازنده سخت است اما به کمک این دستورات توانمند برنامه‌نویس می تواند آن‌چه در ذهنش وجود دارد را راحت‌تر به زبان ماشین منتقل کند.مزیت مهم x86 ارزان بودن آن است. درست است که طراحی و پیاده‌سازی یک پردازنده RISC آسان‌تر است، اما به دلیل سال‌ها فراگیر بودن x86 (حدود ۴۰ سال!) هزینه‌های ساختش کاهش یافته.امروزه کدام معماری بهتر است؟امروزه کسی به CISC احتیاجی ندارد چون کامپایلرها قرار است برنامه‌ی زبان ماشین تولید کنند و آن‌ها برایشان آنقدر سخت نیست!اما هنوز پردازنده‌های کامپیوترهای شخصی با معماری x86  ساخته می‌شوند. این امر دلایل متفاوتی دارد مثلا اینکه شرکت‌های بزرگ سال‌ها روی بهتر کردن پردازنده‌های x86 شان زمان صرف کرده‌اند. اگرچه در تئوری با معماری arm هم می‌توان به همان کارایی و حتی بهتر رسید. در مورد قیمت ارزان هم صحبت شد.در طولانی مدت رفتن به سمت arm برای مصرف‌کننده‌ها بهتر است چون می‌توانیم کامپیوترها و لپتاپ‌های خنک‌تر با مصرف انرژی/باتری کمتری داشته باشیم. اما اگر قرار باشد یک‌شبه همه به arm مجهز شویم چند مشکل پیش می‌آید:قطعات جانبی مثل بورد اصلی که پردازنده از آن استفاده کند باید طراحی شود.برخی برنامه‌ها ممکن است روی arm کار نکنند. مثلا اگر از دستورات اسمبلی x86 استفاده کرده باشند.تولید میلیارد‌ها چیپ در یک شب ممکن نیست! باید قدم به قدم این اتفاق بیفتد.جدا از کامپیوترهای شخصی، انتخاب ARM در لحظه‌ی شروع برای گوشی‌های هوشمند انتخاب بسیار خوبی بود و باعث شد بتوانیم ساعت‌ها از گوشی و تبلتمان روی باتری و بدون نیاز به فن استفاده کنیم.کَش (cache)در بررسی ساختار کامپیوتر، از پردازنده و مموری صحبت کردم، اما یک جزئیاتی که به آن اشاره نشد کَش است. کَش یک حافظه‌ی سریع داخل پردازنده‌است اما حجم کمتری دارد و رجیسترها سریع نیست. این حافظه در واقع بین پردازنده و مموری قرار می‌گیرد. کار کش افزایش سرعت دسترسی به حافظه‌ی اصلی است. چرا که بدون وجود کش اگر بخواهیم زیاد از حافظه استفاده کنیم اجرای برنامه بسیار کند می‌شود اما پردازنده خانه‌های زیاد استفاده‌شده‌ی حافظه را در کش قرار می‌دهد و به این شکل سرعت کار بسیار بالا می‌رود.کش بر خلاف رجیسترها و حافظه‌ی اصلی تحت کنترل برنامه‌نویس نیست، بلکه خود پردازنده با هوشمندی(!) آن را مدیریت می‌کند. بنابراین ممکن است اصلا دو پردازنده‌ با یک معماری مشخص یکیشان کش داشته باشد و یکی نداشته باشد. البته که کاراییشان بسیار متفاوت می‌شود اما به لحاظ برنامه‌نویسی تفاوتی ایجاد نمی‌شود.پردازنده نتیجه محاسبات را چطور نمایش دهد؟تا اینجا گفتیم که پردازنده اطلاعات را از مموری بخواند و نتیجه را نیز در مموری بنویسد اما چه فایده‌ای دارد؟ کاربر باید چطوری این اطلاعات را ببیند؟پاسخ این سوالات خارج از خود پردازنده است و در قسمت دیگری بررسی خواهیم کرد.توجه: در بسیاری از جاها ساده‌سازی انجام شد، این ساده‌سازی عمدی است و به دو دلیل است: ساده شدن مطلب و ناآشنایی خودم. در صورتی که فکر می‌کنید قسمتی را می‌شد کامل‌تر توضیح داد می‌توانید کامنت بگذارید تا به انتهای مطلب اضافه کنم.</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 23 Sep 2021 09:59:17 +0330</pubDate>
            </item>
                    <item>
                <title>درس‌هایی از سیستم‌عامل برای زندگی روزمره: ارسال پیام</title>
                <link>https://virgool.io/javacup/%D8%AF%D8%B1%D8%B3-%D9%87%D8%A7%DB%8C%DB%8C-%D8%A7%D8%B2-%DA%A9%D8%A7%D9%85%D9%BE%DB%8C%D9%88%D8%AA%D8%B1-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B2%D9%86%D8%AF%DA%AF%DB%8C-%D8%B1%D9%88%D8%B2%D9%85%D8%B1%D9%87-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D9%BE%DB%8C%D8%A7%D9%85-i8aafhcffhc7</link>
                <description>همانطور که از قسمت قبل سری «درس‌هایی از سیستم‌عامل» یادتان هست، در این سری کاربردهای مفاهیمی که در سیستم‌عامل‌ها وجود دارد را در زندگی روزمره بررسی می‌کنیم. در قسمت قبل الگوریتم‌های زمان‌بندی را بررسی کردیم و یاد گرفتیم چه روش‌هایی برای مدیریت زمانمان داریم و چگونه می‌توانیم با الهام از سیستم‌عامل آن‌ها، از زمانمان بهتر استفاده کنیم، مثلا از روش‌های مرسوم زمان‌بندی و اولویت‌بندی استفاده کنیم یا از شیوه‌های پیچیده‌تر مثلا چند صف استفاده کنیم.در این قسمت می‌خواهم منطق‌های پشت ارسال پیام و ارتباط را بررسی کنیم. (از ارسال پیام منظورم همان message passing است.)در سیستم‌عامل چه اتفاقی می‌افتدچرا در سیستم‌عامل نیاز به ارسال و دریافت پیام وجود دارد؟ اصلا مگر چند جزء متفاوت هستند که بخواهند با هم ارتباط برقرار کنند؟ بله در سیستم‌عامل چند جزء متفاوت وجود دارد. هر برنامه‌ای که در حال اجرا می‌بینیم خودش یک (یا بیشتر) پروسس است. یک‌سری پروسس هم  مستقل از برنامه‌های در حال اجرا در پس‌زمینه مشغول سرویس‌دهی هستند. هر پروسس همچنین می‌تواند قسمت‌های هم‌روند متفاوتی داشته باشد، مثلا threadها در جاوا یا گوروتین‌ها. این قسمت‌های هم‌روند به شکل طبیعی بدون ترتیب خاصی و به شکل مستقل اجرا می‌‌شوند اما اگر این‌ها (ترد‌ها و پروسس‌ها) بخواهند برای یک هدف معین فعالیت کنند، باید با هم در ارتباط باشند. مثلا همین مرورگری که این متن را در آن می‌خوانید چند پخش مختلف دارد که برای یک هدف فعالیت می‌کنند. در واقع حداقل یک بخش برای تعامل‌ها با کاربر دارد مثلا فرمان اسکرول را اعمال می‌کند اما بخشی که صفحه را از سرور دریافت می‌کند با بخش اول به شکل هم‌روند و برای یک هدف خاص فعالیت می‌کنند.از کجا فهمیدم این بخش‌ها جدا هستند؟ اگر مرورگر فقط یک بخش پردازشی داشته‌باشد، زمانی که مشغول دریافت اطلاعات از سرور باشد، دیگر به دستورات شما گوش نمی‌کند و اصطلاحا فریز می‌شود اما در واقعیت (با فرض اینکه پیاده‌سازی‌ صحیح و بدون باگی دارد!) اینگونه نیست. شما در حالی یک صفحه دارد لود می‌شود می‌توانید با بخش‌های مختلف رابط کاربری کار کنید و مشکلی هم نباشد یعنی یک بخش مسئول دریافت اطلاعات از سرور است و بخش دیگر مشغول پاسحگویی به فرمان‌های شماست.این بخش‌های جدا احتیاج به ارسال پیام برای همکاری با هم دارند. مثلا اینکه شما روی یک لینک کلیک می‌کنید، بخش تعامل با کاربر باید به بخش دریافت از سرور بگوید که این صفحه را لود کن (و سپس به قسمت رندر کردن خبر بده و .. )(بر حرفه‌ای‌ترها مشخص است که اجزای مرورگر به همین ۲ قسمت محدود نمی‌شود و چندین بخش مختلف وجود دارد ولی این مثال صرفا برای تقریب به ذهن بود.)انواع ارسال پیامدر پاراگراف‌های بالا صحبت از ترد و پروسس به عنوان پخش‌های مختلف پردازشی شد، اما به فرق آن‌ها اشاره نکردم. ترد‌ها در واقع پخش‌های موازی داخل یک پروسس هستند و می‌توان گفت یک پروسس از یک یا چند ترد تشکیل می‌شود. ترد‌ها از نظر سیستم‌عامل یک برنامه محسوب می‌شوند و یک فضای آدرس دارند، یعنی همگی یک حافظه‌ی مشترک داشته‌باشند و اگر آدرسی تخصیص می‌یابد همگی هم می‌توانند هم بخوانند و هم بنویسند. (البته این بستگی به سیاست‌های زبان هم دارد ولی به طور کلی مثلا در جاوا یا سی می‌گویم.) اما فرق پروسس‌ها این است که از نظر سیستم‌عامل دو برنامه جدا هستند (هرچند بخواهند برای یک هدف معین کار کنند) بنابراین سیستم‌عامل اجازه نمی‌دهد به حافظه‌ی هم دسترسی داشته‌باشند و هر کدام فضای آدرس خودش را دارد. فضای آدرس جدا به این معنی است که حتی اگر آدرسی که در برنامه الف، به یک متغیر اشاره دارد را خودمان دستی به برنامه‌ی ب بدهیم، نمی‌تواند به این متغیر دسترسی داشته‌باشد چرا که این آدرس در فضای برنامه‌ی ب معنی ندارد یا معنی متفاوتی دارد. می‌توانید در مورد حافظه مجازی بخوانید. حالا هم ترد‌ها و هم پروسس‌ها نیاز به پیام‌رسانی دارند. تردها که اصلا مربوط به یک پروسس هستند و تحت فضای آدرس یک برنامه اجرا شده‌اند و به یک مموری واحد دسترسی دارند، بنابراین دغدغه‌ی پیام‌رسانی آن‌ها دغدغه سیستم‌عامل نیست، مثل اینکه دو نفر در یک خانه بخواهند زندگی کنند، دیگر دولت/پست درباره پیام‌رسانی آن‌ها کاری انجام نمی‌دهد. برای پیام‌رسانی بین دو ترد هم راه‌های متفاوتی در زبان‌های متفاوت تعبیه شده‌است، مثلا می‌توان یک متغیر (یا همان مقداری حافظه) را به اشتراک گذاشت و با mutex از آن محافظت کرد یا چیزی مثل صف بین آن‌ها برقرار کنیم (مشابه چنل‌ها در گولنگ). حالت صف هرچند در ظاهر اینطور نباشد حالت خاصی از حالت اول است، یعنی مموری یک مموری است حتی اگر قرار باشد به شکل یک صف به آن نگاه کنیم. چنل‌های گو هم همینطوری پیاده شده‌اند. اما پیام‌رسانی بین پروسس‌های متفاوت به سادگی خواندن و نوشتن و مدیریت یک حافظه نیست، چون اصلا حافظه مشترکی ندارند، بنابراین باید سیستم‌عامل راهکاری برای این موضوع ارائه دهد.راه‌های ارسال پیام بین پروسس‌ها در سیستم‌عاملاول بگذارید قضیه را روشن کنم، سیستم‌عامل پیام‌رسان ندارد که بگوییم خب اگر خواستی پیام بدهی به آی‌دی مقصد پیام بده! اما راه حلی که داریم چیزی در همین حدود است!راه اول، حافظه مشترک (shared memory)فرض کنید یک برگه دارید و دو قلم متفاوت، یکی دست شما و یکی دست دوست شما. به شما گفته می‌شود که برای برقراری ارتباط با دوستتان از این برگه استفاده کنید. هرجا از آن که دوست داشتید بنویسید و دوستتان هم هر کجا دلش خواست می‌نویسد. برقرار ارتباط کار ساده‌ای نیست چرا که هر مرتبه باید کل صفحه را بررسی کنید و ببینید چه قسمت‌های جدیدی به آن اضافه شده. اصلا هر مرتبه یعنی چه؟ هر چند وقت یکبار باید چک کنید؟ اگر دوست شما هم همزمان مشغول نوشتن بود چی؟راه دوم، صف ارسال پیام (message queue)ارسال و دریافت پیام می‌تواند در قالب بسته‌های اطلاعاتی انجام شود، به پست فکر کنید، دوست شما چیزی برای شما پست می‌کند و بعد به کارهای خودش می‌رسد، مدت زمانی بعد بسته به دست شما می‌رسد بدون اینکه با خود دوستتان کاری داشته باشید یا اینکه نیاز باشد در یک خانه زندگی کنید. بعد شما هم می‌توانید برای دوستتان یک بسته بفرستید و دوستتان هرموقع دلش خواست صندوق ورودی‌اش را چک کند.این شیوه نه تنها در پست بلکه در پیام‌رسان‌ها و شبکه‌های اجتماعی انجام می‌شود و بسیار در زندگی واقعی پرکاربرد است، فقط محدودیتی که وجود دارد این است که اگر شما هنوز پیام قبلی دوستان را باز نکرده‌اید آیا اون باز هم می‌تواند پیام بفرستید؟ اگر باز هم باز نکردید چی؟ صندوق ورودی شما چند تا باید جا داشته باشد؟ اگر آن هم پر شود پیام ها دور ریخته شود یا دوستتان بابت زیاد پیام فرستادن دستگیر شود (مثلا دیگر نتواند به شما پیام بفرستد یا به دید کامپیوتر پروسس بلاک/ترمینیت شود)شاید بگویید این هم یک حالت خاص از بالایی است، بلکه هست اما بسیار محدود تر، یعنی شما فقط می‌ توانید چیزی داخل صف بنویسید اما اینکه کجای حافظه‌ی مشترک (بافر) نوشته شود به عهده پست‌چی یا همان سیستم‌عامل است.راه سوم: جویبار داده اگر کاربر لینوکس باشید حتما از pipe استفاده کرده‌اید، مثلا cat a.txt | grep salam این خط یعنی محتوای a.txt  را چاپ کن (دستور cat) و خروجی آن را به ورودی grep لوله‌کشی کن، در این حالت هرخطی که از stdout دستور cat خارج شود، وارد stdin دستور grep می‌شود. با این روش حتی برنامه‌هایی که خودشان برای همکاری طراحی نشده‌اند ولی از ورودی و خروجی استاندارد استفاده می‌کنند را می‌توانید به هم متصل کنید تا تحت یک هدف واحد کار کنند، در واقع به عنوان کاربر برنامه‌ها را متحد کنید نه به عنوان برنامه‌نویس. البته این حالت باز هم حالت خاص از حافظه‌ی مشترک است اما به شکلی عملی‌تر، در واقع بدون نیاز به هیچ کانفیگ خاصی با حداقل کد (یک علامت پایپ |) می‌توانید اتصال دو برنامه را برقرار کنید. در این حالت هم مشکل اندازه بافر وجود دارد چرا که ممکن است برنامه‌ی تولید کننده بسیار سریع تولید کند ولی برنامه‌ی مصرف کننده در مصرف سریع نباشد، در این بین اطلاعات باید بافر شوند. لینوکس اندازه‌ی این بافر را عددی مثل ۶۴ کیلوبایت می‌گیرد. در حالتی که هم بافر پر شود تولید کننده صبر می‌کند تا مصرف کننده مقداری مصرف کند.ارسال از طریق شبکهاین راه، عمومی‌تر از ارتباط داخل یک سیستم‌عامل است و می‌توانیم با آن به سراسر جهان (اگر فیلتر و تحریم نباشد البته) مسیج ارسال کنیم، مثلا همین مطلب را از طریق شبکه از سایت ویرگول دریافت کرده‌اید! اما داخل یک سیستم هم می‌توان با باز کردن سوکت به localhost یا 127.0.0.1 و مشخص کردن پورتی که برنامه‌ی دیگر روی آن گوش می‌کند یک کانکشن باز کرد، این روش البته سربار شبکه را دارد اما در اکثر سیستم‌عامل ها جواب می‌دهد و برای همین جذابیت خاص خودش را دارد.در زندگی واقعی مثل این است که یک بسته پستی را با پست بین‌الملل برای یک نفر در کشور خود بفرستید، بله هزینه احتمالا بیشتر می‌شود ولی اگر کشور شما سامانه پست ندارد (سیستم‌عاملتان) این روش می‌تواند جوابگو باشد.روش‌های دیگرالبته خلاقیت بشر و روش‌ها نامحدودند! در ویکیپدیا می‌توانید چند مورد آن را بخوانید، اگر دوست داشتید در کامنت‌ها به راه‌هایی که من اشاره نکرده‌ام اشاره کنید.  https://en.wikipedia.org/wiki/Inter-process_communication منابع خواندنی دیگر: https://www.cs.princeton.edu/courses/archive/fall16/cos318/lectures/11.MessagePassing.pdf  https://www.mtholyoke.edu/courses/dstrahma/cs322/ipc.htm </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 02 Sep 2021 20:15:19 +0430</pubDate>
            </item>
                    <item>
                <title>کامیپوتر با صفر و یک کار می‌کند، چطور همه‌ی چیزهای زیبا با صفر و یک ساخته می‌شوند؟</title>
                <link>https://virgool.io/@rsharifnasab/computer-1-bminlms1w4hb</link>
                <description>تیتر مطلب بلندتر از تیتر‌های معمول شد اما همچنان آن‌قدر که باید گویا نیست، بنابراین پاراگراف اول را به این اختصاص می‌دهم که قرار است در مورد چه چیزی صحبت کنم. همه ما شنیده‌ایم که کامپیوتر با ۰ و ۱ کار می‌کند، یا به شکل پایه‌ای‌تر، در دنیای دیجیتال فقط دو مقدار (مثلا ۰ ولت و ۵ ولت) تعریف شده‌است. کامپیوتر از روی این صفرها و یک‌ها چطور برای ما برنامه های رنگی اجرا می‌کند؟ چطور می‌فهمد باید چه کار کند؟ چطور در شبکه داده‌ها را انتقال می‌دهد؟ چطور همه چیز‌های زیبا فقط با این دو عدد ساخته می‌شوند؟تصویر نمایشی است! فاز هکری نداریم!چیزی که اینجا می‌نویسم، درواقع ریشه در درس‌های مدار منطقی، معماری کامپیوتر، ریزپردازنده و اسمبلی، مبانی کامپیوتر، شبکه و شاید کمی بیشتر است اما سعی می‌کنم تا حد امکان ساده توضیح دهم. اگر جایی بی‌دقتی علمی دارد یا می‌شود بهتر توضیح داد حتما بگویید.دنیای دیجیتال بین همه مقادیر دنیای آنالوگ فقط تعدادی مقدار گسسته را برگزید، در واقع دو مقدار در دنیای کامپیوتر‌های امروزی که این دو مقدار گسسته ۰  و ۱ هستند، اینکه  ۰ و ۱ هرکدام معادل چه ولتاژی در آنالوگ هستند چندان مهم نیست مثلا منفی ۵ و مثبت ۵ یا ۰ و ۵. در این مرحله ولتاژ abstract شده و فقط با ۰ یا ۱ بودنش کار داریم. چرا مقدارهای بیشتری مثلا ۳ و ۴ گرفته نشد؟ اتفاقا تلاش‌هایی شد: (درمورد ternary computer بخوانید) اما پیاده‌سازی قطعات آن‌ها سخت‌تر بود، کلیت ایده هم تفاوتی ندارد، مثلا همچنان می‌توان پرسید با ۴ مقدار چطور می‌توان همه چیز را پیاده سازی کرد. یادگیری باینری بی‌مصرف است.اگر موضوعی که در این مطلب سعی دارم پوشش دهم را سرچ کنید، اکثر مطالب به شما باینری (مبنای ۲) یاد می‌دهند، یعنی می‌گویند که ۱۱۰۰۱ در واقع نمایش عدد ۲۵ در مبنای دو است، یا اینکه چطور با روش تقسیم‌های متوالی یک عدد را به نمایش مبنای دو ببریم؟ این هم علم مفیدی است و برنامه‌نویس‌ها گاهی مجبور می‌شوند خودشان یک عدد را به مبنای دو ببرند یا نمایش باینری یک عدد را بخوانند و متوجه شوند مربوط به چه عددی است. اما این فقط یکی از مشکلات است، در واقع فقط یاد می‌گیریم که عدد مثبت چطوری به باینری تبدیل می‌شود اما این برای اینکه بفهمیم مثلا تصویر چطور ذخیره می‌شود کافی است؟ نه. برای اینکه بفهمیم ویدیو چطور پخش می‌شود کافی است؟ بازهم نه! حتی یک سوال مهم هم نمی‌توان جواب داد، از کجا می‌فهمیم این عدد تمام شده؟ تا چند رقم باینری این عدد ادامه دارد؟ از همان مثال‌هایی که جواب سوالمان را نمی‌دهد.بیت و بایت هم بی‌مصرف هستند.شاید بسیاری از آموزش‌های کامپیوتر هم به شما بگویند که در کامپیوتر هر کدام از صفر و یک‌ها یک «بیت» (bit) است، یعنی یک بیت می‌تواند «یک» باشد یا «صفر». حالا هر ۸بیت را در یک دسته می‌گذاریم و اسمش را «بایت» (Byte) می‌گذاریم. خب این چه کمکی می‌کند؟ متاسفانه باز هم هیچ. البته دانستن این موضوع هم یکی از اجزای مهم برای فهم ادامه مطلب است برای همین اینجا آوردم تا اگر به گوشتان نخورده آشنا شوید.حالا که بحث بیت شد یک سوال حاشیه‌ای هم جواب دهم: کامپیوتر من ۶۴ بیتی است یعنی کلا ۶۴ تا بیت دارد؟ یعنی فقط ۶۴ تا صفر و یک؟ جواب این است که نه، کامپیوتر شما بیشتر از این حرف‌ها صفر و یک دارد، این یکی از شاخص های اندازه‌گیری است که خیلی هم مهم نیست.داده‌ها و دستورها را بشناسیم.فرض کنیم که کامپیوتری درکار نیست و فقط خودمانیم. حالا می‌خواهیم یک مساله ریاضی حل کنیم، این مساله ریاضی دبستان یک‌سری عدد به ما می‌دهد و یک‌سری دستور که روی این اعداد پیاده‌کنیم، به عدد‌ها بگویم داده (value) و به دستورها (مثلا ضرب کن یا تقسیم کن یا جمع کن) بگوییم دستور (instruction).برای کامپیوتر هم که ماشین محاسبات است هردوی این‌ها معنی‌دار است، یک‌سری دستور دارد که باید انجام دهد و یک‌سری داده دارد که عملیات روی این‌ها انجام می‌شود، حتی اگر ماشین حساب را یک کامپیوتر در نظر بگیریم (که تقریب بدی هم نیست)، یک‌سری داده دارد (اعداد) و یک‌سری دستور (مثلا عملیات‌های ریاضی) که آن‌ها را می‌فهمد و روی داده‌ها اعمال می‌کند.کامپیوتر: تشکیل شده‌ از پردازنده، مموری و حافظه جانبی.به طور ساده اگر بخواهم یک کامپیوتر را شرح دهم، یک پردازنده دارد که کار پردازش را انجام می‌دهد یعنی دستورها و مقدارها را می‌گیرد و کارهایی که دستورها بیان می‌کنند را روی داده‌ها انجام می‌دهد و داده‌های نتیجه را به دست می‌آورد. داده‌ها و دستورها در کجا ذخیره شده‌اند؟ روی حافظه یا مموری (RAM). در واقع حافظه  یک لیست بسیار بزرگ از بیت‌هاست که روی آن داده‌ها و دستور‌ها وجود دارند. (روی این بیشتر صحبت خواهیم کرد)  اما این مموری یک محدودیت دارد و این است که اگر برقش قطع شود (مثلا کامپیوتر خاموش شود) اطلاعاتش از دست می‌رود، پس وقتی سیستم را روشن می‌کنیم اطلاعاتش را از کجا می‌خواند؟ حافظه جانبی (که می تواند بیرون کامپیوتر باشد مثل فلش مموری یا داخلش مثل hard disk)  هم مثل مموری یک حافظه بزرگ است اما با این تفاوت که اطلاعات را می‌تواند بدون نیاز به برق برای همیشه نگهداری کند، بنابراین وقتی سیستم روشن می‌شود اطلاعات از این حافظه باید خوانده شوند و در طول اجرا نیز مموری و پردازنده باید اطلاعاتی که نیاز دارند دفعات بعدی هم داشته باشند در حافظه جانبی بنویسند. بازی با اعدادبیایید کمی دانش ریاضیات گسسته را به مفاهیم بیتی و بایتی هم اضافه کنیم.اگر هر بیت دو حالت داشته باشد، یعنی یک بیت می‌تواند ۲ حالت مختلف را نشان دهد. حالا دو بیت کنار هم چند عدد را می‌توانند نشان دهند؟ بیایید حالت‌ها را بنویسیم: ۰۰و ۰۱و ۱۰ و ۱۱، یعنی ۴ حالت. برای ۳ بیت چی؟ برای ۳ بیت نتیجه دوبرابر جواب ۲ بیت است یعنی ۸ حالت. (۲ به توان ۳)یک بایت (یا همان ۸بیت) که یک اندازه محبوب برای برنامه‌نویس‌هاست می‌تواند دو به توان ۸ (۲۵۶) حالت مختلف را برای ما بسازد.در ۴ بایت (یا ۳۲ بیت) چند حالت مختلف می‌توانیم داشته باشیم؟ ۲ به توان ۳۲ حالت. (4294967296)نشان دادن چیزهای مختلف با صفر و یکبالاخره مقدمه تمام شد!  در این قسمت می‌خواهیم به ۰ و ۱ ها کم‌کم معنی بدهیم.حالت‌های مختلف چه معنی‌ای می‌دهند؟ وقتی می‌گویم یک بایت می‌تواند ۲۵۶ حالت مختلف را نشان دهد  تقریبا مشخص است، یعنی ۲۵۶ شکل مختلف دارد که هر کدام می‌تواند یک معنی مختلف داشته باشد، اما این معنی‌ها چه چیزایی هستند؟ آیا کامپیوتر خودش این ۲۵۶ حالت مختلف را می‌فهمد؟ نه کامپیوتر خودش ۲۵۶ حالت را نمی‌فهمد، خود این حالت‌ها هم به خودی خود هیچ معنایی ندارند، مثلا 00011001 یکی از آن ۲۵۶ حالت است اما به خودی خود هیچ معنایی ندارد، حتی یک برنامه‌نویس خبره هم از این به تنهایی هیچ برداشتی نمی‌کند. شاید بگویید که نه ما کمی مبنای دو یاد گرفتیم، این عدد همان ۲۵ است که بالا دیدیم، اما بگذارید مخالفت کنم. چرا 00011001 همان ۲۵ نیست؟اگر در دنیای ریاضی صحبت می‌کردیم، 00011001 در واقع ۱۱۰۰۱ بود، چون نگفتم در مبنای ۲ است، هر عددی که فقط از ۰ و ۱ تشکیل شده که لزوما در مبنای دو نیست! در ریاضی برای اینکه مشخص کنیم 11001 باید چطوری خوانده شود از یک نگارش خاص استفاده می کنیم، مثلا پایین سمت راست عدد مبنایش را می‌نویسیم. یک نمونه از نمایش ریاضی برای عددهاچرا در ریاضی این کار را می‌کنیم؟ برای اینکه مشخص باشد این دنباله‌ای از اعداد که پشت سر هم نوشته‌ایم چطور تفسیر می‌شود، در کامپیوتر هم نیاز به چنین چیزی است اما فعلا آن را نداریم، بنابراین سرخود 00011001 را به ۲۵ تفسیر نمی‌کنیم بلکه باید ببینیم چطور تفسیر می‌شود؟ بدون روش تفسیر فقط یک دنباله از ۰ و ۱ ها داریم.درست است که یکی از روش‌های تفسیر مهم همین مبنای دو است و هر رشته‌ای از ۰ و ۱ ها را می‌توانیم مبنای دو در نظر بگیریم اما آیا واقعا این کار صحیح است؟ مثلا دنباله زیر را در نظر بگیرید:یکسری ۰ و ۱ با معنی نامعلوم!اگر بپرسم این رشته باینری که به بایت‌ها هم شکسته شده چیست چطور پاسخ می‌دهید؟ می‌خواهیم هر بایتش را مبنای ۲ فرض کنید و به یک عدد تبدیل کنید؟ یا کلش را یک عدد می‌گیرید؟ این کار‌ها هرچند قابل انجام است اما صحیح نیست، این رشته صفر و یک در واقع اصلا عدد نیست، بلکه چند بایت اولی است که تصویر پروفایل من را نشان می‌دهد. درمورد اینکه چطور یک تصویر را می‌شود با رشته باینری نشان داد جلوتر صحبت می‌کنیم اما در همین حد مهم است بدانید که هر رشته‌ای از صفر و یک قرار نیست یک عدد بدون علامت باشد. فرق عدد با نمایش چیست؟ عدد یک چیز انتزاعی است، مثلا عدد ۱ است، همه ما وقتی به یک فکر می‌کنیم یک چیز واحد در ذهنمان می‌آید، همچنین عدد دو یا عدد پی، حتی با اینکه عدد پی به شکل دقیق محاسبه نشده ولی به یک عدد واحد فکر می‌کنیم. اما نمایش (representation)، شکلی است که عدد را روی کاغذ می‌نویسیم مثلا «۰» یا «۱» یا «۲۵» (برای آخری فرض کنیم مبنا ده است) نمایش هستند.یک عدد می‌تواند چند نمایش داشته باشد مثلا ۲۵ را می‌توانیم با باینری هم نمایش دهیم که می‌شود 11001. همچنین یک رشته از ارقام هم می‌تواند نشان‌دهنده‌ی چند عدد مختلف باشد مثلا ۱۰ بسته به اینکه در چه مبنایی بخوانیمش عدد متفاوتی می‌شود!اگر این میم رو متوجه نشدید اشکال نداره! میگه برای کسی که مبنای چهار می‌فهمه، نمایش چهار به شکل &quot;10&quot; میشه اما برای کسی که مبنای ده می‌فهمه، نمایش ده به شکل &quot;10&quot; می‌شه.پس برای اینکه یک عدد را نمایش دهیم باید یک نمایش را انتخاب کنیم، مثلا مبنای ۲ یا ده‌دهی نمایش‌هایی هستند که بلدیم، از طرف دیگر برای اینکه رشته از ارقام را تفسیر کنیم (عددش را بفهمیم) نیاز داریم که بدانیم از چه نمایشی استفاده می‌کند.نمایش‌های اعداد مخصوص کامپیوتر همه‌ی نمایش‌های ممکن، در ریاضی نیستند، ریاضیدان‌ها بر حسب نیاز خود که روی کاغذ می‌نوشتند یک نمایش برای هر مبانی درست کردند، اما اینکه روی کاغذ می‌نوشتند آزادی‌ای‌ به آن‌ها می‌داد که برنامه‌نویس‌ها روی کامپیوتر نداریم، بیاید به اعداد غیر از طبیعی نگاه کنیم، مثلا عدد منفی ۲۵، ریاضی‌دان‌ها در مبنای ۲ آن را به شکل 11001-  نشان می‌دهند اما در کامپیوتر که فقط ۰ و ۱ داریم، منها رو باید چکار کنیم؟! یا مثلا برای اعداد اعشاری مثلا 12.5 را به شکل 1100.1 نشان می‌دهند اما بازهم در کامپیوتر فقط ۰ و ۱ داریم و نقطه‌ی اعشاری هم نداریم.برای حل این مشکل، یعنی نشان دادن اعداد اعشاری یا علامت‌دار در کامپیوتر نمایش‌های متفاوتی بر پایه‌ی همین ۰ و ۱ درست کرده‌اند، توضیح همه ان‌ها اینجا جا نمی‌شود اما مثلا می‌توانید فرض کنید که جدا از خود عدد یک بیت داریم که نشان می‌دهد عدد مثبت است یا منفی. مثلا 011001 می‌شود عدد ۲۵+ و 111001 می‌شود ۲۵−. البته به این سادگی نیست مثلا این روش دو تا صفر دارد (مثبت صفر یا منفی صفر)  به این روش اشکال‌دار «علامت  و مقدار» (sign and magnitude) می‌گویند.برای مطالعه بیشتر در این مورد می‌توانید اسلاید‌های number representation از این لینک را بخوانید.چطور یک نمایش‌ برای کامپیوتر معنا پیدا می‌کند؟فرض کنیم تنها چیزی که می‌خواهیم نشان بدهیم اعداد مثبت و منفی هستند و از همین نمایش اشکال‌دار «علامت و مقدار» که پاراگراف بالا معرفی شد استفاده کنیم، آیا با این قراردادی که ما کردیم کامپیوتر می‌فهمد که 011001 یک عدد مثبت است و به معنی مثبت ۲۵ است؟ نه، کامپیوتر متاسفانه این قرارداد ما را نمی‌فهمد، هرقرارداد دیگری هم کنیم باز هم نمی‌فهمد، کامپیوتر اصلا قرار نیست چیزی را بفهمد، کامپیوتر قرار است برای ما محاسبه انجام دهد! اگر قرار بود بفهمد که اسمش computer (ماشین محاسبه‌گر) نبود.اما اگر قرار است کامپیوتر چیزی را نفهمد پس چطوری محاسبه انجام می‌دهد؟ چطور به او بگوییم یک عدد را با دیگری جمع کن؟  حالا این دو عدد با نمایش «علامت و مقدار» در حافظه وجود دارند یا هر نمایش دیگری.بگذارید من یک سوال دیگر بپرسم، اصلا چطور به کامپیوتر بگوییم یک کار را بکن؟ حالا اصلا هرکاری! پردازنده از ما یک‌سری دستور (instruction) قبول می‌کند، روی کاتالوگ پردازنده می‌توانید این دستورات را مطالعه کنید، حالا پردازنده برنامه‌ریزی شده که هرکدام از این دستورات را انجام دهد، مثلا یک دستور ساده ولی مهم این است که یک عدد بدون علامت را بگیر (که می‌شود همان مبنای دو خودمان) و یکی به آن اضافه کن.  بنابراین برای اینکه کامپیوتر بتواند یک عدد «علامت و مقدار» را بعلاوه یک کند، باید برای اینکار طراحی شده باشد و یک دستور برای اینکار داشته باشد، اما متاسفانه پردازنده‌ها برای این نمایش طراحی نشده‌اند و چنین دستوری ندارند به جایش دستوری دارند که عددی که در سیستم نمایش «مکمل ۲» نمایش داده شده را بلاوه یک کنند چون سیستم مکمل دو سیستم رایجی است که اعداد علامت‌دار را با آن در کامپیوتر نشان می‌دهند.کامپیوتر از کجا می‌داند که عدد ما قرار است چند بایت باشد؟ وقتی به کامپیوتر می‌گوییم عددم را با یک جمع کن (اسم دستور inc است) از کجا می‌داند این عدد تا کجا است؟ مثلا ۲۵ در باینری علامت و مقدار می‌شود ۶ رقم اما  ۲۵۰۰ می‌شود ۱۵ رقم، کامپیوتر از کجا می‌داند که چند رقم باید ادامه دهد تا آخر عدد را پیدا کند؟ در نوجوانی فکر می‌کردم که بعد از عدد حتما یک الگوی خاصی قرار می‌دهند مثلا 11111111 که کامپیوتر از روی آن می‌فهمد که دیگر عدد تمام شده و نباید جلوتر را بخواند (هرچند این شیوه هم واقعا جاهایی استفاده می‌شود!) اما برای اعداد روش خیلی ساده‌تر است، عدد در کامپیوتر طول مشخصی دارد، ۴ بایت یا همان ۳۲ بیت! اگر عددمان کوچکتر بود چیکار کنیم؟ پشتش صفر می‌گذاریم.اگر عدد بدون علامت باشد با این روش نهایتا همان ۲ به توان ۳۲ منهای یک که در قسمت بازی با اعداد گفتیم را می‌تواند نگه دارد اما اگر عدد علامت دار باشد یک بیت برای علامت می رود پس بزرگ‌ترین عدد نصف این عدد است. (۲ به توان ۳۱)اما اگر عدد بزرگتر بود؟ پردازنده به ما کمکی نمی‌کند، مثل ماشین حساب که فقط مقدار محدودی محاسبات دارد، اما خودمان می‌توانیم به خودمان کمک کنیم، مثلا به این شکل که عددمان را به نحوی به ۲ قسمت بشکنیم و در چند مرحله عملیات مورد نظرمان را انجام دهیم.(البته ۴ بایت برای پردازنده‌های arm آن هم ۳۲ بیت دقیق است، برای سری x86 اعداد مختلفی هست ولی معمولا از بین اعداد ۱ یا ۲ یا ۴ یا ۸ بایت خارج نیست)آیا پردازنده برای همه چیز دستور دارد؟خب تا اینجا دیدیم که برای اعداد علامت‌دار باید خود پردازنده یک دستور مخصوص این کار داشته باشد، اما آیا پردازنده برای همه چیز دستور دارد؟ مثلا برای عدد اعشاری یا تصویر هم یک instruction خاص داریم؟ نه.البته برای عدد اعشاری اکثر پردازنده‌ها استاندارد ممیز شناور IEEE 754 را پشتیبانی می‌کنند اما این فقط یک حالت است، هرجور دیگری که بخواهیم قرارداد کنید پردازنده کمکی به شما نمی‌کند، به جز عدد هم که پردازنده کلا کمکی نمیکند، اما برای نمایش تصویر چه کار باید بکنیم؟ برای کاربردهای دیگر باید برنامه خودمان را طوری بنویسیم (یعنی instructionها را طوری دنبال هم قرار دهیم) که کاری که می‌خواهیم بکنیم را با دستورات دیگر انجام دهد، در این باره در قسمت‌های بعد بیشتر می‌خوانیم. نمایش کارکترها با ASCIIاَسکی یا ASCII یکی از ساده‌ترین مشکلاتی است که حل می‌شود و مثال خوبی برای شروع است، فرض کنید به جز عدد می‌خواهیم یک چیز دیگر هم نمایش دهیم: کارکترهای انگلیسی. 26 تا حرف انگلیسی داریم که با کوچک و بزرگش می‌شود 52 تا، با کارکترهای خاص مثل علامت تعجب و درصد و ویرگول و .. نهایتا به ۱۰۰ تا نمی‌رسد، پس می‌توانیم همه حروف انگلیسی را با یک بایت نمایش دهیم. یادمان هست که بایت ۸ بیت بود و می‌توانست ۲۵۶ حالت مختلف داشته باشد. حالا باید یک mapping یک به یک از هریک کارکترها به یک نمایش بیتی داشته باشیم، یعنی در واقع قرارداد می‌کنیم که هر کدام از کارکترها یک عدد بین ۰ تا ۲۵۵ باشند. این قرارداد را در جدول اسکی می‌توانید ببینید:جدول اسکی محبوببه جدول نگاه کنیم، ستون DEC یک عدد ده‌دهی را نشان می‌دهد، ستون chr یکی از کارکترهایی که در قرارداد هستند آمده، البته  ستون سمت راست شاید خیلی آشنا به نظر نرسد چون کارکترهای خاص برای نیاز داخلی کامپیوتر هستند اما ستون دوم و سوم و چهارم کارکترهای آشنای خودمان هستند. قسمت Hex هم معادل مبنای ۱۶ (Hexadecimal) از عدد ده‌دهی است و نکته خاصی ندارد! (فرض کنیم) این استاندارد کاملا جدید است، کامپیوترها در زمان ساخت هیچ چیزی در مورد اسکی نمی‌دانند اما با کمک برنامه‌هایی که می‌نویسیم می‌توانیم با اینها کار کنیم، مثلا یک عدد ۶۵ را نگه می‌داریم و آن را یکی اضافه می‌کنیم، در واقع کارکترمان از A تبدیل به B شد! اینکه چطوری این عدد را روی مانیتور نمایش می‌دهیم بماند برای یک قسمت دیگر!برای کارکترهای فارسی و ایموجی‌های دوست داشتنی هم از یک قرارداد مفصل تر استفاده می‌شود به اسم Unicode. خوبی قرارداد ASCII این بود که همیشه در یک بایت جا می‌شد، اما Unicode اینطوری نیست، در واقع Unicode یک جدول بسیار بزرگ از کارکترهای همه زبان‌ها و همه‌ی ایموجی‌ها دارد (character set)اما همچنین یکسری نمایش (representation) مختلف هم وجود دارد، مثلا UTF-8 یا UTF-16 یا UTF-32 که تلاش می‌کنند این کارکترها را به ترتیب در یک بایت، دو بایت و چهار بایت جا دهند. (character encoding)اینکه UTF-8 تلاش می‌کند هزاران کارکتر را با یک بایت نشان دهد هم جالب است، دقت کنید که تلاش می‌کند و لزوما موفق نمی‌شود! همگی در حداکثر حالت به ۴ بایت می‌رسند اما در جاهایی که بشود (مثلا اوایل لیست Unicode) از تعداد بایت‌های کم‌تری استفاده می‌کنند.توجه: این قسمت یه مقدار نادقیقه، استانداردهای بسیار بیشتری هم داریم که دلایل تاریخی دارند اما ایده کلی همین است که خواندید.در قسمت‌های بعدی در مورد چیزهای زیر می‌خوانیم:۲− خود پردازنده چطور با ۰ و ۱ محاسبات را انجام می‌دهد؟ ۳- رنگ، تصویر و ویدیو و کارت گرافیک چطور پردازش می‌شوند و نمایش داده می‌شوند.۴- اطلاعات چطور منتقل می‌شوند. (گذری بر peripheral و لایه‌های شبکه)۵- برنامه نویسی چطوری انجام می‌شود: کامپایلر و مفسر</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 02 Sep 2021 19:11:37 +0430</pubDate>
            </item>
                    <item>
                <title>توضیحی در مورد threadها در زبان جاوا</title>
                <link>https://virgool.io/javacup/java-threads-jdkqpmie0kkj</link>
                <description>توضیح: این متن رو به عنوان توضیح thread چیه به شکل غیررسمی نوشتم، تصمیم گرفتم اینجا هم منتشرش کنم شاید برای کسی مفید بود. برنامه‌نویسی بدون ترد رو انجام دادیم ولی برامون راحته ولی با ترد می‌تونیم به کارایی بالاتر و کدهای منطم‌تری دست پیدا کنیم یا جتی چیزاهایی رو بتونیم انجام بدیم که قبلا نمی تونستیم. در این مطلب اصول اولیه thread رو یاد می‌گیریم.ما بدون آگاهی از thread، برنامه‌هامون یک نخ پردازشی داشت، همه  چیز دنبالِ هم اجرا می‌شد.اول دستورِ اول، بعد دستورِ دوم همینطور تا اخر (طبیعتا توی حلقه‌ها این اتفاق نمی‌افتاد ولی منظورم مشخصه)اما سیستم‌های ما چند تا هسته‌ی پردازشی دارند یعنی در آن واحد می‌تونن چند تا کار رو همزمان انجام بدن. (چرا چند تا هسته دارند؟ چون نمیشه یه هسته‌ی پردازشی رو از یه حدی قوی‌تر کرد، پس میان چند تا هسته پردازشی رو توی یه چیپِ واحد قرار می‌دن و اینطوری توان پردازشی رو زیاد می‌کنن)حالا چطوری می‌شه؟ یک پردازنده داریم.این پردازنده در آنِ واحد  چند تا هسته پردازشی داره (مثلا چندتا سی‌پی‌یو کوچولو در پردازنده اصلی!) یه سری پردازش داریم، مثلا برنامه فایرفاکس، تلگرام و برنامه جاوایی شما که توی jvm داره اجرا میشه.این پردازش‌ها اگر برنامه‌نویسشون مبحث thread رو بلد نباشه، هر کدوم روی یه هسته اجرا می شن، مثلا روی هسته صفر، فایرفاکس اجرا می‌شه. روی هسته ی ۱ تلگرام، بازم روی هسته‌ی ۱ برنامه جاوای شما (برنامه‌ریزی این که کی کجا اجرا بشه به عهده سیستم‌عامله.)حالا نکته‌ای که هست اینه که به ۲ دلیل ما ممکنه نخواهیم برنامه مون در آنِ واحد فقط یک کار رو انجام بده.دلیل ۱: وقتی پردازنده بیشتر از یک هسته پردازشی داره، اینکه برنامه‌ما فقط یک thread داشته باشه و فقط روی یک هسته پردازشی اجرا بشه به لحاظ قدرت پردازشیِ به یک هسته از پردازنده محدود هستیم اما اگر چند تا thread داشته باشیم توان پردازشی بیشتری در اختیارمون قرار می‌گیره. (البته به صورت خطی رشد نمی‌کنه، اگر خیلی زیاد thread داشته باشیم اثر عکس میده)دلیل ۲: برنامه ما، چند تا کار همزمان رو باید انجام بده، مثلا تلگرام همزمان داره تایپ کردنِ من رو هندل می‌کنه و همچنین پیام‌هایی که میاد رو از سرور می‌گیره و رابط ظاهری رو آپدیت می‌کنه.پس ما از ترد‌های مختلف (یا فارسی بگم، نخ پردازشی) استفاده می‌کنیم.همین مثال تلگرام رو بریم جلو، ۲ هسته پردازشی براش فرض کنید.هسته اول که رابط گرافیگی رو هندل می‌کنه و کارای گرافیکی رو انجام می‌ده مثلا هندل کردن رفتن از این صفحه به اون یکی صفحه.هسته دوم هم کارهای پس‌زمینه مثلا ارتباط با سرور رو انجام می‌ده.اگر این ۲ تا هسته نباشن (و فقط یک هسته باشه) برنامه‌نویسی چنین چیزی خیلی سخت می‌شه مثلا من وقتی تایپ می‌کنم نمی‌تونه اطلاعات رو از سرور آپدیت کنه، یا اگر بخواد سرور رو بخونه، نمی‌تونه همزمان به تایپِ من رسیدگی کنه. (البته این سختی برای جاوا و بدون امکانات خاص مثل callback یا java.nio برقراره، ولی event loop در javascript این کار رو خیلی زیبا انجام می‌ده.)اما وقتی که چند تا ترد داشته‌باشیم، این ترد‌ها به شکل «تقریبا مستقل از هم» به کارشون ادامه می‌دن.مستقل بودنشون از این جهته که یکیشون ممکنه کاری برای انجام داشته باشه و انجام بده، اون یکی ممکنه کاری نداشته باشه (مثلا تایپ نکنم) یا اصلا اینترنتم قطع بشه و اون یکی نتونه کار کنه ولی این یکی کار می‌کنه.اما به هم مربوطن و مثل ۲ تا برنامه جدا نیستند. چرا؟چون مثلا وقتی که تایپم تموم بشه و ارسال کنم، دکمه ارسال رو می‌زنم، باید قسمت UI به صفِ کارهای تردِ شبکه این رو اضافه کنه که این پیام رو بفرست و خبرش رو بده بهم که اگر ارسال شد تیک بزنمش.یا اینکه می‌پرسه از تردِ شبکه که آیا پیام جدیدی اومد که من نشونش بدم؟یعنی این‌ تردها به شدت با هم ارتباط دارند و به هم نیازمند هستند.مثال ساده‌ش کار گروهیه. شما در زمان‌های مشخصی برای یه هدف معین همکاری می‌کنید. این همکاری‌تون برای یه نتیجه واحده ولی هر کدوم یه کار متفاوت می‌کنید و از نتیجه اون یکی هم استفاده می‌کنیم یا یکیتون بقیه رو مدیریت می‌کنه و نتایح رو با هم ادغام می‌کنه.راستی یادمون نره که ما ۲ تا مفهوم داریم، یکی هم‌روندی (concurrency) و یکی اجرای موازیه (parallel)در حالت هم‌روند، برنامه چند تا ترد داره و «به نظر می‌رسه» همشون با هم در حال اجرا هستند. حالا ممکنه واقعا این هسته‌ها روی هسته‌های پردازشی مختلفی در حال اجرا باشن یا به نوبت روی یک هسته اجرا بشن. (این نوبت‌دهی رو سیستم‌عامل هندل میکنه همونطور که گفتم)اما مفهوم دوم اجرای همزمانه. (parallel)به این صورت که این ۲ تا ترد «واقعا همزمان روی ۲ تا هسته مختلف» اجرا بشن.ولی همونطور که گفتم این دومی دست سیستم‌عالمه و در زمان اجرا تصمیماتش رو می‌گیره و دست من و شمای برنامه‌نویس نیست. ما فقط می‌تونیم مشخص کنیم حالت اول بشه یعنی چند تا ترد داشته باشیم که در آنِ واحد در وضعیت اجرا باشند. تا اینجا همه چی خوب و خوشحاله، فقط مفهوم ترد رو فهمیدیم اما قسمت سختش اینه که هسته‌ها بخوان با هم حرف بزنن یا ارتباط برقرار کنن یا از یک منبع مشترک استفاده کنند. اینکه بخوان با هم صحبت کنن که مشخصه، مثلا یکیشون به باقی دستور بده که این پردازش رو انجام بده تا من از نتیجه‌ش استفاده کنم.فرض کنید که ۴ تا آرایه داریم و می‌خوایم مجموع هر کدوم رو حساب کنیم و بعد از روی این، جمع کل رو به دست بیاریم.چیکار می‌کنیم؟ ۴ تا ترد می‌سازیم که هر کدوم یه آرایه رو محاسبه کنن.بعد لازمه توی هسته اصلیمون، نتیجه رو بگیریم ازشون (مثلا یه جایی ذخیره کردن) و عملیات نهایی رو انجام بدیم روش.باید یه جوری بگیم آقا یا خانمِ ترد، شما اجرا شو، کارت تموم شد خبر بده بهم.اینجا از start و join استفاده می‌کنیم.مثلا میگیم:// main thread:
t1.start();
t2.start();
t3.start();
t4.start();

// now they wre working concurrently

t1.join(); //wait for t1
t2.join(); //wait for t2
t3.join(); //wait for t3
t4.join(); //wait for t4
long sum = t1.ans + t2.ans + t3.ans + t4.ans;متد join، در واقع یه جور delay برای هسته فعلیمون هست. به این صورت که اینقدر صبر کن تا اون هسته مد نظر کارش تموم شه.خب یعنی اینطوری میشه برنامه که به همه ۴ تا ترد دیگه می‌گه کارشون رو شروع کنن و بعد به اولی میگه من صبر می‌کنم تو کارت تموم شه.حالا اولش کارش تموم شد همین رو به دومی میگه.حالا ممکنه دومی کارش زودتر تموم شده باشه و دیگه این delay نخوره یا ممکنه دومی هم کارش طول بکشه حالا برای دومی صبر می‌کنه.همینطور برای سومی و چهارمی.حالا که هر ۴ تا عملیاتشون تموم شده و نتیجه آماده است  و از ans استفاده می‌کنیم. (فرض کنیم نتیجه جمعشون رو توی یه متغیر توی شی خودشون به نام ans نوشتن.حالا همه این‌ها برای این بود که ۲ تا کد موازی اجرا بشن.اما اگر بخوان که از یه «منبع مشترک» استفاده کنن چی؟مثلا یه متغیر شمارنده (چیزهای پیچیده‌تر هم می‌تونن باشن فقط آرایه یا فایل مشترک)مثلا هر کدوم می‌خوان یه متغیر مثل count رو زیاد کنند.چاره چیه؟ اول بگم مشکلی که پیش میاد چیه؟فرض کنید که ۲ تا ترد دقیقا همزمان بخوان count رو زیاد کنن.هر کدوم نگاهش می‌کنن می‌گن خب صفر هست و من باید ۱ بنویسم توش، بعد توش یک می‌نویسن ولی در واقع باید ۲ نوشته بشه.اینجا ۲ تا پیشنهاد برای رفع مشکل داریم.یکی اینکه عملیات رو atomic تعریف کنیم، یعنی به جای int معمولی، بیایم atomic integer استفاده کنیم.اتمیک یعنی هرکاری با من داری رو توی یه حرکت انجام بده، و تا زمانی که تو داری استفاده می‌کنی کس دیگه‌ای دست نمی‌زنه. یعنی چی؟ یعنی ۲ تا تردی که همزمان می‌خواد به int count دست بزنن، به یکیشون می‌گه دست نگه دار، به دومی می‌گه خب هرکاری داری انجام بده، بعد که کارش تموم شد نوبت دومی میشه. اتمیک بودنش یعنی اینطوری نیست که اول بگه حالا می‌خونم بعد کارم رو انجام می‌دم و می‌نویسم، خوندن و نوشتن رو توی یه مرحله انجام می‌ده و تا زمانی که توی این مرحله هست، ترد دیگه ای حتی اجازه خوندن هم نداره. (مطالعه بیشتر)اما یه چیز دیگه هم داریم به اسم بلوک‌های سنکرون.به این صورته که ما یه سری بلوک سنکرون (یا متد سنکرون) داریم. این بلوک‌های سنکرون اینطوری هستند که برنامه‌نویس می‌گه بلوک‌ها قراره روی یه منابع مشترکی کار کنند که اگر همزمان دست بزنن بهش، خراب می‌شه. پس از جاوا می‌خوایم دسترسی یکیشون رو در آن واحد میسر کنه (به عبارت دقیق تر mutual exclusion).حالا چطوری بگیم این بلوک سنکرون روی چی داره کار می‌کنه که کس دیگه ای روی این شی خاص دست نبره؟به هر بوک سنکرون یه شی می‌دیم، این قفل در واقع یه object یا یه class هست. تا وقتی که یه بلوک سنکرون با شی X در جال اجراست، هیچ بلوک سنکرون دیگه ای نمی‌تونه قفل شی  X رو بگیره و عملا اجرا یشه.می تونه روی شی Y از همون کلاس کار کنه ها، ولی همون شی نه.حالا متد سنکرون هم عملا یه بلوک هست که روی this یا همون آبجکت لاک می‌شه، تا وقتی که این متد در حال اجرا یاشه، هیچ متد سنکرون دیگه‌ای «روی این شی» اجرا نمیشه.سینتکس ساخت ترد هم دو مدله.اولی اینکه یه کلاس داشته باشیم و از Thread ارث‌بری کنه و متد runش رو override کنیم و توش دستوراتمون رو بنویسیم. ساخت این مدل ترد سینتکسش راحت تره ولی سختیش اینه که باید کلاسمون الزاما از Thread ارث‌بری کنه و نه چیز دیگر.دومی هم اینکه کلاسمون اینترفیس Runnable رو implement کنه، باز هم کدهامون رو توی تابع runش می‌نویسیم با این تفاوت که موقع ترد ساختن، یه شی از اون کلاسمون که Runnable رو implement کرده به ترد می‌دیم.هر دو تا حالت، در صورت اجرای متد start روی thread، میاد و تابع ران رو به صورت موازی اجرا می‌کنه.سینتکس ترد را اینجا ببینید.نکته کنکوری: چرا ما تابع run رو نوشتیم ولی باید start رو صدا کنیم؟چون run یه تابع معمولیه و ما نوشتیمش، اگه اون رو صدا کنیم، فقط انگار یه متد معمولی رو صدا زدیم و به صورت sequential اجرا می‌شه! اما متد start یه متد سطح پایین با پیاده‌سازی خاص هست که با سستم‌عامل در ارتباطه و یه ترد جدید می‌سازه و تابع run ما رو توش اجرا می‌کنه.دیگه نکته اضافه اینکه ساخت تردِ معمولی (همین که ما می‌سازیم و به kernel thread هم معروفه)، کار خیلی کندیه، نسبت به اجرای یه تابع معمولی.و هر ترد هم برای خودش منابع می‌گیره و اینکه ما بیایم تردهای زیاد بسازیم اتفاق خوبی نمی‌افته، فقط فشار روی سیستم‌عامل برای مدیریت ترد‌ها بیشتر می‌شه. مثلا ما یه آرایه ۱۰۰۰ در n داریم و می‌خوایم مجموع همش رو حسابی کنیم، خوب نیست که بیایم ۱۰۰۰ تا ترد بسازیم، چون اینطوری کارایی از همون یک ترد هم کم‌تر می‌شه، بلکه ۴-۶ تا ترد  می‌سازیم. توجه: در زبان های دیگه مثل go ترد‌های غیرکرنلی که سبک‌تر باشن هم وجود داره. در مورد go routine می‌تونید بخونید. در مورد ترتیب و مدیریت ترد‌ها: از اون جا که سیستم‌عامل مدیریت منابع و ترد‌ها رو داره، روی ترتیب اجرای تردها هیچ حسابی نمی تونیم بکنیم. ممکنه ۲ تا کد که انتظار داریم همزمان اجرا بشن، اصلا دومی رو اجرا نکنه به مدت یک دقیقه، بعد ۳ دقیقه دومی رو اجرا کنه (اصولا اگه سیتسم‌عامل باشعوری(!) باشه این کارو نمی‌کنه چون همه چی خراب می‌شه ولی امکانش هست). حالا این مثال فرضی بود ولی عملی ترش اینه که اگه ما توی ۲ تا ترد بگیم یکی ۱ تا ۱۰۰ رو چاپ کنه و دومی a تا z رو، هیچ حسابی روی ترتیب چاپشون نمی‌تونیم بکنیم، ممکنه اول ۱ تا ۱۰ چاپ بشه بعد a  تا c و باز همینطوری، ممکنه اول ۱ تا ۱۰۰ چاپ یشه. توی یه سیستم واحد هم با هر بار اجرا ترتیب‌ها عوض میشه!در مورد گرسنگی یا starvation، اینطوریه که وقتی یک ترد روی یه شی قفل کرده و توی بلوک سنکرون داره کلی عملیات انجام می ده (این دیگه دست برنامه نویسه) حالا ترد‌های دیگه که منتظرن اون قفل رو باز کنه و بتونن وارد بلوک سنکرون خودشون بشن، عملا دقیقه‌ها باید منتظر بمونن و باقی کدهاشون اجرا نمیشه و اصطلاحا دچار گرسنگی یا starvation می‌شن چون اجرا نمی شن و اون تسکی که دارن انجام می‌دن رو زمین می‌مونه.حالتِ بدِ دیگه، deadlock هست. بن‌بست!اینطوریه که ترد دوم منتظره ترد اول تموم شه.ترد اول هم منتظره ترم دوم کارش تموم شه، یا منتظره بره توی بلوک سنکرونش (در حالی که اولی هم توی بلوک سنکرونه) اینجا هیچ کدوم نمی تونه پیش‌رفت کنه و تو همین وضعیت باقی می‌مونیم!این حالت‌ها واقعا پیدا کردنش سخته و ممکنه سال‌ها برنامه درست کار کنه ولی یهو به همچین حالتی برسه و دیگه کار نکنه! واقعا پیدا و رفع کردن چنین باگ‌هایی نبوغ و دقت می‌خواد. در مورد ساحتمان داده‌های هم‌روند (یا thread-safe) هم بگم که اینا یه سری ساختمان داده هستند که در مقابل استفاده چند ترد همزمان امن هستند، یعنی چی؟ مثل اون atomic integer که یه متغیر امن بود، این‌ها لیست و مپ و غیره هستند.به طور ساده بخوایم در نظر بگیریم لیست thread-safe  که یه لیست معمولی هست که همه متدهاش سنکرون شده!کاراییش البته معمولا از چیزی که گفتم بهتره چون پیاده سازیش واقعا اینطوری نیست، مثلا عملیات‌های get به صورت همزمان انجام می‌شه ولی عملیات‌های set به صورت یکی یکی و سنکرون.اینا کاراییشون اگر توی یک نخ استفاده بشن از ساختمان داده‌های عادی کم‌تره چون باید اول هر متد اون چک کردن قفل و اینا رو انجام بدن ولی توی استفاده چند تا ترد در مجموع بهترن (کد منبع اینا رو هم بخونید جالبه، کلی چیزای خفنِ ترد می بینید)در نهایت این رو ببینید اگر دوست داشتید:https://www.youtube.com/watch?v=alJuV66KMEM</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Tue, 06 Apr 2021 12:09:18 +0430</pubDate>
            </item>
                    <item>
                <title>حل یک مثال IEEE 754 (ممیز شناور)</title>
                <link>https://virgool.io/@rsharifnasab/ieee-754-example-gygw76uimzqv</link>
                <description>اخیرا با سایت keysleft.com آشنا شدم. این سایت با گرفتن سن و سرعت تایپ شما، محاسبه می‌کنه تا آخر عمرتون حدودا چند تا کلید کیبورد دیگه خواهید زد. خود این تعداد اهمیت خاصی نداره، بیشتر جنبه نمادین داره که عمرمون رو پای چی می‌گذاریم. توصیه‌ش هم اینه که به جای نوشتن جواب ایمیل، همون جواب رو (اگه جنبه عمومی داره) به شکل وبلاگ در بیارید تا بعدا قابل ارجاع دادن باشه و خلاصه قابل استفاده مجدد باشه و نخواید کل جواب رو از اول تایپ کنید. (برای من این ایده رو داشت که سرعت تایپتون هم بالا ببرید!) حالا به خاطر همین موضوع، یک مثال IEEE 754 که قبلا توی ایمیل حل کردم رو اینجا قرار میدم تا اگر کسی خواست استفاده کنه. شاید هم این رو دارید می‌خونید چون خودم بهش ارجاع دادم قبلا.این مطلب با این فرض نوشته شده که با کلیات IEEE 754 (سیستم ممیز شناور) آشنایی دارید و صرفا یک مثال اضافی برای تثبیت مطلب است.صورت سوال!خب سوال رو با هم بررسی می‌کنیم:  یک عدد در مبنای 16 داده و گفته IEEE 754 با دقت معمولی (32 بیت) هست، ما باید تبدیل کنیم به معادلش در  مبنای  10 و مبنای ۲ (توجه کنید که b نشان دهنده‌ی binary و d نشان دهنده‌ی  decimal یا مبنای 10 است)ما یک قاعده داریم که چطوری می‌تونیم یک رشته 32 بیتی (یعنی 32 تا صفر و یک) رو به معادل باینریش تبدیل کنیم (همون استاندارد IEEE 754 برای مثال میگه بیت اول نشون دهنده علامته و غیره..)استاندارد IEEE754حالا  برای اینکه بتونیم از این قاعده استفاده کنیم، باید اول عدد مبنای 16مون  رو ببریم به مبنای 2، برای اینکار از روش تبدیل مستقیم استفاده می‌کنیم  یعنی هر رقم رو به 4 رقم در مبنای ۲ تبدیل می‌کنیم.از سمت چپ شروع می‌کنیم:4 می‌شود : ‌01001 می‌شود: 0001و A که معادل 10 است می‌شود :‌ 1010و مابقی صفر هستند که هرکدام به 4 صفر تبدیل می‌شود.توجه خیلی مهم: عددی که ما در مبنای 16 داشتیم در واقع فقط یک نمایش از رشته صفر و یک ما بود و خودش مستقلا معنی عددی نداشت، یعنی اینطوری نیست که بگوییم عدد ما معادل  1 میلیارد و 101 میلیون است، چون این نمایش مبنای 16 فقط هدف و کارکردش  نمایش ساده (و خلاصه‌ی) رشته 32 بیتی از صفر و یک است.خب در این مرحله با کنار هم گذاشتن صفر و یک های بالا به یک رشته 32 بیتی از صفر و یک رسیدیم :‌0100-0001-1010-0000-0000-0000-0000-0000 حالا در این مرحله باید طبق قاعده‌ی IEEE 754 32 bit تبدیل را انجام دهیم:بیت اول برای علامت : 0 پس عدد نهایی ما مثبت است8 بیت بعدی برای exponent + bias :(یاد آوری: برای نگهداری exponent از شیوه پیش‌قدر دار استفاده می‌شود)100000011   که بیانگر عدد 131 است، طبق قاعده باید 127 تا از حاصل کم کنیم تا به  exponent  واقعی برسیم. (طبق استاندارد مذکور، مقدار bias برای 32 بیت، 127  است)با کم کردن 127 از 131 مقدار 4 برای exponent به دست می‌اید.در نهایت 23 بیت باقی‌مانده، fraction یا mantissa هستند.این عدد را عینا قرار است استفاده کنیم.حال که ۳ بخش لازم برای تشکیل عدد اعشاری ما به دست آمد، باید طبق قاعده‌ی IEEE 754 جلو برویم:عدد اعشاری را با نمایش علمی تشکیل می‌دهیم:( 1.F ) * 2 ^ (e)که F همان fraction ماست :0100000000000000000000و e همان exponent ماست که ۴ است.در این مرحله عدد ما این است:(1.01) * 2 ^ (4)توجه داریم که صفرهای بعد از اعشار حذف شدند.حالا برای تبدیل نماد علمی به مبنای ۲ معمولی، کافیست علامت ممیز را 4 رقم به سمت راست حرکت دهیم:(1.01) * 2 ^ (4) = 10100عدد ما یک عدد صحیح شد (چون رقمی بعد از ممیز ندارد)این عدد همان جواب ما در قسمت b استبرای تبدیل به مبنای 10 یا دسیمال به این صورت عمل می‌کنیم:از سمت راست رقم ها را در توان متناسب از ۲ ضرب می‌کنیمبرای اولین رقم ضربدر  ۲ به توان ۰ ، برای دومین رقم ۲ به توان ۱ و الی آخر(0) * 2 ^ (0) = 0(0) * 2 ^ (1) = 0(1) * 2 ^ (2)  = 4(0) * 2 ^ (3) = 0(1) * 2 ^ (4) = 16اکنون با جمع کردن 16 و 4 به عدد در مبنای 10 می‌رسیم: 20همچنین توصیه می‌کنم برای توضیح بیش‌تر سیستم IEEE 754 این مطلب :‌https://www.geeksforgeeks.org/ieee-standard-754-floating-point-numbers/و این ویدیو را ملاحظه بفرمایید.https://youtu.be/TZI6Fd4WmIs</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Tue, 06 Apr 2021 10:57:30 +0430</pubDate>
            </item>
                    <item>
                <title>نقطه‌فایل ها (مروری بر dotfile یا دات‌فایل)</title>
                <link>https://virgool.io/@rsharifnasab/dotfiles-emhmfto0nijw</link>
                <description>شاید تا کنون اصطلاح دات‌فایل یا dotfile را شنیده باشید. این عبارت بیشتر بین کاربران حرفه‌ای تر لینوکس/یونیکسی شایع است. در این مطلب می‌خواهم یک مبتدی (با احتمالا لینوکس) را با این مفهوم آشنا کنم.کمی مقدمه در مورد پسوند فایل‌هابه طور سنتی می‌دانیم، فایل‌‌ها شامل اسم و پسوند هستند. مثلا فایل simple.txt یک فایل متنی است (از روی پسوند txt فهمیدم) که نام simple را دارم. یا مثلا فایل document.doc یک فایل مربوط به واژه‌پرداز ورد است که اسم document دارد. همچنین فرمت rtf برای متن غنی، ppt برای اسلاید‌های نرم‌افزار پاورپوینت و ..اما در سیستم‌عامل‌های بهتر از ویندوز، این پسوند فایل ارزش چندانی ندارد و ما‌ می‌توانیم یک فایل با نام filename داشته باشیم. بدون هیچ پسوندی. از روی ساختار فایل مشخص می‌شود که این فایل، فایل متنی‌است یا فیلم است یا ... ما در این مطلب با فایل‌های متنی کار داریم. فایل‌هایی مثل سورس‌کدها در زبان‌های برنامه‌نویسی، فایل‌های کانفیگ و تنظیمات و ... دات قبل از اسم فایلدر سیستم‌عامل‌های لینوکس/یونیکسی، نقطه، فقط جداکننده‌ی اسم فایل از پسوندن نیست. بلکه اگر در ابتدای اسم فایل قرار بگیرد، باعث می‌شود آن فایل حالت مخفی پیدا کند. جالب نیست؟ به جای اینکه برویم properties -&gt; general -&gt; mark as hidden، فقط به اول اسم فایل/فولدر، یک نقطه اضافه می‌کنیم و فایل به صورت هاید در می‌آید. برای دیدن فایل‌های مخفی می‌توانیم از ls -A استفاده کنید یا در فایل‌منیجر، ctrl h را بزنید. اما آیا هر فایل مخفی‌ای dotfile است؟نه! دات‌فایل‌ها اگرچه فایل‌های مخفی هستند، ولی به عنوان کاربر هر فایل را مخفی کنیم دات فایل محسوب نمی‌شود. بلکه دات‌فایل‌ها فایل‌های تنظیمات خاصی هستند که از قضا برای اینکه به چشم کاربر عادی نیایند، به صورت مخفی قرار می‌گیرند.پس تا اینجا فهمیدیم که دات‌فایل‌ها فایل‌های تنظیمات ابزار‌های مختلف هستند  اول اسمشون نقطه میاد که به معنی مخفی‌بودن فایل است.مثال: bashrc.فکر می‌کنم اولین دات‌فایلی که کاربران با آن برخورد می‌کنند، همین فایل bashrc. باشد. اسم فایل bashrc است و یک نقطه قبلش دارد. (برای اینکه در ویرگول درست بیفتد من نقطه رو بعدی تایپ کردم.)در این فایل، کاربران (یا احیانا برنامه‌ها) تنظیمات مربوط به برنامه‌ی bash (که شل پیش‌فرض اکثر لینوکس‌هاست) را می‌ریزند. از کلمه‌ی bash در اسم bashrc هم می‌توان فهمید که به برنامه‌ی کاربردی bash مربوط است.این فایل در پوشه‌ی ~ (همان home) هر کاربر وجود دارد. برای مثال فایل bashrc. من در اینجاست:حالا من اگر بخواهم تنظیماتی برای bash ام اعمال کنم باید این فایل را چه تفییری بدهم؟ آیا باید یک برنامه نصب کنم که این کارها را انجام دهد؟ آیا باید در setting خود bash بروم و تنظیماتی را عوض کنم یا در این فایل سیو شود؟ نه. نکته‌ی جذاب همینجاست. فایل‌های کانفیگ چطوری برنامه‌ها را تنظیم می‌کننددو مورد را باید بررسی کنیم. اول اینکه برنامه‌های موجود چطوری فایل‌های کانفیگ (مثلا همین bashrc) را می‌خوانند. دوم اینکه این‌ فایل‌ها را چطوری باید تغییر دهیم؟ دستی یا با کمک یک ابزار؟ در مورد bashrc داستان ساده است. همانطور که می‌دانید bash یک پوسته‌ی اجرا کننده دستور (shell) است. کارش اجرا کردن دستور (یا به صورت interactive یا اسکریپت‌های نوشته شده در فایل) است. این bashrc هم از قضا یک اسکریپت است. اسکریپتی که در آن دستورات محتلف bash نوشته شده. با هربار اجرای bash (البته فقط به صورت تعاملی)، فایل bashrc. خوانده و خط‌به‌خط اجرا می‌‌شود. این دستورات را کاربر حرفه‌ای به گونه‌ای می‌نویسد که در نتیجه‌ی اجرا کردن آن‌ها، bash تنظیمات موردنظرش را می‌گیرد. در واقع تنظیمات‌ شامل یک سری دستور bash است که تغییراتی را با هربار اجرای bash روی آن اجرا می‌کند.یک مقدار واضح تر مثال بزنم، هربار که یک ترمینال جدید باز می‌کنیم، باید shell هم درون آن اجرا شود. در صورتی که shell ما bash باشد، bash فراخوانی می‌شود. حالا bashی که اجرا شده حالت پیش‌فرض خودش (تنظیم‌نشده) را دارد. سپس (ر یک چشم به هم‌زدن) دستورات داخل bashrc. را می‌خواند و اجرا می‌کند و در نتیجه تنظیمات رویش اعمال می‌شود و کاربر bash تنظیم شده را می‌بیند. اما اگر یک دستور دیگر مثلا ls بزنیم، آیا باز هم همه آن دستورات اجرا می‌شوند؟ خیر. فقط زمانی که یک bash جدید ایجاد شود تنظیمات از فایل bashrc. خوانده می‌شوند.یک کار خوب و بی‌خطری که خوب است الان انجام دهید این است که فایل ,bashrc خود را با یک ادیتور باز کنید و تنظیمات داخلش را ببینید. (چیزی را حذف نکنید) احتمالا چند خط تنظیمات که سازندگان توزیع شما صلاح دیده‌اند آن جا نوشته‌شده‌است. اما چگونه تنظیمات bash را به کمک bashrc. می‌توان عوض کرد؟ به سادگی، همین مرحله قبل که فایل را با یک ادیتور متنی باز کردید، کافیست در انتها اضافه کنید echo salam. از این به بعد، (اگر فایل را سیو کنید) هربار bash جدیدی باز شود، salam هم چاپ می‌شود و سپس آماده به کار می‌شود. همچنین می‌توانید مفید تر عمل کنید، مثلا یک alias اضافه کنید.چرا خود bash تنظیمات را در bashrc. ذخیره نمی‌کند؟ مثلا ما اگر در یک bash در حال اجرا، دستور alias را بزنیم و alias جدید اضافه کنیم، چرا خودش انتهای bashrc ذخیره نمی‌کند تا لازم نباشد خودمان آن فایل را تغییر دهیم؟ برای اینکه bash در طول اجرا ممکن است دستورات خیلی متفاوتی اجرا کند و حتی کاربر اشتباه کند و دستور نامناسبی وارد کند. اگر خود bash قرار باشد هر تغییری در آن می‌دهیم را در bashrc بنویسد، هر خطایی در طول کل حیات سیستم کرده باشیم در bashrc  خواهد بود! حتی می‌تواند بسیار بزرگ و کند شود. کلا این کار منطقی نیست. در عوض می‌توانیم از دستوری استفاده کنیم که وقتی یک alias داریم خودش آن را به انتهای bashrc اضافه کند. به صورت عمدی و آگاهانه! (مثلا یک رشته بسازیم و آن را با دستور &gt;&gt; به آخر bashrc. بریزیم.)مطالعه بیشتر در مورد کارکرد bashrc https://unix.stackexchange.com/a/129144 مثال غیر از bashrc همانطور که حدس می‌زنید، bash تنها ابزاری نیست که احتیاج به تنظیم شدن دارد. ادیتور‌های خوب (مثل vim و emacs) شل‌های غیر از bash مثل zsh و fish، ابزار محبوب tmux و حتی ابزارهای گرافیکی مثل window manager تنظیمات شروعشان را از dotfileها می‌خوانند. (بعضی از ابزارهای گرافیکی اگر وارد settingشان شوید، خودشان فایل کانفیگشان را ادیت می‌کنند.)مثال‌هایی که من با آن‌ها کار کرده‌ام:فایل vimrc. فایلی است که ادیتور vim موقع اجرا، آن را می‌خواند. این فایل به زبان vimscript نوشته شده و در آن با اعمال دستوراتی می‌توان کاملا رفتار این ادیتور را شخصی‌سازی کرد.پوشه‌ی emacs.d. پوشه‌ی حاوی فایل‌های کانفیگ ادیتور emacs فایل config/nvim/init.vim. فایل اصلی تنظیمات ادیتور neovim است، همه تنظیمات ابزار neovim در پوشه‌ی nvim واقع در پوشه‌ی config. قرار دارند. بسیار از برنامه‌ها تنظیماتشان را به پوشه‌ی config. منتثل کرده‌اند. فایل ghci. شامل تنظیمات شل ghci (کامپایلر هسکل) است.مدیریت dotfileهاقدم اول فهمیدن کارکرد dotfileها بود و آن را با موفقیت پشت سر گذاشتیم، اما کاربران حرفه‌ای‌تر چگونه این کار‌ها را انجام می‌دهند؟ جالب است بدانید که خیلی از دات‌فایل‌ها به هزاران خط می‌رسد و معمولا کاربر در طول زمان بر خسب نیاز خودش برای برنامه‌های مختلفی که استفاده می‌کند، این تنظیمات را انجام داده. خیلی اوقات این تنظیمات در نتیجه‌ی یک سرچ بوده تا یک مشکل خاص را حل کنیم. پس خوب است در کنار خود تنظیم، کامنتی بگذاریم که این خط چه کار می‌کند، چرا اضافه شده یا مثلا به صفحه استک‌اورفلو یا ایشوی گیتهابی که باعث حل مشکل شده اشاره کنیم.همچنین خوب است آینده‌نگری کنیم. اگر خواستیم روی یک سیستم دیگر کار کنیم باید همه هزار خط را از اول بنویسیم؟ یا باید قسمت‌های مهم را جدا کنیم؟ خیلی از کاربران دات فایل‌های خود را در گیت‌هاب/گیت‌لب می‌گذارند تا بتوانند به راحتی به آن دسترسی داشته‌باشند. در نتیجه از گیت هم استفاده می‌کنند و تاریخچه تنظیمات قبلی خود را هم دارند. البته باید دقت کنید که در home خود git init نکنید و کل home (شامل فیلم و عکس و زندگی و ..) را کامیت کنید. بلکه فقط فایل‌های کانفیگ را commit کنید!از اینجا به بعد راه‌های بسیار متفاوتی وجود دارد و هر کاربر بسته به سلیقه و تسلطی که روی ابزارهای مختلف دارد، مکانیسمی برای مدیریت دات‌فایل‌ها درست می‌کند (یا اصلا نمی‌کند.) مثلا راه ساده‌‌ای که برای شروع مناسب است، این است که یه ریپوزتوری داشته باشیم و هربار bashrc را تغییر می‌دهیم، یکبار هم در آن پوشه کپی کنیم و کامیت/پوش کنیم. همچنین استفاده از ابزار rsync هم خیلی اوفات کمک‌کننده است. کاربران حرفه‌ای‌تر اسکریپت‌های پیچیده‌ای می‌نویسند که با هر بار تغییر، چه چیزهایی باید آپدیت شود، یا مثلا اسکریپت‌های نصاب. با استفاده از این اسکریپت می‌توانند هربار روی سیستم جدیدی می‌نشینند، یک دستور را اجرا کنند و همه تنظیماتشان دانلود شود و برنامه‌های مورد نیاز نصب شود و مثل لپتاپ خودشان احساس راحتی داشته باشند! اما باید دقت داشت که برای شروع اصلا نیازی به این کارها نیست و قرار نیست خودمان را اذیت کنیم. الگوگیری از dotfileهای خوبیرای شخص من، خواندن دات‌فایل‌ها و تنظیمات کاربران دیگر، برای ابزارهایی که هرروز استفاده می‌کنم خیلی هیچان انگیز و جالب بود و مطالب جدیدی زیادی یاد گرفتم و با آن‌ها dotfiles خودم را بهتر کردم. برای همین چند نمونه که به نظرم نمونه‌های تمیز و زیبایی بودند را در اینجا قرار می‌دهم تا شما هم استفاده کنید.مثلا همین نکته جالب را می‌توان در نظر داشت که هر کدام از این عزیزان روش‌های متفاوتی برای آپدیت کردن دات‌فایل ها انجام داده‌اند.  https://github.com/1995parham/dotfiles  https://github.com/MCSH/dot-files  https://github.com/mo1ein/My-dotfiles  https://github.com/hoomanist/dotfiles کپی نکنیداین جمله‌هام،شاید شبیه حل تمرین یه درس برنامه‌نویسی به نظر بیاد که میگم از کدهای دیگران روی اینترنت کپی نکنید، ولی بهم گوش بدید! البته یکی از مزیت‌های گذاشتن دات‌فایل‌ها در گیت‌هاب، دیدن کدها/تنظیمات بقیه  و الگو گرفتن است، ولی اینکه عینا تنظیمات یک شخص دیگر را کپی کنیم اصلا کار صحیحی نیست. چرا که هیچ‌ ایده‌ای نداریم چرا این تنظیمات وجود دارد، چطوری باید از ابزار تنظیم شده استفاده کنیم، و حتی بدتر، این تنظیمات مطلوب ما نیست و نمی‌دانیم چطوری تبدیل به مطلوب خودمان کنیم. بهترین روش (از نظر من) این است که با یک کانفیگ خیلی مختصر شروع کنیم و رفته رفته با سرچ‌ مشکلاتمان و بررسی دات‌فایل‌های دوستان، کم کم تنظیمات کامل‌تری برای خودمان داشته باشیم.باز هم تاکید میکنم کورکورانه کار نکنید، خیلی مهم است که بدانیم هر تنظیم برای چی وجود دارد و اگر نباشد چه اتفاقی می‌افتد و چه گزینه‌های دیگری در دسترس است.مطالعه بیشتر:ویکی‌پدیا https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory#Unix_and_Unix-like_environments معرفی کلی ابزار و به‌روش در مورد dotfileها https://dotfiles.github.io/ </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Fri, 19 Feb 2021 15:52:38 +0330</pubDate>
            </item>
                    <item>
                <title>ساخت رندوم در کامپیوتر چگونه است؟</title>
                <link>https://virgool.io/javacup/random-generate-jnwpgxv84hgf</link>
                <description>کامپیوترها ماشین‌حساب‌های خیلی خوبی هستند. با سرعت و دقت محاسبات انجام می‌دهند و جواب را به ما نمایش می‌دهند یا در شبکه ارسال می‌کنند. در طی این سال‌ها در محاسبه بهتر و بهتر هم شده‌اند. حتی با پیشرفت هوش مصنوعی، در حد انسان و بهتر تصمیم‌گیری می‌کنند، انتخاب می‌کنند و بازی می‌کنند. اما یکی از کارهایی که برای کامپیوترها خیلی سخت بوده و هست، تاس انداختن است! دقیق‌تر بگم، ساخت عدد تصادفی. توزیعِ یکنواختزود، تند، سریع  یک عدد بین ۱ تا ۱۰ انتخاب کن. انتخاب کردی؟ چند بود؟‌ ۱؟ شاید هم ۱۰. ولی موفق شدی درسته؟ اما آیا این انتخاب کردنت «تصادفی» بود؟ اگر دوباره بگویم انتخاب کن ممکن است همین عدد را بگویی؟ یا حتما یک عدد دیگر را انتخاب می‌کنی؟ یا ممکن است هر دو حالت اتفاق بیفتد؟می‌خواهم در مورد «توزیع یکنواخت» صحبت کنم. اسم انگلیسیش سخت‌تر هم هست ولی نکته پیچیده‌ای ندارد. کل موضوع به این بر می‌گردد که اگر تعداد خیلی زیادی تاس انداختیم، تعداد ۱ ها با ۲ ها با ۳ ها تا ۶ ها برابر باشند. به بیان دیگر «احتمال آمدن هر یک از حالت‌های دامنه» برابر باشد. در کتاب آمار و احتمال دبیرستان (اگر هنوز چنین کتابی باشد و به دبیرستان همچنان دبیرستان بگویند) از واژه‌ی «تاس سالم» استفاده می‌کرد. یعنی تاسی که ۶ وجه (۶ حالت مختلف خروجی) دارد و احتمال آمدن هر یک از وجه‌ها دقیقا ۱/۶ است. سوال پیش می‌آید که تاس غیر سالم چگونه است؟ فرض کنید یکی از وجه‌ها را سنگین کنیم. در این حالت احتمال بیش‌تری وجود دارد که آن وجه تاس به سمت زمین بیاید و وجه مخالف احتمال بیشتری برای بالا آمدن دارن. این یک تاس غیر سالم است! یا مثلا سکه‌ای را فرض کنید که به ازای ۱۰۰ بار پرتاب، ۸۰ بار شیر بیاید و ۲۰ بار خط. چنین سکه‌ای مورد قبول است؟ البته می تواند مورد قبول باشد ولی اکثر اوقات می‌خواهیم توزیع یکنواخت داشته باشیم، یعنی احتمال شیر آمدن با خط آمدن یکسان باشد. در حالت اغراق شده‌ی غیر یکنواخت، می‌توان تاسی را فرض کرد که همواره ۱ می‌آورد. یا سکه‌ای که فقط و فقط شیر می‌آید. (مثل سکه‌ی هاروی‌دنت قبل از اینکه یک طرفش بسوزد!)غیرقابل پیش‌بینی بودناصلا برای چه تاس می‌اندازیم؟ برای اینکه نتیجه را ببینیم. وگرنه مگر بیکاریم در صورتی که جواب را می دانیم تاس بیندازیم. یکی از خاصیت‌های مهم عدد رندوم (که از اسمش هم بر میاد!) غیر قابل پیش‌بینی بودن است. فرض کنید قرار است یک بازی دو نفره بکنید. قرار است طرف مقابل یک عدد حدس بزند‌ (یا الگویی را مشخص کند) و شما آن را کشف کنید. حالا فرض کنیم این رندوم انتخاب کردن، حالت یکنواختی خوبی دارد و یه بیانی، احتمال انتخاب حالت های مختلف آن یکسان است. آیا همین کافیست؟مثلا نظرتان درباره بازیکنی که در مقابل شما عدد‌های زیر را انتخاب کند چیست؟۱ - ۲ - ۳ - ۴ - ۵ - ۶ - ۷ - ۸ - ۹ - ۱۰ - .. این بازی اصلا هیجانی دارد؟ نه! چون قابل پیش‌بینی است. همین موضوع برای تاس و سکه هم برقرار است. در کاربردهای رندومِ کامپیوتر بسیار مهم‌تر هم می‌شود. مثلا فرض کنید password generatorی که نصب کرده‌اید، پسوردی را برایتان تولید کند که هکر محترم بتواند آن را حدس بزند. به نظر موضوع مهمی می‌آید. خاصیت دوم رندوم خوبی که کامپیوتر باید تولید کند، غیر قابل پیش‌بینی بودن است. شاید حتی در کاربردهای رمزنگاری از خاصیت اول مهم‌تر هم باشد. کامپیوترِ قابل پیش‌بینیمشکل بزرگ اینجاست که کامپیوتر قابل پیش‌بینی است. اصلا کامپیوتر را طراحی کردیم که قابل پیش‌بینی باشد و محاسباتی که جوابش را می‌دانیم سریع تر انجام دهد. پردازنده مرکزی به عنوان مغز متفکر کامپیوتر هیچ‌گونه تصادفی‌بودن (randomness)ی ندارد. دستوری که باید اجرا کند را می‌گیرد، اجرا می‌کند و جواب را در جای مربوطه ذخیره می‌کند. این دستورات هم از ضرب و جمع  و منها خارج نیست. رم و مادربورد هم کارشان را انجام می‌دهند. کار هیچ کدام تاس انداختن نیست. آیا می‌شود الگوریتمی طراحی کرد که با استفاده از جمع و منها و ضرب و ..، عدد تصادفی تولید کرد؟ پاسخ «خیر» است. اما احتمالا می‌گویید نه من با پایتون/سی/جاوا/هرزبانی عدد رندوم ساخته‌ام، هر دو خصوصیتی که ذکر کردی هم داشت، پس داستان چیست؟ به داستان هم می‌رسیم، اصلا هدف مطلب توضیح چگونگیِ کارکرد همان تابع است.برگردیم به طراحی الگوریتم‌مان. آیا می‌شود با جمع و منها و ضرب و تقسیم الگوریتمی ساخت که خروجی رندوم بدهد؟ متاسفانه از «الگوریتم» هم انتطار داریم قطعی عمل کند. اصلا فرض کنید دوستمان ادعا کرد چنین الگوریتمی نوشته، باید پرسید ورودی الگوریتم چیست؟ اولن جمع/منها را با چه چیزی شروع می‌کنی؟ با یک عدد ثابت؟ اما همین الگوریتمی که دوستمان نوشته را دور نیندازیم، آیا می‌شود یک ورودی بهش بدهیم و خروجی رندوم از آن انتظار داشته باشیم؟ به شرط رندوم بودن ورودی بله! خب اگر ورودی رندوم داشتیم که چه کاری بود الگوریتم بنویسیم؟ موضوع این است که می‌توان بازه خروجی را در محدوده خاصی قرار داد (مثلا اگر کاربرد تاس داریم برد تابع ۱ تا ۶  شود) یا حتی حالت uniform بودن به آن بدهیم. به این الگوریتم‌ها «الگوریتم ساخت شبه‌رندوم» گفته می‌شود (PRNG)، یکی از مشهورترین (و البته بین جدیدها) مِرسِم توئیستر  نام دارد. این پاسخ هم می‌تواند مفید باشد.ورودی الگوریتم‌های ساخت شِبه‌رندومتا اینجا گفتیم کامپیوتر قابل‌ پیش‌بینیست، نمی‌تواند به تنهایی عدد تصادفی تولید کند. دوستمان ادعا کرد که الگوریتمی نوشته که عدد تصادفی تولید می‌کند ولی از او پرسیدیم «ورودی الگوریتمت چیه؟». احتمالا جواب داد که الگوریتم تولید رندوم، یک ورودی رندوم می‌خواهد ولی می‌تواند کیفیت رندومش را بالا ببرد. حالا به این الگوریتم چه رندوم بی‌کیفیتی بدهیم که کیفیت آن را بالا ببرد؟ به این ورودی seed گفته می‌شود. در واقع seed کلیدی برای حل مشکل رندوم نبودن کامپیوترهایمان است. مشکل به این صورت حل می‌شود که seed را به الگوریتم تولید عدد شبه‌تصادفی می‌دهیم و برایمان عدد شبه‌تصادفی درست می‌کند. (راستی چرا شبه‌تصادفی؟ چرا واقعا تصادفی نه؟)اما سوالمان همچنان باقیست. seed را چه مقداری بگذاریم تا رندوم خوبی بگیریم؟ اگر با سی عدد رندوم ساخته باشید، معروف است که می‌گویند برای ساخت رندوم باید این دو دستور را بنویسی:srand(time(0));
long random = rand() % limit;
الان می‌بینیم srand معنی‌اش ررا از کجا گرفته. در واقع داریم به برنامه‌ی سی خودمون می‌گوییم که برای seed رندوم‌ات از time(0) استفاده کن. بعد از تابع رندوم را صدا کرده‌ایم. خروجی تابع time(0) چیست؟ زمان کنونی سیستم به واحد ثانیه از اول ژانویه ۱۹۷۰. (اول ژانویه ۱۹۷۰ برای سی و یونیکس تاریخ مهمیست.) در این حالت، سیستم زمان کنونی خودش را نگاه می‌کند و از آن به عنوان seed برای تابع رندوم استفاده می‌کند. مشکل حل شد؟ «تقریبا» به بیانی الان به این حالت رسیدیم که اگر امروز تابع رندوم را فراخوانی کنیم یک جواب می‌دهد. اگر یک ساعت بعد فراخوانی کنیم یک جواب دیگر می‌دهد. اگر فردا فراخوانی کنیم باز هم یه جواب دیگر می‌دهد. همین را می‌خواستیم مگر نه؟ جواب‌ها هم به صورت یکنواخت توزیع‌شده هستند. یعنی احتمال ۱ آمدن تاس با ۲ آمدن برابر است. ساخت یک عدد رندوم یا ساخت یک دنباله رندوم؟بیایید فرض کنیم یک seed معین به تابع داده‌ایم. بار اول rand() را صدا می‌زنیم. جواب چه می‌شود؟ مثلا ۵. بار دوم صدا بزنیم چه اتفاقی می‌افتد؟ باز هم از همان seed استفاده می‌کند و باز هم همان ۵ را خروجی می‌دهد؟ اگر بله، می‌توانیم srand(time(0)) را دوباره  صدا کنیم تا seed جدیدی بدهد و رندوم دومی ۵ نشود؟ احتمالا نه چون از بار قبلی که صدا کردیم یک ثانیه نگذشته. نمی‌شود که هر دفعه یک ثانیه صبر کنیم تا رندوم بعدی را بسازیم.برای حل این مشکل، توابع ساخت شبه‌رندوم، یک حالت داخلی در خود نگه می‌دارند. به این معنی که چیزهای محدودی از بار قبل که اجرا شدند یادشان هست. مثلا می‌توان فرض کرد که رندوم دفعه قبلی که ساخته‌اند را به عنوان seed به رندوم این دفعه می‌دهند. به این صورت مشکل گفته‌شده حل می‌شود. یا می‌شود ساده‌تر نگاه کرد، برای بار دوم رندوم ساختن از seed+1 استفاده می‌کنند و برای بار سوم از seed+2. هر چه که باشد، فرض می‌کنید که seed ما باعث تولید و شکل‌گیری «فقط یک عدد رندوم» نمی‌شود. بلکه دنباله‌ای نامتناهی از اعداد تصادفی با استفاده از همین seed اولیه قابل ساخت هستند. مشکلات استفاده از زمان به عنوان seedبیاید فرض کنیم در سراسر کشور، خودپردازهایی وجود دارند که سر ساعت ۲ صبح که تراکنش کم‌تر است، یک عدد تصادفی تولید می‌کنند و یک‌سری کار بر اساس عدد تصادفی انجام می‌دهند. همه از یک زبان برنامه‌نویسی و کتاب‌خانه‌های رندوم و سخت‌افزارهای مشابه استفاده می‌کنند. بدیهی‌است که زمانشان هم با دقت بسیار زیادی با هم سینک شده و زمان یکسانی دارند. خب فکر می‌کنید نتیجه رندوم ساختن چی خواهد بود؟ همه‌ی سیستم‌ها عدد تصادفی یکسانی خواهند ساخت! چرا؟ چون برای seed از یک مقدار مشخص (زمان آن لحظه) استفاده کردیم که بین همه مشترک بود. الگوریتم ساخت رندوم هم که بینشان یکی بود. جواب هم یکی در آمد. منطقی ولی ترسناک! همانطور که دیدیم در شرایطی خاص، ممکن است عددمان اصلا خاصیت تصادفی نداشته باشد! حتی غیرقابل پیش‌بینی هم نیست. اگر n دستگاه خودپرداز ما در سراسر کشور این عدد را فهمیده‌اند، هکر عزیز هم می‌توان همان زمان را به seed رندومش بدهد و عدد ما را پیدا کند. اما از این تهدید (که جلوتر روش‌های حلش را هم بررسی می‌کنیم) به عنوان فرصت استفاده می‌کنیم. برای دیباگ برنامه‌هایی که رندوم تولید می‌کنند، می‌توانیم از seed ثابت استفاده کنیم. در این صورت مطمئنیم اگر یه باگ رخ داده، به ازای «دنباله رندوم ساخته شده توسط آن seed» بوده، پس اگر seed را همان بگذاریم حتما باز هم رخ می‌دهد و می‌توانیم باگ را پیدا کنیم. مثلا عجیب نیست که موقع دیباگ نوشته باشیم: srand(5) تا خاصیت رندوم‌بودن را از تابع رندوم بگیریم و بتوانیم دیباگش کنیم.چرا «شبه‌رندوم»؟ مشکلات این رندوم را چطوری حل کنیم؟از اول مطلب چندبار به عبارت شبه‌رندوم (Pseudorandomness) اشاره کردم. شبه‌رندوم یعنی دنباله رندوم‌هایمان اگرچه به نظر بی‌قاعده می‌آیند ولی با داشتن seed، کاملا قابل حدس زدن هستند. برای بسیاری از کاربردها همین رندوم مناسب است. همان بازی فکر بکری که اول مطلب مثال زدم، یا انواع بازی‌های کامپیوتری با همینقدر رندوم بودن مشکلشان حل می‌شود. حتی خودپردازهای مذکور را هم می‌توان مشکلشان را حل کرد، مثلا به جای ثانیه‌های گذشته از 1970، ثانیه‌های گذشته از آخرین ریبوت را ورودی بدهیم. یا ترکیبی از زمان ساخت، اسم کوچک آخرین تعمیرکار، کد نزدیک‌ترین شعبه و البته زمان فعلی می‌تواند مشکل را حل کند. بازهم رندوم قابل حدسی داریم ولی با باقی سیستم‌ها متفاوت است.برای قسمت کوچک (ولی مهم)ی از کارها، نیاز به رندوم واقعی داریم. رندومی که هیچ‌کس نتواند حدس بزند، نه هکر، نه تعمیرکار قبلی، نه کسی که پشت سیستم نشسته است.  «کاربردهای عدد تصادفی در رمزنگاری» معمولا به رندوم واقعی نیاز دارند تا آن رمزنگاری قابل اتکا باشد. پس رندوم واقعی چطوری تولید می‌شود؟ یک اپراتور استخدام می‌کنند تا مدام تاس بیندازد و عدد تاس را وارد کند؟ آنتروپی: ساخت رندوم واقعی!تا اینجا، هر رندومی که می‌ساختیم «شبه رندوم» بود. با استفاده از یک seed مشخص و الگوریتم معینی به جواب‌های قابل پیش‌بینی‌ای می‌رسیدیم. اما اگر یک seed واقعا رندوم به الگوریتم بدهیم چه می‌شود؟ در این صورت واقعا رندوم خواهیم داشت. ورودی غیر قابل پیش‌بینی به خروجی غیرقابل پیش‌بینی منجر می‌شود. اما کامپیوتر با توصیفاتی که در پاراگراف اول از آن کردم، چطوری قرار است رندوم واقعی بسازد؟ با استفاده از آنتروپی سیستم. (دقت کنید ورودی غیرقابل پیش‌بینی یعنی کسی هم به آن دسترسی نداشته باشد و private باشد)آنتروپی یعنی بی‌نظمی. سیستم کامپیوتری چطوری بی‌نظمی دارد که برای رندوم ساختن از آن استفاده کند؟ از سخت‌افزارش! مثلا صدای فن یا کارکردهای هارددیسک یا حتی حرکات موس. این‌ها ارتباطات سیستم با دنیای بی‌نظم بیرون هستند و باعث می‌شوند کامپیوتر هم بی‌نظمی را تجربه کند. حتی قطعات سخت‌افزاری برای تولید رندوم وجود دارند که رندوم واقعی و امن تولید می‌کنند. احتمالا حدس می‌زنید که برای سرورهایی که عملیات رمزنگاری سنگین انجام می‌دهند توجیه اقتصادی دارد.لینوکس برای استفاده از این آنتروپی، فایل‌های /dev/random و /dev/urandom را معرفی کرده‌است. تفاوت چیست؟ فرض کنید سیستم به اندازه کافی آنتروپی ندارد ولی ما درخواست داده‌ایم که ۱۰ بایت رندوم تولید کند. چه اتفاقی می‌افتد؟ از همین آنتروپی کم استفاده می‌کند و رندوم بی‌کیفیتی می سازد یا صبر می‌کند آنتروپی کافی داشته باشد تا رندوم با کیفیت مطلوب به ما بدهد؟ هردو امکان را داریم. حالت اول (صبر نمی‌کند ولی کیفیت را هم تضمین نمی‌کند) می‌شود فایل /dev/urandom. این به رندوم non blocking هم می‌گویم چون معطل نمی‌شود. حالت دوم که ممکن است مقدار زیادی صبر کند ولی رندوم با کیفیت می‌دهد /dev/random است.در سروری که امنیت و رمزنگاری مهم‌ باشد قاعدتا باید از /dev/random استفاده شود ولی اگر کیفیت رندوم خیلی برایمان اهمیتی ندارد همان /dev/urandom هم می‌تواند کافی باشد. جالب است بدانید که یکی از شیوه‌های کند کردن (و حتی از دسترس خارج کردن) سرور خودمان واداشتنش به تولید مقدار زیادی عدد تصادفی است. در این حالت تمام منابع سیستم آزاد هستند ولی چون سیستم آنتروپی کافی ندارد بلاک می‌شود و صبر می‌کند تا آنتروپی تولید شود. </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 24 Dec 2020 00:21:13 +0330</pubDate>
            </item>
                    <item>
                <title>فعال کردن warning‌ها در gcc</title>
                <link>https://virgool.io/@rsharifnasab/%D9%81%D8%B9%D8%A7%D9%84-%DA%A9%D8%B1%D8%AF%D9%86-warning%D9%87%D8%A7-%D8%AF%D8%B1-gcc-td07ijxnfcrz</link>
                <description>وارنینگ‌ها اصلا چی هستند؟زمانی که برنامه‌مون رو کامپایل می‌کنیم، کامپایلر (مثلا gcc یا mingw یا clang یا ...) میاد برنامه رو می‌خونه و از روش فایل قابل اجرا (exe یا out) رو تولید می‌کنه. حالا در این بین، یه چیزایی رو می‌بینه که طبق قواعد زبان (سینتکس) مشکلی نداره و باعث ارور نمیشه، ولی به نظرش می‌رسه که کسی که چنین چیزی نوشته حواسش نبوده و احتمالا باعث مشکل می‌شه.مثال بزنم. کد زیر رو در نظر بگیرید:int main(){
  int a;
  a++;
  return 0;
}به لحاظ سینتکس هیچ مشکلی نداره و کامپایل میشه. حتی اجرا هم میشه. ولی gcc حدس می‌زنه که وقتی شما متغیر a تعریف کردی و بهش مقدار ندادی، ++ کردنش عملا کار بی‌معنی‌ایه. چون اصلا نمی‌دونی چی توش هست که بخوای بخونیش و یکی بهش اضافه کنی.حالا چه خوب بود که خود gcc چنین چیزی می‌نوشت برامون:warning: ‘a’ is used uninitialized in this function [-Wuninitialized]
    3 |   a++;یا مثلا اگر متغیر تعریف کنی و «فقط مقدار توش بریزی» ولی «چیزی ازش نخونی» می‌تونه نشون‌دهنده این باشه که اون متغیر هیچ فایده‌ای نداره برای روال برنامه چون هیچوقت از مقدارش استفاده نشده.بازم تاکید میکنم که مشکل سینتکس نیست ولی «احتمالا برنامه نویس بی‌دقتی کرده».روش فعال کردن در gccدر حالت عادی کامپایلر این ارور‌ها رو برای ما نمی‌نویسه، یا اگر بنویسه یه تعداد خیلی خیلی کمی‌شون رو می‌نویسه. اما می‌تونیم با پاس دادن -Wall به کامپایلر، بهش بگیم که «همه اخطار‌ها رو نشون بده»یعنی در زمان کامپایل کردن، به جای gcc code.cبیایم بنویسم gcc -Wall code.c(دقت کنید W بزرگه ولی all کوچکه)نکات تکمیلی برای g++ هم دقیقا به همین صورته روند.این برخلاف اسمش «همه وارنینگ‌ها» رو فعال نمی‌کنه بلکه یه تعداد خوبیشونه. می‌تونید در کنار -Wextra استفاده کنید که تعداد بیشتری رو نشون بده. گاهی برنامه‌نویس‌ها علاقه دارن که زمانی که برنامه وارنینگ داره اصلا کامپایل نشه و اصطلاحا «وارنینگ‌ها به عنوان خطای کامپایل در نظر گرفته بشن» برای اینکار می‌تونیم از -Werror استفاده کنیم. با اینکار دیگه نمی‌نویسه وارنینگ بلکه می‌نویسه error و تا رفع نکنیم کامپایل صورت نمی‌گیره. all warnings being treated as errors(رفتار پیش‌فرض اینه که کامپایل میشه صرفا وارنینگ هم چاپ میشه) اگرچه برای زمانی که تست می‌کنیم و خودمون کد می‌نویسم -Werror گزینه خوبی به نظر میاد برای اینکه مجبورمون می‌کنه که به وارنینگ‌ها توجه کنیم، اما برای محیط عملی مثلا زمانی که کدمون رو می‌خوایم بفرستیم دست کاربرهای محتلف تا کامپایل کنن اصلا گزینه مناسبی نیست. حتی با فرض اینکه شما همه وارنینگ‌ها رو برطرف کرده باشید.چرا؟ چون وارنینگ  ها کامپایلر به کامپایلر و نسخه به نسخه متفاوت می‌شن. بنابراین ممکنه شما با gcc نسخه ۹ کامپایل کنید و تست کنید و همه چیز هم خوب باشه ولی بفرستید برای من ولی من که gcc نسخه ۱۰ دارم وارنینگ جدیدی بهش اضافه شده باشه و باعث بشه کد توی سیستم من کامپایل نشه در حالی که واقعا مشکلی نداره. (خودم برای کامپایل یه کد قدیمی به این مشکل خوردم!)</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 17 Dec 2020 14:05:40 +0330</pubDate>
            </item>
                    <item>
                <title>از سوییچ‌کیس تا پترن‌مچینگ</title>
                <link>https://virgool.io/@rsharifnasab/%D8%A7%D8%B2-%D8%B3%D9%88%DB%8C%DB%8C%DA%86%DA%A9%DB%8C%D8%B3-%D8%AA%D8%A7-%D9%BE%D8%AA%D8%B1%D9%86%D9%85%DA%86%DB%8C%D9%86%DA%AF-ltlpdic5yuxb</link>
                <description>به نظرم یکی از مغفول‌ترین ویژگی‌های زبان‌های برنامه‌نویسی، سوییچ کیس ها هستند. این عبارت ها در زبان‌های مختلف مثل سی و جاوا همواره با محدودیت‌هایشان وجود داشتند ولی بعدا تکامل پیدا کردند و قابلیت‌های جدید کسب کردند. همچنین بعد از مدتی الهام بخش چیزهای جدیدتر مثل سینتکس guard و pattern matching شدند. در این مطلب می‌خواهیم بیش‌تر با انواع آن‌ها و امکانات و محدودیت‌هایشان آشنا شویم. حتی از زبان‌هایی که این قابلیت را ندارند علت را جویا شویم و دید خوبی به دست بیاوریم!در آخر هم با نسل جدید سوییچ‌ها (pattern matching) آشنا می‌شویم.تصویر تزئینی است. سوییچ در زبان‌های برنامه‌نویسی ارتباطی به سوییچ شبکه و Nintendo switch نداردسوییچ‌کیس ها در C یا «چرا اصلا سوییچ کیس‌ها به وجود آمدند»در زمانی که صحبت از شرط در زبان‌های رویه‌ای می‌شود، ابتدا if و احتمالا عملگر سه‌عملوندی (ternary operator) به ذهن می‌آیند، در وهله بعدی سوییچ‌کیس‌ها گفته می شوند با یک قید که «با if هم می توان آن‌ها را پیاده‌سازی کرد». بله، البته که همه انواع شرط را می‌شود با if پیاده‌سازی کرد اما هر ساختار زبانی را بهر کاربردی ساختند. مثلا ساختارهای حلقه را می‌توان با recursion پیاده‌سازی کرد (به شرط tail call optimization از طرف کامپایلر) و برعکس. می‌توان حلقه و تابع را با goto جایگزین کرد. اما همه اینها آمده‌اند که به ما به عنوان برنامه‌نویس کمک کنند و کیفیت کد نوشته‌شده یا کامپایل شده را بهبود دهند.پس این سوییچ‌کیس‌ها که از آن‌ها حرف می‌زنم هم کاربردی دارند. این کاربرد را از دو جهت می‌توان بررسی کرد. در زمان نوشتن و ادیت کردن سورس‌کد و زمان بعد از کامپایل.در زمان کدنویسی، قرار است «خوانایی کد را بالا ببرد» یعنی برای کسی که کد را می‌خواند تصریح می‌کند که همکار، این عبارتی که من نوشتم فقط و فقط قرار است بر اساس مقدار‌های مختلف ورودی (که این مقدارها از پیش معلوم هستند) کارهای مختفلی انجام شود. قرار نیست اتفاق خاص و عجیبی بیفتد. از طرف دیگر وقتی سوییچ‌کیس وجود دارد و در برنامه if و else می‌بینیم، از خود می‌پرسیم چرا از خود سوییچ استفاده نشده است؟ چه اتفاق ویژه‌ای قرار است بیفتد که با خود سوییچ ممکن نبود؟ اما تنها کاربرد این نیست! کاربرد مهم‌تر این است که کدی که تولید می‌شود بهینه‌تر است. یعنی با فرض اینکه دو برنامه با منطق یکسان که یکی با سوییچ‌کیس و یکی با if else های متوالی نوشته شود، برنامه اول سریع‌تر کار می‌کند. (البته شاید تفاوت به چشم نیاید ولی ندیدن به معنی نبودن نیست.)دلیل این موضوع این است که کامپایلرها برای کامپایل کردن (و تولید کد ماشین) برای سوییچ کیس، تمهیدات خاصی در نظر گرفته‌اند و کدی که تولید می‌شود معادل if else نیست. قرار نیست در این مطلب وارد جزئیات کامپایلر و یا زبان ماشین شویم ولی خوب است همینقدر بدانیم که در صورتی که روی اعداد ۱ تا ۱۰ سوییچ زدیم و عدد ۱۰ را ورودی داده ایم، برابری های ۱==۱۰ و ۲ == ۱۰ و .. چک نمی‌شوند و یک‌راست به حالت ۱۰ == ۱۰ می‌رسیم در حالی که اگر با if else می‌نوشتیم باید این چک‌ها انجام می‌شد. این موضوع در زمانی که caseهای زیادی داریم به چشم می‌آید. کامپایلر با استفاده از lookup table کد سطح پایین تولید می‌کند و از قبل می‌داند که برای ورودی ۱۰ باید به کجا jump شود. مطالعه بیشتر: branch tableمطالعه بیشتر: مقایسه performanceهمچنین برای درک شهودی از روال اجرا این پاسخ هم کمک‌کننده است.محدودیت‌های سوییچ‌کیس‌های سنتیسوییچ‌کیسی که در C داشتیم، محدودیت‌های خودش را هم دارد، مثلا اینکه فقط امکان سوییچ زدن روی ورودی‌هایی با تایپ عدد صحیح داریم (کاراکتر هم عدد صحیح است!) و اینکه مقدار هر case باید در زمان کامپایل معلوم باشد وگرنه اصلا بهینه‌سازی ای ممکن نیست.switch(a){ // type: integer or character
     case 1: // known in compile time
           // some code
           break;
 }اما چرا برای اعداد اعشاری ممکن نیست؟ برای اینکه اعداد اعشاری به صورت ممیز شناور نگهداری می‌شوند و تساوی اصلا در در آن‌ها دقت ندارد و موضوعیت ندارد. بنابراین اینکه ورودی یک double یا float بدهیم و انتظار داشته باشیم در صورت که برابر 1.3 شد کار خاصی انجام شود، حتی با if هم نیازمند دقت فراوان است، چه برسد به switch case با نحوه کامپایل شدن خاصش.مطالعه بیشتر در مورد عدم دقت سیستم ممیز شناور در سیستم‌های مالی و همچنین این سایت در مورد محاسبات ممیز شناوردر نهایت خوب است به Fall-through نیز بپردازیم. کد زیر را در نظر بگیرید:    switch (n) {
        case 1:
            printf(&amp;quotthis is 1\n&amp;quot);
        case 2:
            printf(&amp;quotthis is 2\n&amp;quot);
        case 3: 
            printf(&amp;quotthis is 3\n&amp;quot);
        default:
            printf(&amp;quotdefault\n&amp;quot);
    }با ورودی ۱ این برنامه چه چیزی چاپ می‌کند؟ آیا فقط this is 1 چاپ می‌شود؟ خیر! بلکه همه دستورات از case 1 به بعد چاپ می‌شوند. یعنی خروجی این برنامه (با ورودی ۱) چنین چیزی است:this is 1
this is 2
this is 3
defaultبه این اتفاق Fall-through می‌گویند. در واقع دستورات داخل سوییچ کیس بر خلاف if، بلاک‌های جدا از هم نیستند، بلکه همه دستورات یک بلاک هستند که فقط بسته به ورودی، از جاهای مختلف (case های مختلف) وارد بلاک می‌شویم. خروج از بلاک به صورت خودکار نیست و باید برنامه‌نویس با استفاده از  دستور break تصریح کند. مثلا خروجی این برنامه به ورودی ۱، this is 1 است:    switch (n) {
        case 1:
            printf(&amp;quotthis is 1\n&amp;quot);
            break;
        case 2:
            printf(&amp;quotthis is 2\n&amp;quot);
            break;
        case 3: 
            printf(&amp;quotthis is 3\n&amp;quot);
            break;
        default:
            printf(&amp;quotdefault\n&amp;quot);
    }توصیه می‌شود که همیشه در آخر دستورات هر case، خودمان break قرار دهیم چون در غیر اینصورت بررسی برنامه و trace سخت می‌شود و ممکن است به باگ‌ بربخوریم. اما چرا اصلا این اماکان وجود دارد و خود C یه صورت پیش‌فرض break نمی‌کند؟دلیل این کار، فراهم کردن امکان «اجرای یک تکه کد برای caseهای مختلف است»به این مثال توجه کنید:    switch (n) {
        case 1:
        case 2:
        case 3: 
            printf(&amp;quotthis is 1 or 2 or 3\n&amp;quot);
            break;
        case 4: 
            printf(&amp;quot4\n&amp;quot);
            break;
    }این برنامه به ازای همه‌ی حالت های ۱ و ۲ و ۳، خروجی this is 1 or 2 or 3 را چاپ می‌کند. به این ترتیب می‌توانیم از زدن کد تکراری جلوگیری کنیم. اما آیا ارزشش را دارد؟ در بسیاری از زبان‌های جدید مثل go این قابلیت حذف شده و به صورت خودکار break می‌شود. و یا در نسخه‌های جدید gcc  وارنینگِ نگذاشتن break در آخر هر case اضافه شده که البته فقط برای caseهای غیرخالی نمایش داده می‌شود. برای جلوگیری از آن هم امکان تصریح fall-through در c++17 و تصریحی به شکل کامنت‌ برای gcc وجود دارد. (بله! کامپایلر می‌تواند کامنت‌های کد شما را هم بخواند)مطالعه بیشتر در مورد Fall-through: ویکیپدیاچرا پایتون سوییچ‌کیس ندارد؟در پایتون اهداف فرق دارد. پایتون معتقد است فقط یک راه حل برای یک مسئله وجود دارد. پایتون اصلا قرار نیست قبل از اجرا کامپایل بشود که بهینه‌سازی زمان کامپایلی انجام شود.به همان دلیل قبلی، case ها از زمان کامپایل معلوم نیستند.در پایتون اصلا const ها وجود ندارد و اگر هم سوییچ‌کیسی وجود داشته باشد باید همه case‌های آن اعداد جادویی باشند. با دلایل بالا، اگر هم سوییچ‌کیسی بخواهد وجود داشته باشد کارایی‌ای بهتر از if و  elif نخواهد داشت. پس اصلا زبان را ساده می‌کنیم و بی‌خیال می‌شویم! البته با توجه به سینتکس تمیز دیکشنری در این زبان، می‌توان علاوه بر if else از آن‌ها هم استفاده کرد که گزینه جالبی به نظر می‌آید. مثلا در این برنامه:def numbers_to_strings(argument): 
    switcher = { 
        0: &amp;quotzero&amp;quot, 
        1: &amp;quotone&amp;quot, 
        2: &amp;quottwo&amp;quot, 
    }   
    return switcher.get(argument, &amp;quotnothing&amp;quot) هر key در دیکشنری در نقش یک case عمل می‌کند و پارامتر دوم در get به جای default عمل می‌کند. به این معنی که «اگر key در دیشکنری نبود، به جایش nothing برگردان»همچنین عبارت‌های پرکاربرد و دوست‌داشتنی pattern matching در راه اضافه شدن به پایتون هستند، آن ها را معرفی خواهیم کرد.مطالعه بیشتر: انواع راه‌های پیاده‌سازی سوییچ‌ در پایتونبهبود‌های سوییچ‌کیس در جاواسوییچ‌کیسِ جاوا بسیار شبیه به سوییچ‌کیس در سی است ولی مثل خیلی از امکانات دیگر، این امکان هم مقداری بهبودیافته. قابلیت سوییچ‌ زدن روی رشته‌ها از  jdk7 هم فراهم‌شده است. بنابراین می‌توانیم کدی شبیه به این داشته‌باشیم:String s = &amp;quotsalam&amp;quot
final String kh = &amp;quotkhubi?&amp;quot
switch (s) {
            case &amp;quotsalam&amp;quot:
                System.out.println(&amp;quotalaik e salam&amp;quot);
                break;
            case kh:
                System.out.println(&amp;quotmersi&amp;quot);
                break;
            default:
                System.out.println(&amp;quot?&amp;quot);
        }در این حالت، هم امکان نوشتن مستقیم literal و هم استفاده از متغیرهای final String مجاز است. خوب است بدانیم که به کمک hashcode، این سوییچ‌کیس‌ها هم کارایی بالایی دارند و به if و equals ترجمه نمی‌شوند.مطالعه بیشتر: سورس‌کد کامپایلر جاوا!سوییچ‌کیس به عنوان expression اخیرا (در جاوا ۱۳) اضافه شده‌است. با استفاده از این قابلیت می‌توانیم از کل سوییچ‌کیس یک خروجی بگیریم و آن را استفاده کنیم! تفاوت statement و expression:به دستور‌های زبان برنامه‌نویسی، statement گفته می‌شود. مثلا if و while و تعریف یا فراخوانی تابع statement هستند. یک دسته از این statement‌ها قابل تبدیل به مقدار اند، مثلا متغیر a که مقداری داخلش دارد، قابل تبدیل به مقدار است. یا 2+2 قابل تبدیل به مقدار است. همچنین فرخوانی تابعی که مقدار برمی‌گرداند (void نیست) قابل تبدیل به مقدار است. به این عبارت‌ها expression می‌گویند. در زمینه شرط‌ها، if فقط یک statement است ولی ternary operator هم statement است و هم expression، چون خودش قابل تبدیل به مقدار (و مثلا چاپ یا assign) است.// use if (statement)
int a;
if(1 == 2){
   a  = 3;
} else {
   a = 4;
}

// use ternary operator (expression)
int a = (1==2)? 3 : 4;مطالعه بیشتر:‌ expressionهااما در جاوا ۱۳ شاهد این هستیم که خود عبارت‌های سوییچ می‌توانند مقدار برگردانند. مثلا به کد زیر نگاه کنید:var result = switch(month) {
    case JANUARY, JUNE, JULY -&gt; 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -&gt; 1;
    case MARCH, MAY, APRIL, AUGUST -&gt; 2;
    default -&gt; 0; 
};خود سوییچ تبدیل به مقدار شده. با استفاده از فلش تصریح کردیم که چه مقدار برگردد. برای مثال اگر month برابر JANUARY بود، کل سوییچ‌کیس به مقدار 3 تبدیل می‌شود. البته در حالت expression همچنان قابلیت نوشتن دستور را هم داریم. در این حالت با کلیدواژه yield می‌توانیم مقدار برگشتی را مشخص کنیم.var result = switch (month) {
    case JANUARY, JUNE, JULY -&gt; 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -&gt; 1;
    case MARCH, MAY, APRIL, AUGUST -&gt; {
        int monthLength = month.toString().length();
        yield monthLength * 4;
    }
    default -&gt; 0;
};به کلیدواژه yield دقت کنید.مطالعه بیشتر در باره switchها و بهبود‌هایشان در جاوا: baeldungهمچنین خوب است بدانیم که در حالتی که به عنوان expression استفاده شود، باید حتما case‌های ما، تمام حالت‌های ممکن را پوشش دهند (یا عبارت default داشته باشیم) چرا که نمی توان گفت اگر فلان حالت به وجود آمد، هیچ چیز برنگردان! یک expression باید حتما در تمام حالات قابل تبدیل به مقدار باشد. تطبیق الگو: کمالِ سوییچ‌کیس‌هاتوجه: مرتبط و شبیه دانستن این دو ساختار در متون علمی اشاره نشده و نظر نویسنده است. به لحاظ زمانی لزوما یکی بعد از دیگری نیست و به طور مجزا در خانواده‌های مختلف زبان‌ها وجود داشته‌اند.)تطبیق الگویی که در اینجا مطرح می‌شود ارتباطی به تطبیق الگو در رشته‌ها (regex) یا pattern  recognition در هوش مصنوعی ندارد و جزو ساختار‌های زبان است.تطبیق الگو یا pattern matching یکی از جذابیت‌های زبان‌های فانکشنال است. زبان‌هایی مثل haskell و elixir یا حتی rust بسیاری از کار‌ها را با تطبیق الگو انجام می‌دهند. اما اصلا تطبیق الگو چیست و چه تفاوتی با سوییچ‌کیس‌ها دارد؟ با یک مثال توضیح را شروع می‌کنیم. تابع زیر را در نظر بگیریدf 0 = 1
f n = n * f (n-1)بیاید اینطوری کد را بخونیم:پاسخ تابع به ازای ورودی ۰، یک است.پاسخ تابع برای هر n دیگر، برابر n * f(n-1) است.این یک تابع بازگشتی‌ست که برای n های مثبت، !n را حساب می‌کند. دقت کنید به کدی که نوشته‌ایم. بسیار شبیه به تعریف ریاضی همین تابع است. این قدرت تطبیق الگو را مشخص می‌کند.اما شباهت تطبیق الگو به اینجا محدود نمی‌شود، با استفاده از سینتکس match (یا در برخی زبان‌ها خود case) می بینیم که بسیار شبیه سوییچ‌کیس می‌شود:// match in rust 
let x = 1;
match x {
    1 =&gt; println!(&amp;quotone&amp;quot),
    2 =&gt; println!(&amp;quottwo&amp;quot),
    3 =&gt; println!(&amp;quotthree&amp;quot),
    4 =&gt; println!(&amp;quotfour&amp;quot),
    5 =&gt; println!(&amp;quotfive&amp;quot),
   // else clause, executed when none of value matches
    _ =&gt; println!(&amp;quotsomething else&amp;quot),
}

// case in haskell 
case expression of pattern -&gt; result  
                   pattern -&gt; result  
                   pattern -&gt; result  
                   ...  شاید سوال پیش بیاید که در این حالت که دقیقا مثل سوییچ‌کیس است. پس چه تفاوتی دارد؟ چرا گفتم سوییچ‌کیسِ تکامل‌یافته؟ پاسخ این است که سوییچ‌کیس (و کاربرد‌های اولیه‌ای که پترن‌مچینگ‌ دیدیم) تنها کاری که انجام می‌دهند انتخاب بر روی مقدار‌های مختلف یک تایپ است. یعنی مثلا تمام ورودی‌ها عدد صحیح هستند و بین حالات مختلف انتخاب می‌کنیم. اما قابلیت ویژه‌ی تطبیق الگو، قابلیت تعیین رویه برنامه بر اساس تایپ ورودی و همزمان شکستن آن تایپ و استخراج مقدار از آن است. ممکن است با دیتاتایپ‌های جبری آشنایی نداشته باشید‌(مطالعه بیشتر: adt) برای همین فرض کنید که تایپی که وارد فرایند تطبیق‌الگو می‌شود یک struct یا union است. با استفاده از این قابلیت می‌توانیم در حین سوییچ کردن، هم بر اساس تایپ تصمیم گیری کنیم، هم اجزای سازنده‌ی سازنده ی آن تایپ را جدا کنیم و از آن‌ها نیز استفاده کنیم. در واقع می‌توان گفت pattern matching ترکیب قدرتمندی از سوییچ‌کیس و tuple unpack است. فرض کنید یک استراکت (product type) داریم، در زبان‌های فانکشنال می‌توانیم به این صورت تطبیق الگو انجام دهیم:-- pattern matching in Haskell with records syntax 
info p = case p of 
    (&amp;quotroozbeh&amp;quot, _) -&gt; &amp;quotauthor&amp;quot -- any person with name roozbeh is ok
    (&amp;quothadi&amp;quot, &amp;quotali akbar&amp;quot) -&gt; &amp;quotmentor&amp;quot
    (&amp;quotmahdi&amp;quot, &amp;quotseyedan&amp;quot) -&gt; &amp;quotmentor&amp;quot
    (&amp;quotmohammad&amp;quot, &amp;quothasani&amp;quot) -&gt; &amp;quotmentor ^_^&amp;quot
    _ -&gt; &amp;quotunknown&amp;quotmain = do
    let p0 = (&amp;quotroozbeh&amp;quot, &amp;quotsharifnasab&amp;quot)
    putStrLn $ info p0

    let p1 = (&amp;quothadi&amp;quot, &amp;quotali akbar&amp;quot)
    putStrLn $ info p1

    let p2 = (&amp;quotmahdi&amp;quot, &amp;quotseyedan&amp;quot)
    putStrLn $ info p2

    let p3 = (&amp;quotmohammad&amp;quot, &amp;quothasani&amp;quot)
    putStrLn $ info p3

    let p4 = (&amp;quotali&amp;quot, &amp;quotalavi&amp;quot)
    putStrLn $ info p4در تابع info، از مقدار داخل استراکت‌ها به صورت مستقیم برای تعیین روند برنامه استفاده کردیم. در صورت نبود این قابلیت باید if else می‌گذاشتیم و داخل شرط if هم از and استفاده می‌کردیم. مثلا:if p[0] == &amp;quotroozbeh&amp;quot and p[1] == &amp;quotsharifnasab&amp;quot:
      return authorمطالعه بیشتر  در مورد تطبیق الگو در هسکل و راسط و توضیح کلیِ آن در ویکی‌پدیا تطبیق‌الگو در زبان‌های شی‌گرازبان‌های شی‌گرا (به طور خاص #C و جاوا) بعد از مشاهده‌ی موفقیت‌های تطبیق‌الگو و علاقه برنامه‌نویسان به این سینتکس‌ها، سعی کردند با توجه به ساختار‌های قبلی زبان، این امکان را نیز اضافه کنند.سی‌شارپ مدتی پیش این امکان را اضافه کرد و امروزه استفاده از آن بین برنامه‌نویسان سی‌شارپ بسیار رایج است. در این زبان‌ها تمرکز تطبیق الگو، روی استخراج sub-type از روی super-type است. مثلا ورودی یک شی از جنس کلاس Shape است و براساس تایپ‌های مختلفی که از آن ارث بری می‌کنند، برنامه رویه‌های مختلفی را دنبال می‌کند. همچنین در عین حال که تایپ‌ها را جدا می‌کنیم، می‌توانیم یک رفرنس از آن sub-type بگیریم. مثلا مرسوم است که بگوییم اگر شی ورودی Circle بود، یک Circle با نام c به من بده. (عملیات cast کردن به صورت امن) سی‌شارپ پا را فراتر گذاشته و می‌توانیم با استفاده از کلیدواژه‌ی when، همزمان شرط هم بگذاریم. public static double ComputeArea(object shape)
{
    switch (shape)
    {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
        case Triangle t when t.Base == 0 || t.Height == 0:
        case Rectangle r when r.Length == 0 || r.Height == 0:
            return 0;

        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Triangle t:
            return t.Base * t.Height / 2;
        case Rectangle r:
            return r.Length * r.Height;
        case null:
            throw new ArgumentNullException(paramName: nameof(shape), message: &amp;quotShape 
ust not be null&amp;quot);
        default:
            throw new ArgumentException(
                message: &amp;quotshape is not a recognized shape&amp;quot,
                paramName: nameof(shape));
    }
}این سوییچ‌کیس را به این صورت می‌خوانیم:اگر shape از نوع Square بود و ظلعش برابر با صفر بود یااگر shape از نوع Circle بود و شعاعش صفر بود یا ...، مقدار ۰ برگردان.اگر shape از نوع Square بود، یک رفرنس Square به اسم s از آن به من بده و ... مطالعه بیشتر در مورد pattern matching در سی‌شارپو اما جاوا! جاوا متاسفانه هنوز به مرحله عملیاتی نرسیده ولی صحبت‌های جدی در مورد آن انجام شده. (امیدوارم این را بعدا بخوانید و به صورت رسمی اضافه شده‌باشد!)چیزی که قرار است در جاوا اضافه شود اصلا در قالب if است ولی کاربردی دقیقا مانند همتای سی‌شارپی خود خواهد داشت. در واقع به آن pattern matching for instanceof گفته می‌شود.// current java version without pattern matching
if (obj instanceof String) {
    String s = (String) obj;  
    ...
}//  java version with pattern matching
if (obj instanceof String s) { 
   // instance of and cast in  single step
   // Let pattern matching do the work!
    ...
}یکی از کاربرد های خوب این سینتکس در متد‌ equals است چرا که حتما ورودی Object می‌گیرد و ناچار به استفاده از instanceof و cast هستیم.برنامه‌نویسان جاوا اگر علاقه به برنامه‌نویسی فانکشنال دارید می‌توانید از زبان دوست‌داشتنی اسکالا استفاده کنید که با هدف ترکیب دو پارادایم آمده و به صورت پیش‌فرض دارای چنین امکاناتیست.همچنین در پایان توصیه می‌کنم این مطلب را هم مطالعه کنید. به تفاوت‌های تطبیق الگو و سوییچ‌کیس در برخی دیگر از زبان‌ها پرداخته‌است.</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 17 Dec 2020 13:52:35 +0330</pubDate>
            </item>
                    <item>
                <title>درس‌هایی از سیستم عامل برای زندگی روزمره: الگوریتم‌های زمان‌بندی</title>
                <link>https://virgool.io/@rsharifnasab/%D8%AF%D8%B1%D8%B3%D9%87%D8%A7%DB%8C%DB%8C-%D8%A7%D8%B2-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%B9%D8%A7%D9%85%D9%84-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B2%D9%86%D8%AF%DA%AF%DB%8C-%D8%B1%D9%88%D8%B2%D9%85%D8%B1%D9%87-%DB%B1-%D8%A7%D9%84%DA%AF%D9%88%D8%B1%DB%8C%D8%AA%D9%85%D9%87%D8%A7%DB%8C-%D8%B2%D9%85%D8%A7%D9%86%D8%A8%D9%86%D8%AF%DB%8C-azdvvlmykoo2</link>
                <description>نویسنده این مطلب در حال گذراندن درس سیستم‌عامل است. به نظرم رسید که مفاهیم این درس بیش‌تر از مقداری که بهشان پرداخته می‌شود کاربردی هستند، نه لزوما در رشتهٔ کامپیوتر بلکه در زندگی روزمره.چرا؟ در ادامه می‌بینیم. تعریف ویکیپدیا:‌ سیستم عامل یا سامانهٔ عامل[۱] نرم‌افزار سیستمی ای است که مدیریت منابع رایانه را به عهده گرفته و بستری را فراهم می‌سازد که نرم‌افزار کاربردی اجرا شده و از خدمات آن استفاده کنند.به زبان ساده یعنی سیستم‌عامل منابع کامپیوتر را در اختیار گرفته و کارهایی‌که باید انجام شوند (تسک‌ها) را مدیریت می‌کند یعنی به آن‌ها بسته به نیازشان منبع می‌دهد.برای تشریح کاربرد‌ سیستم‌عامل در روزمره هم از همین واژه‌ها استفاده می‌کنم: یک سری کار برای انجام دادن داریم و یک سری منابع.کار (task) که فکر نمی‌کنم ابهامی داشته باشد! مثلا تکلیف چهارم درس فلان، پروژه‌ای که برای خودم دارم و می‌خواهم با زبان X انجام دهم یا چیزی که کارفرما خواسته و تا آخر هفته باید تمام شود.منبع (resource) یعنی امکاناتی که برای انجام کارها در اختیار داریم، مثلا زمان و سرمایه. اینجا بیشتر با زمان کار داریم. ولی می‌توانید برای چیزهای دیگر را هم منابع در نظر بگیرید مثلا فضای روی میز!برای کامپیوتر فهمیدیم که چه کسی مسئول تخصیص منابع است، اما برای خودمان چی؟ متاسفانه (و البته خوشبختانه) کسی به این دقت برای ما برنامه‌ نمی‌ریزد و این کارِ خودِ ماست. کدام کار را انجام دهیم؟ کدام را انجام ندهیم؟ کدام را زودتر انجام دهیم چون واجب‌تر است و ...سیستم عامل خودت باشخب من به عنوان سیستم‌عامل خودم که قرار است تسک‌ها را مدیریت کنم، چطوری باید این کار را انجام بدهم؟روش‌های مختلفی برای مدیریت‌ کار‌ها هست. من صرفا دو تا را که خودم شنیدم اینجا ذکر می‌کنم.از طریق مشاور دبیرستانمان با ماتریس آیزنهاور آشنا شدم. این روش می‌گوید کار‌ها ۴ حالت دارند:مهم و فوری: باید به سرعت انجام شود.مهم و غیرفوری: باید انجام شود ولی زمانش چندان اهمیتی ندارد.غیرمهم و فوری: انجام شدنش برای شخص ما اهمیتی ندارد ولی زمان انجامش محدود است.غیرمهم و غیرفوری: اسمش گویا هست.در صورتی که از این روش تبعیت کنیم کارهای مهم و فوری باید اول انجام شوند، سپس بین کارهای مهم و غیرفوری و غیرمهم و فوری تصمیم می‌گیریم و احتمالا به غیر مهم و غیر فوری نخواهیم رسید.https://zehnekhalag.ir/eisenhower/منبع و مطالعه بیشتر روش معروف دیگر که در کتاب clean coder با آن آشنا شدم، روش مدیریت زمان گوجه‌ای یا پومودرو است. در این روش، فرد کار‌ها را در بازه‌های زمانی ۲۵ دقیقه‌ای انجام می‌دهد. در آن ۲۵ دقیقه هیچ کار دیگری انجام نمی‌دهد. بعد از ۲۵ دقیقه چند دقیقه استراحت می‌کند و سپس ۲۵ دقیقه بعدی را شروع می‌کند.این مثال‌ها را دیدید؟ این‌ها روش‌هاییست برای مدیریت زمان برای انجام کارها. دقیقا کاری که سیستم عامل در کامپیوتر شما انجام می‌دهد. (البته کار سیستم‌عامل بسیار بیشتر است، یکی از کارهایش CPU scheduling است.)الگوریتم‌های اولویت‌دهی در سیستم‌عامل از اسم این قسمت نترسید! قرار نیست حرف پیچیده‌ای بزنم! حداقل پیچیده‌تر از «عمیق نگاه کردن به زندگی روزمره».توضیح مختصر پس‌گیرنده و پس‌نگیرنده اگر زمانی که در حال انجام یک کار هستیم، تا زمانی که تمام نشده، با آمدن کارهای جدید، آن را متوقف نکنیم، می‌شود پس نگیرنده (non-Preemptive). اما اگر هنگامی که در حال یک کار هستیم با آمدن کار جدید به لیست کارها، سری هم به کار جدید بزنیم و یک دور لیست کار‌ها ها ورق بزنیم و بعد تصمیم بگیریم چه کاری را انجام دهیم، می‌شود پس‌گیرنده (preemptive). منظور از پس‌گیرنده این است که کاری که در حال اجرا بود را پس می‌دهیم و کار دیگری را برمی‌داریم اما در حالت پس‌نگیرنده کار گرفته شده تا وقتی کامل انجام نشود پس گرفته نمی‌شود.الگوریتم‌های پس‌نگیرندههرکاری اول آمده را زودتر انجام بده (first come first serve): منتظر بمان تا کاری بیاید، آن را انجام بده و دوباره ببین (به ترتیب آمدن) کار بعدی را انتخاب کن و انجام بده.کار کوتاه‌تر را زودتر انجام بده (shortest job first): بین کارها کاری را انتخاب کن که زمان کم‌تری برای انجام شدن نیاز دارد. با این ریسک که کار خیلی بلندی ممکن است هیچوقت انجام نشود.صرفا برای زیبا(تر) کردن مطلب!الگوریتم‌های پس‌گیرندهکوتاه‌ترین زمان باقی‌مانده را اول انجام بده (shortest remaining time first): مثل حالت کار کوتاه‌تر است با این تفاوت که در زمانی که کار جدید بیاید (بدون توجه به اینکه کار فعلی انجام شده یا نشده) بررسی می‌کند و کاری که زمان باقی‌مانده کمتری دارد را انجام می‌دهد. مثلا یک کار ۲ ساعته دارم و یک ساعتش انجام شده، حالا یک کار نیم ساعته اضافه می‌شود. پس کار ۲ ساعته را رها می‌کنم و نیم ساعته را پیگیری می‌کنم. استفاده از این الگوریتم با این ریسک است که یک کار در لیست بماند و قدیمی و قدیمی‌تر شود.بالاترین نسبت پاسخ (highest response ratio next): برای حل مشکل قدیمی‌شدن، علاوه بر اولویت‌دادن به «کوتاه‌ترها»، به کار‌های «قدیمی‌تر» هم اولویت می‌دهیم تا کاری در صف برای مدت زیادی نماند.نوبت‌گردشی (round robin): به نظرم این روش خیلی به روش گوجه نزدیک است. روش کار به این صورت است که از هر کار مدت زمان معینی را انجام بده (مثلا همان ۲۵ دقیقه). سپس این کار را بگذار کنار و کار بعدی را ۲۵ دقیقه انجام بده. در این صورت همه کار‌ها در حال انجام شدن هستند. در روش نوبت گردشی، دو نکته قابل توجه است. اول اینکه اگر کاری در صف است که اهمیت بیشتری‌دارد می‌توانیم چند نوبت به آن اختصاص دهیم، مثلا در طی یک گردش که از هر کدام از کارها ۲۵ دقیقه انجام می‌دهیم، از این کار دو یا چند نوبت انجام دهیم. نکته دوم اینکه برای انتخاب زمان مناسب (در مثال ما ۲۵ دقیقه) باید تعادلی برقرار کنیم بین دیر شدن کار‌ها (اگر ۱۰۰ کار داشته باشیم، بعد از ۲۵۰۰ دقیقه دوباره نوبت کار فعلی می‌شود!) و زمان کانتکست سوییچ.از این نمودار برای نمایش و مثال زدن برای الگوریتم‌ها استفاده می‌شود ولی آوردنش اینجا هدفی ندارد.کانتکست سوییچ؟ کانتکست‌سوییچ (context switch) یعنی زمانی که بین انجام کارها هدر می‌رود. یعنی زمانی که تصمیم می‌گیرم دست از کار۱ بکشم و به کار۲ بپردازم، زمانی در این بین هدر می‌رود. مثلا الان در حال نوشتن وبلاگ هستم (کار۱)، از زمانی که این کار‌ را رها کنم تا زمانی که شروع کنم و به صورت موثر برای امتحان فردا بخوانم (کار۲) می‌شود زمان context switch. این زمان برای کامپیوتر معمولا اندک و قابل چشم‌پوشی است ولی برای ما انسان‌ها نه! مثلا من تا جزوه را در بیاورم و شماره صفحه و فصل‌های مورد سوال و .. را پیدا کنم و اولین کلمه را بخوانم همه در زمان هدر رفته‌ی context switch هستند. اگر می‌خواهیم بازه‌های زمانی کوتاهی انتخاب کنیم باید حواسمان به این زمان هدر رفته باشد.حالت چند‌صفیما قرار نیست همه کارهایمان را فقط در یک صف قرار دهیم بلکه می‌توانیم برای کار‌های ددلاین‌دار یک صف داشته‌باشیم و آن‌ها را بر اساس ددلاینشان زمان‌بندی کنیم و برای کار‌های بدون ددلاین (مثلا همان پروژه‌های شخصی) صف دیگری داشته باشیم. یا مثلا یک صف برای کارهای بزرگ و طولانی داشته‌باشیم و یک صف برای کارهای کوتاه که هر کدام جدا زمان‌بندی شوند. (Multi Queue) در آخر باید تصمیم بگیریم که چقدر از زمان را به هر صف بدهیم. مثلا ۷۰ درصد برای کارهای مهم (ضروری و غیرضروری) و ۳۰ درصد برای کارهای غیرمهم. حتی می‌توانیم بسته به شناخت از خودمان ساعت‌های پربازده را به یک‌سری کار بپردازیم و ساعات کم‌بازده را به یک سری کار دیگر. داشتن صف‌های جدا در این مورد خیلی کمک‌کننده است.بازخورد گرفتن هم بسیار مهم است، مثلا می‌توانیم با بازخوردی که از کارایی یک کار در یک تسک داشتیم، تصمیم بگیریم در صف دیگری قرارش دهیم. (Multi Queue with feedback)اگر این قسمت را خواندید قطعا یاد صف نانوایی افتاده‌اید. صف «یدونه‌ای» و صف «چندتایی». با این هدف که کسی که کار کوتاه‌تری دارد (یک نان می‌خواهد) برای کار کوتاهش معطل یک کار بزرگ نشود. هر صف به صورت مستقل و با قوانین خودش (مثلا هرکس اول آمده را زودتر انجام بده) مدیریت می‌شود.زمان‌بندی برای کارهای دوره‌ای کارهای دوره‌ای یعنی مثلا کاری که هر هفته باید انجام شود، مثلا درسی که هر هفته تکلیف می‌دهد و مهلتش تا هفته دیگر همان موقع است. در همین حین ممکن است درس دیگری هر ۱۰ روز و درس دیگری هر ۱۴ روز تکلیف دهد.کدام را باید اول انجام داد؟هر کدام زمان کم‌تری دارد (Rate-monotonic scheduling): به صورت پس‌گیرنده تسکی را انجام بده که زمان کم‌تری دارد. مثلا درسی که هرروز تکلیف می‌دهد را با اولویت بالا انجام بده.هر کدام ددلاین نزدیک تری دارد (earliest deadline first): به صورت پس‌گیرنده، تسکی را انجام بده که ددلاین نزدیک‌تری دارد. (بیشتر توضیح دهم؟)کاری که نسبت به زمان باقی‌مانده‌اش، زمان کم‌تری داریم (least laxity first): از بین تسک‌ها تسکی را انتخاب کن که زمان باقی‌مانده تا ددلاین منهای زمان باقی‌مانده از تسک، کم‌ترین باشد. به بیانی برای رساندن کار به ددلاینْ تنبلی (laxity) کم‌تری مجاز است.از کجا بفهمیم این کار چقدر طول می‌کشد؟در بعضی از الگوریتم‌ها نیاز داریم بدانیم که این کار چقدر طول می‌کشد. در کامپیوتر و زمانی‌ که یک کار ناشناخته را می‌خواهد انجام دهد این سوال خوبی است. البته جواب خوبی ندارد! می‌توان از اطلاعات گذشته برای حدس زدن انتخاب کرد ولی معمولا برای پاک کردن صورت مسئله از الگوریتم‌هایی استفاده می‌شود که نیاز به این عدد ندارند مثلا نوبت‌گردشی.برای ما انسان‌ها ولی معمولا تخمین زدن کار سختی نیست، با استناد به همین توانایی جلو بروید و هربار سعی کنید تخمین بهتری بزنید. سعی کردم در این مطلب ساده و کاربردی مباحث را توضیح دهم، در صورتی که به منبع دقیق‌تری نیاز دارید این مطلب در مورد الگوریتم‌های زمان‌بندی را توصیه می‌کنم: https://sabzdanesh.com/%D8%A7%D9%84%DA%AF%D9%88%D8%B1%DB%8C%D8%AA%D9%85-%D9%87%D8%A7%DB%8C-%D8%B2%D9%85%D8%A7%D9%86%D8%A8%D9%86%D8%AF%DB%8C-%D9%BE%D8%B1%D8%AF%D8%A7%D8%B2%D9%86%D8%AF%D9%87/ در انتها یادی کنم از دوست خوبم جناب آقای محمدصادق دهقان که با مدیریت بی‌نظیر منابع زندگی‌اش هنگام کار و تحصیل، ایده‌ی این مطلب را در ذهنم انداخت. </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 29 Oct 2020 02:17:07 +0330</pubDate>
            </item>
                    <item>
                <title>گزارشی از Deprecate شدن یک متد در جاوا!</title>
                <link>https://virgool.io/javacup/%DA%AF%D8%B2%D8%A7%D8%B1%D8%B4%DB%8C-%D8%A7%D8%B2-deprecate-%D8%B4%D8%AF%D9%86-%DB%8C%DA%A9-%D9%85%D8%AA%D8%AF-%D8%AF%D8%B1-%D8%AC%D8%A7%D9%88%D8%A7-zvdewmvphgjc</link>
                <description>امروز می‌خواستم برنامه جیتسی رو از AUR دانلود کنم اما به یه دلیل جالب موفقیت‌آمیز نبود. اما مشکل چی بود؟ بیاید کمی عمیق‌تر نگاه کنیم.در مورد AURیکی از قابلیت های دوست داشتنی توزیع‌های مبتنی بر arch linux، مخزن نرم افزاری AUR هست (مخفف arch user repository). همونطور که از اسمش پیداست، توسط کاربرا نگه‌داری می‌شود. هر کاربر می‌تونه برنامه دلخواهش رو بعلاوه روش نصب خودکارش توی AUR قرار بده تا کابرای دیگه بتونن استفاده کنن. مثلا لینک  Jisit در AUR.خوبیِ این مخزن اینه که به بسیاری از نرم‌افزار‌ها به سادگی و بدون نیاز به کامپایل کردن دستی سورس‌کد می‌شه دسترسی داشت. بدیش هم اینه که طبیعتا کسی این‌ها رو تایید نکرده و ریسک مخرب بودن وجود داره.راستی، برای استفاده از  AUR، باید برنامه مخصوص نصب کنید و با خود پکیج منجیر پک‌من، این قابلیت وجود نداره. معمولا به این برنامه‌ها AUR helpers می‌گویند. برنامه‌های زیبا و متعددی وجود دارد، من از yay استفاده می‌کنم که رابطی شبیه خود pacman دارد.مشکل کامپایل نشدن Jitsiدانلود برنامه از AUR چند بخش داره. اول فایل  PKGBUILD دانلود می‌شه که در واقع روش نصب اون برنامه هست. بعدش طبیعتا باید یه سری فایل دانلود بشه که سورس‌کد هستند. در آخر طبق اون روش نصب کامپایل شده و نصب اتفاق می‌افته. معمولا مشکل نصب نشدن از AUR به خاطر تحریمه چون فایل دانلودی روی source forge قرار داره،، اما اینبار دانلود بدون مشکل انجام شد. هش‌ها هم با موفقیت چک شدن و همه‌چیز برای نصب درست بود. همونطور که در تصویر می‌بینید، دانلود موفقیت‌آمیز بوده و مشغول باز کردن فایل زیپ شده.مساله این بود که زمان کامپایل کردن سورس‌کد، کامپایل‌ارور داد و موفقیت‌آمیز نبود.اما مشکل چی بود؟زمانی که سورس‌کد را با آخرین نسخه جاوا (چون تا این لحظه ۱۵ روی مخازن نیومده، نسخه ۱۴) کامپایل می‌کردم یک سری کلاس رو پیدا نمی‌کرد، یک سری هم وارنینگ  deprecate بودن می‌داد.سوالی که پیش میاد اینه که Deprecate چیه.در مورد Deprecateتوی برنامه نویسی هم مثل دنیای بیرون، همه چیز فانی است (!) و عمر بی‌نهایت وجود نخواهد داشت. هر محصول/زبان/فریم‌ورکی یک روز ساخته می‌شود، تا مدت کم یا زیادی پشتیبانی می‌شود و توسعه داده می‌شود و زمانی هم باید از بین برود.شاید معروف‌ترین مثال این روز‌ها adobe flash بود که به دلیل مشکلات امنیتی و  باگ‌های زیاد بالاخره تصمیم گرفته شد که در پایان سال ۲۰۲۰ برای همیشه نابود شود. در برنامه‌نویسی هم خیلی از وقت‌ها یک تابع/متد به دلیل ضعف در پیاده سازی یا ناامن بودن یا آمدن راه‌های بهتر و سریع‌تر باید از دور خارج شوند اما این اتفاق یک‌باره اتفاق نمی‌افتد. بلکه شرکت سازنده در یک نسخه این کلاس/تابع را Deprecate اعلام می‌کند. به این معنی که با چیزهای بهتری جایگزین شده و بهتر است استفاده نشود و ممکن است در نسخه‌های آتی حذف شود.یکی از صدها وارنینگ زمان کامپایل!نتیجه استفاده از کلاس‌ها و متد‌های Deprecate شده چیست؟ در مرحله اول زمان کامپایل وارنینگی به شکل بالا دریافت می‌کنیم. اما این تمام اتفاق نیست. همانطور که گفتیم هدف Deprecate شدن، حذف و نابودی است بنابراین در نسخه‌های آتی برنامه ما اصلا کامپایل نخواهد شد چراکه آن کلاسی که استفاده کرده‌ایم حذف شده و دیگر اصلا وجود ندارد!در این نسخه از Jitsi هم از چندین متد و کلاس Deprecate شده استفاده شده بود مثل تصویر بالا که این به تنهایی مشکل ایجاد نمی‌کند. مشکل از جایی شروع می‌شود که در نسخه‌‌ی جاوای من آن api حذف شده و دیگر قابل استفاده نیست. طبیعتا نتیجه کامپایل‌ارور خواهد بود.مشکل چطور برطرف شد؟ برای برطرف کردن مشکل چند راه پیش رو داشتم، اول اینکه بیخیال نصب برنامه بشوم و به استفاده از اسکایپی ادامه بدهم! راه دوم اینکه سورس‌کد را باز کنم و آن قسمت‌ها را بازنویسی کنم. اما در زمان کم چه فکری کردم؟ بله! استفاده از یک جاوای قدیمی‌تر. نسخه جاوای سیستمم را موقتا به jdk8 تغییر دادم و مشکل برطرف شد و به درستی نصب و کامپایل شد!چرا این متد‌ها Deprecate شده‌بودند؟من با کلاس اصلی‌ای حذف شده‌بود و باعث کامپایل‌ارور شده بود آشنا نبودم (شاید به همین دلیل که من تازه جاوا یاد گرفته‌ام و این کلاس سال‌هاست که برای استفاده مناسب نیست!)اما مواردی که وارنینگ می‌داد و تصویرش را در بالا هم آوردم را می‌توانم توضیح بدهم!کد زیر را در نظر بگیرید:این wrapper‌ها همیشه توی جاوا مشکل‌ساز بودند! قضیه از این قرار است که که همه چیز توی جاوا شی است به جز داده های اولیه مثل int و long و char. اما بعضی جاها مثلا لیست (به طور عمومی‌تر جنریک‌ها)، ما نیاز داریم که با آبجکت کار کنیم (برخلاف تمپلیت توی cpp). پس برای تبدیل نوع داده‌های اولیه به کلاس،  کلاس‌های wrapper برای نوع داده های اولیه تعریف کردند.کدی که اینجا نوشته در قسمت new Integer(98) تلاش می‌کند از روی primitive، یک آبجکت wrapper تولید کند. اما ایراد چیه؟چون استفاده از اعداد خیلی مرسومه و سرعت هم بسیار اهمیت دارد، برای بهینه‌سازی constructor معمولی را deprecate کردند و به جای آن استفاده از Integer.valueOf را معرفی کردند. چرا؟ چون به ازای هر استفاده از عدد (لزوما) یک آبجکت جدید ساخته نشود، بلکه برای بازه‌ی پر استفاده و کوچک (−۱۲۸ تا ۱۲۷) از آبجکت‌های کَش‌شده استفاده بشود تا سربار ساخت آبجکت جدید نداشته باشیم.نتیجه‌گیری۱- جاوا مستقل از سکو هست؟ معمولا بله ولی در صورتی بی‌دقتی شایدم نه، البته نه به دلیل تفاوت platform بلکه به دلیل تفاوت نسخه‌های جاوا.۲- همیشه اخبار Deprecate شدن را جدی بگیریم و در برنامه خود از این متدها/کلاس‌ها استفاده نکنیم.۳- هیچوقت توی کدی که به دست کاربر می‌دهیم، وارنینگ‌ها را به عنوان ارور در نظر نگیریم چون در صورتی که یک متد مورد استفاده ما بعدا Deprecate شود، برنامه کامپایل نمی‌شود در حالی که تا حذف شدن کامل آن هنوز چند نسخه‌ای باقی است.۴- فعال باشید و از خواندن سورس‌کد نترسید.</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Sat, 26 Sep 2020 15:31:16 +0330</pubDate>
            </item>
                    <item>
                <title>در مورد دیتابیس‌های «سری‌زمانی» و معرفی InfluxDB</title>
                <link>https://virgool.io/@rsharifnasab/%D8%AF%D8%B1-%D9%85%D9%88%D8%B1%D8%AF-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3%D9%87%D8%A7%DB%8C-%D8%B3%D8%B1%DB%8C%D8%B2%D9%85%D8%A7%D9%86%DB%8C-%D9%88-%D9%85%D8%B9%D8%B1%D9%81%DB%8C-influxdb-zpnwdtbq2e4z</link>
                <description>این مطلب به عنوان تحقیق درس پایگاه‌داده دکتر حقیقی در ترم پاییز ۹۹ نوشته می‌شود.در جست‌وجو برای پایگاه‌داده‌های پسا-رابطه‌ای (post-relational) به عبارت‌ها و تکنولوژی‌های گوناگونی برمی‌خوریم. معمولا عبارات برپازدید NoSQL، ردیس، مونگو‌-دی‌بی و BSON هستند اما در این مطلب به موضوعی که کم‌تر به آن پرداخته‌شده و شاید برای بسیاری از ما ناشناخته باشد می‌پردازیم.داده‌های سری‌زمانی (Time Series Data)داده‌هایی که یک ماشین خودران جمع‌آوری می‌کند را در نظر بگیرید. قاعدتا حجم زیادی داده خام به ذهنمان می‌رسداین‌ داده‌ها که یک ویژگی مهم دارند، آن‌ها برچسب زمانی که داده تولید (گردآوری) شده را نیز به همراه دارند. بسیاری از داده‌هایی که از دستگاه‌ای اینترنت چیزها (IoT) تولید می‌شوند چنین خاصیتی دارند. این داده‌ها گاهی خاصیت پیوستگی هم دارند مثلا شاخص‌های بورس یا دمای یک محیط در طی زمان می‌توانند به صورت (تقریبا) پیوسته ذخیره شوند.این داده‌ها ۳ ویژگی مهم دارند: داده تقریبا در همان زمانی که ثبت می‌شود، به دست ما می‌رسد.داده‌ها (معمولا) به ترتیب به دست ما می‌رسند.زمان ثبت یکی از شاخص‌های اهمیت داده است.به بیان دیگر تنها عملیات، الحاق داده‌های جدید به داده‌های قبلی است. (با صرف نظر از بررسی و رفع خطا و ..)پایگاه‌داده‌های سری‌زمانی (Time Series Databases)پایگاه‌داده‌های سری‌زمانی (TSDB) پایگاه‌داده‌هایی هستند که به طور خاص برای نگه‌داری و کاوش در داده‌های سری‌های زمانی طراحی می‌شوند. این داده‌ها عموما به شکل زوجِ مقدار-زمان نگه‌داری می‌شوند.پایگاه‌داده‌های TSDB برای کار با زوج مقدار-زمان بهینه شده‌اند، مثلا از الگوریتم‌های فشرده‌سازی برای مدیریت بهینه‌تر داده استفاده می‌کنند.برخی از این پایگاه‌داده‌ها، قابلیت بررسی تغییرات داده‌ها و در آوردن به شکل گراف را نیز می‌دهند.از مهم‌ترین پایگاه‌داده‌های TSDB می‌توان به موارد زیر اشاره کرد:Riakkdb+IMB InformixInfluxDBPrometheuseXtremeDBIBM Informix on CloudAzure Time Series InsightsTimescaleDBDruidOpenTSDBGtaphiteدر مورد مزایا و معایب هر کدام می‌توان زیاد صحبت کرد برای مثال TimescaleDB قابلیت پشتیبانی از SQL سنتی و ادغام با PostgresSQL را دارد. من در اینجا قصد معرفی پرکاربردترین آن‌ها که InfluxDB است را دارم.چرا با دیتابیس‌های معمولی کار راه نمی‌افتد؟سوالی که پیش می‌آید این است که مگر دیتابیس‌های رابطه‌ای (یا معمولی!) نمی‌توانند داده‌ای که برچسب زمان داشته‌باشد را مدیریت کنند؟ جواب این است که چرا می‌توانند اما دیتابیس‌های مخصوص TSDB که برای همین‌کار بهینه‌شده‌اند از ۲ جهت بهتر هستند.مقیاس‌پذیری: عمدتا داده‌های سری‌زمانی با سرعت بالا تولید می‌شوند، مثلا یک خودروی خودران ۴ گیگابایت داده فقط در یک روز تولید می‌کند. دیتابیس‌های رابطه‌ای برای این حجم از داده مناسب نیستند. دیتابیس‌های NoSQL در این زمینه بسیار بهتر از رابطه‌ای ها هستند ولی در مقابل TSDBها حرفی برای گفتن ندارند.قابلیت استفاده راحت: معمولا TSDBها توابع و عملیات‌هایی برای کار‌های مرسوم روی داده‌های سری‌زمانی دارند مثلا قابلیت هندل کردن «سیاست‌های نگهداری اطلاعات»، «کوئری‌های مداوم» و «واحد‌های زمانی انعطاف‌پذیر» را دارند. حتی اگر مقیاس‌پذیری جزو دغدغه‌های فعلی ما نباشد، این توابع تجربه استفاده بهتری می‌دهند.کمی بیشتر در مورد InfluxDBدو بازیگر اصلی TSDBها، Prometheus و InfluxDB هستند، هر دو متن‌باز هستند. ما در اینجا کمی بیش‌تر در مورد InfluxDB می‌خوانیم.این TSDM محصول شرکت InfluxData واقع در سان‌فرانسیسکو است. در سال ۲۰۱۳ به عنوان یک پروژه کاملا متن‌باز عرضه شد که روی تمام سیستم‌عامل‌های مرسوم کنونی کار میکند. همچنین مجموعه بزرگی از زبان‌های برنامه‌نویسی (حتی لیسپ و کلوژر) را پشتیبانی می‌کند. برای بار سنگینِ نوشتن بهینه شده و به صورت هم روند بسیار موثر عمل می‌کند. از مزیت‌های دیگر آن این است که بعد از نصب با ۵ دقیقه خواندن داکیومنت‌ها، می‌توانید کار با آن را شروع کنید.این TSDB با زبان Go نوشته‌شده و برای سرعت و دسترس‌پذیری بالا بهینه شده. از انواع داده‌ی زیر هم پشتیبانی می‌کند:عدد صحیح ۶۴ بیتیعدد ممیز شناور ۶۴ بیتی رشتهبولینمنابع: https://hazelcast.com/glossary/time-series-database/  https://blog.timescale.com/blog/what-the-heck-is-time-series-data-and-why-do-i-need-a-time-series-database-dcf3b1b18563/  https://www.trustradius.com/time-series-databases  https://medium.com/schkn/4-best-time-series-databases-to-watch-in-2019-ef1e89a72377  https://en.wikipedia.org/wiki/Time_series_database  https://www.influxdata.com/  https://en.wikipedia.org/wiki/InfluxDB </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 24 Sep 2020 22:46:29 +0330</pubDate>
            </item>
                    <item>
                <title>تجربه نوشتن ادیتور متنی تحت ترمینال با جاوای خالص</title>
                <link>https://virgool.io/javacup/vi-with-java-icneylrx2ncn</link>
                <description>به عنوان پروژه درس ساختمان داده، از ما خواسته شد ساختمان داده piece table را پیاده‌سازی کنیم و آن را در یک ادیتور متنی به کار بگیریم. قوانین پروژه عدم استفاده از هرگونه import و include بود (به جز اسکنر جاوا و stdio در c/c++) و زبان‌های قابل استفاده هم محدود به همین دو زبان جاوا و سی‌پلاس‌پلاس بود.اسکرین‌شات جهت زیبایی است و از ادیتور neovim گرفته شده و ارتباطی به ادیتور مورد بحث نداردچون هدف اصلی، طراحی ساختمان داده بود و من نمی‌خواستم مدیریت حافظه دردسر دومی برایم باشد، جاوا را انتخاب کردم. از مزیت‌های این انتخاب این بود که بدون دیباگر و ابزار کمکی می‌توانستم دسترسی به خارج از آرایه را تشخیص بدهم و در مدیریت حافظه و نداشتن نشتی حافظه هم کارم ساده‌تر بود. اما همه این‌ها مربوط به قبل از شروع پیاده‌سازی بود. در ادامه اتفاقاتی که وقعا افتاد را با هم بررسی می‌کنیم: ۱- گرفتن ورودی از کاربریک ادیتور تحت ترمینال باید به صورت لحظه‌ای به ورودی‌های کاربر واکنش نشان دهد، یعنی باید بتواند هر هر کلیدی که روی کیبورد زده می‌شود را دریافت کند. مثلا تابع getch در زبان سی برای اینکار مناسب است، چرا که در همان لحظه که کلیدی زده می‌شود ورودی دریافت می‌شود و کد اسکی آن به دست می‌آید.اما در جاوا وضعیت چگونه است؟ هیچ تابعی معادل getch وجود ندارد! تنها راه ورودی گرفتن ما اسکنر بود که باید صبر می‌کردیم کاربر اینتر را وارد کند تا یک رشته ورودی بگیریم.. این غیر قابل قبول است! کاربران در اینترنت هم تنها راهی که به ذهنشان می‌رسید استفاده از سویینگ و معادل‌های آن و تعریف Action listener بود اما من فقط اجازه استفاده از جاوا را داشتم.راه حل: وقتی از سرچ ناامید شدم، تصمیم گرفتم خود فایل System.in را بررسی کنم تا ببینم چه متد‌هایی دارد. آیا به اسکنر تک کاراکتر به تک کاراکتر خروجی می‌دهد؟ متوجه شدم که خود System.in یک کلاس منحصر به فرد نیست و یک شی از نوع InputStream است. بررسی کلاس InputStream چه نتیجه‌ای می‌دهد؟ این کلاس متد های read و mark و close دارد که متدهای mark و close در مورد System.in نتیجه خوبی نمی‌دهند و غیر قابل استفاده هستند. اما متد read متدی بود که به کار ما آمد. این متد (Read) یک مرتبه هم اورلود شده است، هم می‌توان بدون ورودی دادن آن را فراخوانی کرد که در این صورت خروجی آن، مقدار اولین بایت ورودی است. به نظر خوب می‌آید. هر کاراکتر در یک بایت جا می‌گیرد و مثل getch می تواند عمل کند.اما خواندن مستقیم از یک InputStream می‌تواند منجر به پرتاب استثنای IOException شود، البته که احتمالا در شرایط واقعی برنامه ما این اتفاق نخواهد افتاد ولی به هر حال استثنای چک شده‌است و باید مدیریت شود.مشکل بعدی این است که همه کلید‌های روی کیبورد، تک کارکتری نیستند که بتوانیم در یک بایت ورودی بگیریمشان. مهم‌ترین کلید‌هایی که لازم داشتیم arrow keys بودند. کلید‌های جا به جا شدن در متن که به صورت ۳ کاراکتر ظاهر می‌شوند.این ۳ کاراکتر، کاراکتر‌های ۲۷ (اسکیپ) و ۹۱ و کاراکتر سوم ۶۵ تا ۶۸ بود. ۶۵ ما را یاد A می‌اندازد و همین‌طور با D. حالا برای این ۴ کاراکتر چه تدبیری باید بیندیشیم؟ در واقع سوال این است که زمانی که یک کاراکتر ۲۷ دریافت کردیم، از کجا متوجه شویم که این واقعا یک کلید escape بوده یا در ادامه ۲ کارکتر دیگر هم وارد خواهد شد و arrow key است؟کار اولی که به ذهنم رسید، این بود که مقدار کمی صبر کنم و منتظر کاراکتر بعدی بمانم، اگر کلیدی وارد شد یعنی ۳ کارکتر بوده و همه همزمان وارد شده‌اند، اما اگر تا آن زمان چیزی وارد نشد یعنی کاربر فقط یک escape وارد کرده بوده است و باید همین را پردازش کنیم. برای اینکار یک ترد موازی برای صبر کردن در نظر گرفتم که ترد ورودی گرفتن را بعد از مدتی interrupt می‌کرد! متاسفانه این روش جواب نداد چون زمانی که روی System.in در حال read باشید و ترد اینتراپت شود، استریم ورودی دچار مشکل می‌شود و یک کاراکتر بعدی وارد نمی شود. ضمنا این روش یک باگ دارد و در صورتی که کاربر esc را نگه دارد هم اشتباه عمل می کند.روش دومی که انجام دادم و نهایتا مشکل مرتفع شد، این بود که به متد read یک آرایه ۳ بایتی ورودی دهیم. در یک بار خواندن هرچند کاراکتر که وارد شود را می‌خواند و مقدار دهی می‌کند و نهایتا به عنوان مقدار برگشتی، تعداد المنت‌هایی که ورودی گرفته را بر می‌گرداند. حالا کار ساده‌است. در صورتی که مقدار برگشتی read برابر ۱ باشد یعنی یک تک کاراکتر وارد شده، اما در صورتی که ۳ باشد یعنی arrow key بوده و باید به مقدار اسکی کاراکتر سوم این ارایه توجه کرد. مشکل دیگر بافر شدن مقدار ورودی در خود ترمینال بود، تا زمانی که یک newline ورودی داده نمی‌شد، استریم ورودی نمی‌توانست کاری از پیش ببرد. برای رفع این مشکل هم یک دستور shell را با کمک Runtime فراخوانی کردیم:			Runtime.getRuntime().exec&#40;new String[]{&quot;/bin/sh&quot;,&quot;-c&quot;,&quot;stty icanon &lt;/dev/tty&quot;}&#41;;۲- مشکلات مستقل از سکو بودنمستقل از سکو بودن جاوا مزیت‌های زیادی دارد و شکی در آن نیست ولی این سطح از انتزائی‌سازی، یک شمشیر دو لبه است. یک مشکل ساده که واقعا قابل حل نبود، گرفتن اندازه فعلی ترمینال بود. در جاوا هیچ راهی برای این کار پیدا نکردم. (البته با لایبرری jcurses و چیزهای مشابه امکان‌پذیره ولی به صورت خالص منظور است.) در آخر هم بیخیال شدم و اابعاد ترمینال را به صورت ثابت ۸۰*۲۴ در نظر گرفتم.اما مشکل دیگر، نبود فایل اجرایی binary بود. کدام ادیتوری را دیدید که برای اجرا به ران‌تایم احتیاج داشته باشد، و بدتر از آن برای اجرای ادیتور مجبور باشید دستور زیر را بنویسید:java -jar EditorName.jar myfile.txtبه همه اینجا، سربار جمع آوری زباله و مصرف رم زیاد برای بافر های نسبتا بزرگ هم اضافه کنید.۳- جا به جا کردن کرسر و چاپ روی صفحهبا مطالبی که خوندم، به نظر می‌رسد ترمینال‌ها دو حالت اصلی داشته باشند، حالت چاپ متن عادی که به صورت معمولی در آن ها print می‌کنیم و حالت خام یا raw mode. در این حالت آزادی بیشتری وجود دارد مثلا هر کلیدی که کاربر وارد کند مستقیم روی صفجه چاپ نمی‌شود، بلکه هرجا که برنامه‌نویس بخواهد هرچیزی چاپ می‌شود. اما متاسفانه امکان فعال سازی این حالت در جاوا وجود نداشت.. پس راه کار چی بود؟تنها دوستس ما در این زمینه ascii escape code ها بودند. اینها کد هایی هستند که به شما اجازه می‌دهند با ترمینالی که ansi را پشتیبانی کند (اکثر ترمینال‌ها) کارهای جالب انجام دهید. مثلا با رنگ‌های گوناگون چاپ کنید، صفحه را پاک کنید، یک خط را پاک کنید، و کرسر را جا به جا کنید. برای ادیتور هم بیشترین استفاده‌ی من همین اسکیپ‌کد ها بودند. با هر بار وارد کردن یک کلید، کل آن خط را پاک می‌کرده و مجدد چاپ می‌کنیم چرا که اثر آن حرف روی صفحه باقی مانده بود و به لحاظ بصری جالب نبود. همچنین برای اسکرول هم کد صفحه را پاک می‌کنیم و با یکی جا به جا کردن خط ها دوباره چاپ میکنیم. همچنین برای جا به جا کردن کرسر هم باید مختصات کرسر را با کد مخصوصی تولید و چاپ کنید تا کرسر جا به جا شود. پیاده‌سازی های مرتبط را در کلاس Cursor می‌توانید بررسی کنید.https://en.wikipedia.org/wiki/ANSI_escape_code  https://en.wikipedia.org/wiki/ANSI_escape_code جمع بندیبا همه محدودیت‌ها و مواردی که اشاره شد، ادیتور مورد اشاره به مرحله کار کردن رسید و اینجانب درس مربوطه را پاس کردم. :)  اما به عنوان توصیه و یادگاری از من، برای هرکاری ابزارهای مناسب را استفاده کنید. جاوا در جا و محل استفاده خودش مثلا برنامه‌های سازمانی بسیار مناسب است و هیچ حرفی در آن نیست ولی برای برنامه‌های تحت ترمینال مخصوصا به صورت جاوای خالص و بدون لایبرری اصلا گزینه مناسبی نیست و بهتر است از ابزارهایی که برای اینکار مناسب‌تر هستند استفاده شود، مثلا تا جایی که اطلاع دارم به جز سی و سی‌پلاس‌پلاس، rust و golang و node js گزینه‌های مناسبی برای توسعه برنامه تحت ترمینال هستند.برای مشاهده ادیتور مورد بحث و کار کردن با آن می‌توانید به صفحه گیتهاب پروژه مراجعه کنید.همچنین یک فایل گزارش موجود است که تک تک کلاس ها و چالش‌های برنامه را توضیح داده است. کد نیز تا حد خوبی کامنت‌گذاری شده است.  https://github.com/rsharifnasab/sbu_vi_limited/releases </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Sat, 01 Aug 2020 14:19:21 +0430</pubDate>
            </item>
                    <item>
                <title>اشتباهات رایج در C: آزاد کردن حافظه‌ای که نگرفتیم!</title>
                <link>https://virgool.io/@rsharifnasab/%D8%A7%D8%B4%D8%AA%D8%A8%D8%A7%D9%87%D8%A7%D8%AA-%D8%B1%D8%A7%DB%8C%D8%AC-%D8%AF%D8%B1-c-%D8%A2%D8%B2%D8%A7%D8%AF-%DA%A9%D8%B1%D8%AF%D9%86-%D8%AD%D8%A7%D9%81%D8%B8%D9%87%D8%A7%DB%8C-%DA%A9%D9%87-%D9%86%DA%AF%D8%B1%D9%81%D8%AA%DB%8C%D9%85-b0gagw1ntekh</link>
                <description>دز این مطلب می‌خوام بپردازم به یه ایرادی که توی سی خیلی اوقات برنامه‌نویس‌ها بهش مشکل می‌خورن و منم طبق تجربه می‌دونم چیه مشکل و توی سرچ سخت می‌شه پیدا کرد پس چه بهتر که با شما هم به اشتراک بذارم.فرض این مطلب اینه که شما با پوینتر‌ها آشنایی مختصری دارید و با malloc یا new و free یا delete هم کار کردید. از عنوان مطلب هم مشخصه که به اینا احتیاج داریم در مجموع.خب مشکل ساده‌س (البته سخت پیدا می شه)، مشکل اینه که یه بلوک حافظه رو که با malloc دریافت کردیم، free می‌کنیم ولی برنامه runtime error می‌ده و خاتمه پیدا می‌کنه.اما این مشکل چرا پیدا میشه و چطوری رفعش کنیم؟در یک جمله بخوام بگم، همون چیزی هست که توی عنوان مطلب اشاره شد، اگه پوینتر به حافظه‌ای رو به عنوان آرگومان به free بدید که قبلا به عنوان malloc برنگشته، حالا یا پوینتر به یه خونه که اصلا مال ما نیست، یا پوینتر به یه خونه‌ای غیر از اول بلوک حافظه که توسط malloc ریترن می‌شه، تابع free نمی‌تونه این دستور رو هندل کنه و باعث ران‌تایم ارور می‌شه.اما بیاید یکم عمیق‌تر جزئیات این اتفاق رو بررسی کنیم.وقتی که شما malloc رو صدا می‌زنید (ما صدا می‌زنیم، همه صدا می‌زنن، اکثر سیستم‌عامل‌ها با سی نوشته شدن و صدا می‌زنن، پایتون هم اگر توسط C پیاده‌شده باشه که ۹۹درصد شده، اونم malloc رو صدا می‌زنه:) ) یه پوینتر به اولین خونه‌ی یک بلوک حافظه برای شما بر می‌گرده. همچنین جدولی توی پس‌زمینه شکل می‌گیره که آدرس خونه اول حافظه و در مقابلش مقداری که allocate شده رو می‌نویسه که بعدا موقع free کردن استفاده کنه.موقع free کردن همونطور که می‌دونید تنها و تنها چیزی که لازمه پاس بدیم، آدرس اولین خونه‌ی حافظه هست و حجمش (تعداد بایت) رو لازم نیست ذکر کنیم. چرا؟ چون خود سی توی پس‌زمینه این رو یادداشت کرده. حالا میاد و این آدرس حافظه رو توی جدول سرچ می‌کنه و می‌بینه که چند بایت بوده و به سیستم‌عامل میگه این رو فری کن. (شاید هم یکم نادقیق دارم می‌گم و سهم سیستم‌عامل بیشتر یا کمتره ولی این جدول وجود داره)حالا چرا آدرس همه‌ٔ خونه‌های حافظه رو توی حدول نمی‌نویسه؟ چون اولا جدول خیلی بزرگ می‌شه و باید نصف رم رو بدیم به این جدوله (منطقیه دیگه، هر خونه آدرس، خودش یه value خواهد داشت، و خونه معادل هم توی اون جدول) پس این کار عبثیه و امکان‌پذیر نیست، تازع عملیات free کردن هم خیلی پرهزینه میشه.اما اگر ادرسی که ما free رو باهاش صدا می‌کنیم توی جدول نباشه، برنامه ارور میده چون نمی‌دونه چی رو باید free کنه.این مشکل هم به ۳ دلیل اتفاق می‌افته: آزاد کردن حافظه‌ای که اصلا allocate نکردیم و مال ما نیست.آزاد کردن مجدد حافظه‌ای که قبلا free کردیم و باز هم مال ما نیست.آزاد کردن پوینتر به یه خونه‌ای غیر از خونه اول حافظه مثلا خونه دوم یا سوم.از مورد سوم، نمونه کد خیلی ساده هم بیبنیم:نمونه کد مشکل‌دارهمونطور که می‌بینید خود linter من که از clang استفاده می‌کنه هم بهم یه وارنینگ داده.عبارت دقیق وارنینگش اینه: Argument to free() is offest by 4 bytes from the start of memory allocated by malloc()و می‌تونید سوال مربوط بهش رو اینجا بخونید: https://stackoverflow.com/questions/4744774/can-i-free-by-referencing-an-offset-pointer و موقع اجرا اتفاقی که می‌افته:اروری که مشاهده میکنیمدر نهایت به عنوان راه حلی که بخوام برای حل تمام سوالات پوینتری شما ارائه بدم، می‌تونم پیشنهاد کنم اگه درک کمی از پوینتر دارید از یه زبان سطح بالاتر استفاده کنید یا بیشتر درموردش مطالعه کنید. برای حل همین یه مشکل، می‌تونیم پوینتر رو جلو و عقب نبرید و اگه نیاز داشتید اندازه ش عوض بشه یا خونه‌های اولش غیر قابل دسترس بشه، یه حافظه جدید allocate کنید و هر مقدار از محتوای قبلی رو که می‌خواید بریزید توی حافظه جدید. یا اینکه پوینتر به اول حافظه رو برای زمان free کردن نگه دارید.نکته: توی متن اشاره نشد ولی لازمه اینجا بهش اشاره کنم، free کردن یه پوینتر با مقدار NULL بدون مشکل است. در واقع تنها پوینتر‌هایی که می‌توانید free کنید، خروجی‌های malloc و calloc و realloc هستند و NULL.شاد باشید و خندون</description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Sun, 31 May 2020 21:40:09 +0430</pubDate>
            </item>
                    <item>
                <title>بافر چیست و فلاش بکنیم یا نکنیم.</title>
                <link>https://virgool.io/javacup/%D8%A8%D8%A7%D9%81%D8%B1-%DA%86%DB%8C%D8%B3%D8%AA-%D9%88-%D9%81%D9%84%D8%A7%D8%B4-%D8%A8%DA%A9%D9%86%DB%8C%D9%85-%DB%8C%D8%A7-%D9%86%DA%A9%D9%86%DB%8C%D9%85-h5jw1wireksc</link>
                <description>گاهی در مورد بافر در برنامه‌نویسی می‌شنویم، مثلا  BufferedWriter در جاوا یا انواع و اقسام دیگری از ابزارهای خواندن/نوشتن که از بافر استفاده می‌کنن.اکثر چیزهایی که خروجی‌ای رو توی فایل ذخیره می‌کنن از بافر استفاده می‌کنن. بافر چیه؟ یه حافظه موقت (که در دسترس مای استفاده کننده نیست)  که کارش اینه که اون محتوایی که قراره نوشته بشه رو توی خودش ذخیره می‌کنه تا &quot;زمانی که واقعا نوشته بشه&quot;پس فهمیدیم که ۲ تا حالت وجود دارهیکی اینکه ما دستور نوشتن رو بدیم، که اون چیزی که گفتیم نوشته بشه توی بافر سیو می‌شه.دوم حالتی که واقعا نوشتن اتفاق بیافته و اصطلاحا بافر فلاش بشه.این ۲ تا به صورت پایه از هم جدا هستن. اما چرا اصلا؟ خب همون موقع که دستور میدیم نوشته بشن.داستان اینطوریه که چیزی که با دیسک/شبکه کار کنه &quot;به شدت&quot; کندتر از چیزیه که با رم/سی‌پی‌یو کار کنه. حالا ما وقتی دستور نوشتن می‌دیم همه چی توی رم/سی‌پی‌یو هندل می‌شه ولی اگر بخواد بنویسه اون موقع باید با دیسک/سوکت درگیر بشه که این برنامه رو کند می‌کنه. فرض اساسا بر اینه که شما چیزهای ریز ریز می‌نویسی و وقتی جمع شد دلت می خواد که همش با هم ارسال/سیو بشه، اینطوری کارایی خیلی بیشتر می‌شه.حالا ما یه متد فلاش هم داریم که میاد و این بافر رو (به زور) خالی می‌کنه و عملیات نوشتن رو انجام می‌ده. اما همچنان اگه از فلاش استفاده نکنیم هم در نهایت نوشتن انجام می‌شه اما لزوما همون لحظه نیست. مثلا ما ممکنه 10 تا چیز write کنیم و فقط یه بار واقعا بافر خالی بشه.حالا چه زمانی باید از فلاش استفاده کنیم و چه زمان نباید استفاده کنیم؟زمانی که صرفا می‌خوایم یه سری چیز رو بنویسیم/بفرستیم و قرار نیست &quot;به این زودی&quot; خونده بشه، ما نیاز به فلاش نداریم. مثلا وقتی که می‌خوایم 10 تا آبجکت رو توی فایل سیو کنیم و بعدا مثلا بعد از همین فورمون یا فردا یا نیم ساعت دیگه اون رو لود کنیم و بخونیم، این نیاز به فلاش نداره. اون پارت آخر بافر هم همین که فایل رو بخوایم ببندیم سیو میشن و کلا لازم نیست خودمون رو نگران بافر و فلاش کنیم.اما یه زمانی هست که وقتی ما چیزی رو می‌نویسیم واقعا دلمون می‌خواد نوشته بشه، مثلا وقتی که من الان دارم توی شبکه ارسال میکنم و اونور سرور منظر جواب منه و مثلا یه چت‌طور داریم. خب تو این مواقع ما حتما حتما باید فلاش کنیم چون سرور منتظره و اگر فلاش نکنیم اصلا چیزی نمی‌ره سمت سرور که سرور جواب بده و ما دوباره جواب بدیم بهش.توی برنامه‌ای که پرفورمنس حیاتی نباشه اصولا می‌شه همیشه فلاش کرد، مثل اینه که اون بافره رو استفاده نمی‌کنیم در روند برنامه مشکلی پیش نمیاد. مشکل فقط وقتی به وجود میاد که باید فلاش کنیم مثل برنامه کلاینت و سرور ولی این کارو انجام ندیم.یه مثال ملموس تر هم بزنم، ما وقتی که چیزی پرینت می‌کنیم هم در واقع داره بافر می‌شه و وقتی  که newline مثل endl رو چاپ می‌کنیم در واقع این بافر فلاش می‌شه. می‌بینید که تقریبا یک ساله از بافر استفاده می‌کردید بدون اینکه نیاز باشه فلاشش کنید یا حتی ازش آگاه باشید. اما چه زمانی واقعا لازمه stdout رو فلاش کنید؟ زمانی که میخوایم یه نوشته رو تا وسط خط بنویسید و همونجا چاپ شه، نه اینکه منتظر بمونه برید خط بعد. https://stackoverflow.com/questions/15042849/what-does-flushing-the-buffer-mean </description>
                <category>روزبه شریف‌نسب</category>
                <author>روزبه شریف‌نسب</author>
                <pubDate>Thu, 21 May 2020 03:03:39 +0430</pubDate>
            </item>
            </channel>
</rss>