ch
Feedback
Amin'sTechLab

Amin'sTechLab

前往频道在 Telegram

🔬 Embedded Systems | AI | FPGA | PCB Design 🚀 Exploring tech at the edge of innovation 🧠 Founder of AminTechLab 📍Engineering the future – one bit at a time

显示更多
1 985
订阅者
无数据24 小时
+77
+2630
吸引订阅者
七月 '26
七月 '26
+45
在0个频道中
六月 '26
+27
在0个频道中
Get PRO
五月 '26
+10
在0个频道中
Get PRO
四月 '26
+7
在0个频道中
Get PRO
三月 '26
+1
在0个频道中
Get PRO
二月 '26
+12
在0个频道中
Get PRO
一月 '26
+4
在0个频道中
Get PRO
十二月 '25
+28
在1个频道中
Get PRO
十一月 '25
+36
在1个频道中
Get PRO
十月 '25
+41
在1个频道中
Get PRO
九月 '25
+56
在1个频道中
Get PRO
八月 '25
+44
在0个频道中
Get PRO
七月 '25
+20
在0个频道中
Get PRO
六月 '25
+15
在0个频道中
Get PRO
五月 '25
+25
在1个频道中
Get PRO
四月 '25
+18
在0个频道中
Get PRO
三月 '25
+30
在0个频道中
Get PRO
二月 '25
+19
在0个频道中
Get PRO
一月 '25
+42
在0个频道中
Get PRO
十二月 '24
+34
在0个频道中
Get PRO
十一月 '24
+36
在0个频道中
Get PRO
十月 '24
+27
在0个频道中
Get PRO
九月 '24
+17
在0个频道中
Get PRO
八月 '24
+23
在0个频道中
Get PRO
七月 '24
+48
在0个频道中
Get PRO
六月 '24
+39
在0个频道中
Get PRO
五月 '24
+44
在0个频道中
Get PRO
四月 '24
+32
在0个频道中
Get PRO
三月 '24
+60
在0个频道中
Get PRO
二月 '24
+82
在1个频道中
Get PRO
一月 '24
+48
在2个频道中
Get PRO
十二月 '23
+30
在0个频道中
Get PRO
十一月 '23
+7
在0个频道中
Get PRO
十月 '23
+10
在0个频道中
Get PRO
九月 '23
+7
在0个频道中
Get PRO
八月 '23
+5
在0个频道中
Get PRO
七月 '23
+10
在0个频道中
Get PRO
六月 '23
+7
在0个频道中
Get PRO
五月 '23
+1 042
在0个频道中
Get PRO
四月 '23
+5
在0个频道中
Get PRO
三月 '23
+9
在0个频道中
Get PRO
二月 '23
+15
在0个频道中
Get PRO
一月 '23
+14
在0个频道中
Get PRO
十二月 '22
+1
在0个频道中
Get PRO
十一月 '22
+8
在0个频道中
Get PRO
十月 '22
+11
在0个频道中
Get PRO
九月 '22
+7
在0个频道中
Get PRO
八月 '22
+13
在0个频道中
Get PRO
七月 '22
+14
在0个频道中
Get PRO
六月 '22
+11
在0个频道中
Get PRO
五月 '22
+18
在0个频道中
Get PRO
四月 '22
+14
在0个频道中
Get PRO
三月 '22
+8
在0个频道中
Get PRO
二月 '22
+12
在0个频道中
Get PRO
一月 '22
+12
在0个频道中
Get PRO
十二月 '21
+12
在0个频道中
Get PRO
十一月 '21
+38
在0个频道中
Get PRO
十月 '21
+5
在0个频道中
Get PRO
九月 '21
+25
在0个频道中
Get PRO
八月 '21
+27
在0个频道中
Get PRO
七月 '21
+554
在0个频道中
Get PRO
六月 '21
+14
在0个频道中
Get PRO
五月 '21
+39
在0个频道中
Get PRO
四月 '21
+58
在0个频道中
Get PRO
三月 '21
+88
在0个频道中
Get PRO
二月 '21
+112
在0个频道中
Get PRO
一月 '21
+692
在0个频道中
Get PRO
十二月 '20
+2 352
在0个频道中
日期
订阅者增长
提及
频道
27 七月+1
26 七月0
25 七月+2
24 七月+2
23 七月+3
22 七月+1
21 七月+2
20 七月0
19 七月+1
18 七月+1
17 七月+5
16 七月+1
15 七月0
14 七月+6
13 七月+1
12 七月+1
11 七月+2
10 七月0
09 七月+2
08 七月+2
07 七月0
06 七月+3
05 七月+1
04 七月+2
03 七月+3
02 七月+2
01 七月+1
频道帖子
آشنایی با glibc : کتابخانهٔ GNU C Library (glibc)، پیاده‌سازی رسمی پروژهٔ GNU از کتابخانهٔ استاندارد زبان C است. افزون‌براین، این کتابخانه علاوه بر توابع استاندارد ISO C، بخش بزرگی از رابط‌های برنامه‌نویسی POSIX و بسیاری از قابلیت‌های اختصاصی لینوکس را نیز در اختیار برنامه‌ها قرار می‌دهد. برای نمونه، هر زمان که در برنامهٔ خود از توابعی مانند: • printf()malloc()free()open()read()write()pthread_create()getaddrinfo() استفاده می‌کنید، در اکثر توزیع‌های لینوکس این توابع توسط glibc پیاده‌سازی شده‌اند. اهمیت glibc : نخست، کرنل لینوکس تنها System Callها را در اختیار برنامه‌ها قرار می‌دهد و استفادهٔ مستقیم از آن‌ها چندان ساده نیست. در این میان، glibc نقش یک لایهٔ واسط را بر عهده می‌گیرد. در عمل، این کتابخانه به‌عنوان یک لایهٔ انتزاعی (Abstraction Layer) میان برنامه و کرنل قرار می‌گیرد و APIهای استاندارد و قابل‌حمل را در اختیار توسعه‌دهندگان قرار می‌دهد. در نتیجه، برنامه‌نویس بدون درگیر شدن با جزئیات معماری پردازنده یا فراخوانی مستقیم System Callها، تنها با استفاده از توابع استاندارد زبان C می‌تواند با سیستم‌عامل ارتباط برقرار کند. قابلیت‌های glibc : ✅ پشتیبانی کامل از استاندارد ISO C ✅ پشتیبانی گسترده از POSIX ✅ مدیریت حافظه (malloc و free) ✅ مدیریت رشته‌ها و کاراکترها ✅ مدیریت فایل و عملیات ورودی/خروجی ✅ پشتیبانی از شبکه و سوکت‌ها ✅ مدیریت Threadها با POSIX Threads ✅ بارگذاری کتابخانه‌های اشتراکی (Dynamic Loader) ✅ پشتیبانی از Locale و بین‌المللی‌سازی (Internationalization) جایگزین‌های glibc : البته، glibc تنها کتابخانهٔ استاندارد موجود برای لینوکس نیست. برای مثال، کتابخانه‌های زیر نیز توسعه یافته‌اند: • musl libcuClibc-ngBionic (مورد استفاده در اندروید) بااین‌حال، glibc همچنان پرکاربردترین کتابخانهٔ استاندارد در توزیع‌هایی مانند Debian، Ubuntu، Fedora و RHEL محسوب می‌شود. ارزش یادگیری glibc : چنانچه در یکی از حوزه‌های زیر فعالیت می‌کنید: 🔹 برنامه‌نویسی سیستمی (System Programming) 🔹 توسعهٔ کرنل 🔹 Embedded Linux 🔹 امنیت نرم‌افزار 🔹 طراحی Runtime یا Compiler 🔹 مهندسی معکوس بدانید که مطالعهٔ مستندات و حتی کدهای glibc دید بسیار عمیق‌تری نسبت به نحوهٔ ارتباط برنامه‌ها با سیستم‌عامل در اختیار شما قرار می‌دهد. جالب است بدانید پروژهٔ glibc بیش از ۳۰ سال قدمت دارد و همچنان یکی از مهم‌ترین پروژه‌های متن‌باز دنیای لینوکس به شمار می‌رود. امروزه، تقریباً هر برنامه‌ای که روی یک توزیع لینوکسی اجرا می‌کنید، به‌صورت مستقیم یا غیرمستقیم به این کتابخانه وابسته است. به‌همین‌دلیل، شناخت glibc برای هر برنامه‌نویس سیستم، توسعه‌دهندهٔ لینوکس و حتی علاقه‌مندان به امنیت، یک مهارت بنیادی و ارزشمند محسوب می‌شود. @Amin_Techlab

2
راز پشت هر برنامه لینوکس glibC : اگر تاکنون با زبان‌های C یا ++C روی لینوکس برنامه‌نویسی کرده باشید، احتمالاً بدون اینکه متوج
راز پشت هر برنامه لینوکس glibC : اگر تاکنون با زبان‌های C یا ++C روی لینوکس برنامه‌نویسی کرده باشید، احتمالاً بدون اینکه متوجه شوید از glibc استفاده کرده‌اید. اما glibc دقیقاً چیست و چرا تا این اندازه اهمیت دارد؟ ادامه در پست بعد .... @Amin_Techlab
107
3
🚀 AMTerminal v1.0.0 منتشر شد! بعد از مدت‌ها توسعه، اولین نسخه عمومی AMTerminal آماده است. AMTerminal یک شل (Shell) و محیط خط
🚀 AMTerminal v1.0.0 منتشر شد! بعد از مدت‌ها توسعه، اولین نسخه عمومی AMTerminal  آماده است. AMTerminal  یک شل (Shell) و محیط خط فرمان (CLI) مدرن است که با زبان Rust  توسعه داده شده و روی Windows، Linux و macOS اجرا می‌شود. ✨ برخی از قابلیت‌ها: • پشتیبانی از Pipe (|) • پشتیبانی از Redirection (>, >>, <) • همراهPrompt هوشمند با نمایش وضعیت Git • تکمیل خودکار (Tab Completion) • ذخیره و جستجوی History • رنگ‌بندی و Prompt کاملاً قابل شخصی‌سازی • اجرای دستورات داخلی و خارجی • عملکرد سریع و سبک 📥 از شما دعوت می‌کنم نسخه اول را دانلود و امتحان کنید. https://github.com/Amin98Hosseini/AMTerminal_Rust/releases/tag/AMT-v1.0.0-rc.1 💬 اگر باگ، پیشنهاد یا ایده‌ای برای بهتر شدن AMTerminal دارید، حتماً در GitHub ثبت کنید یا برای من ارسال کنید. بازخورد شما نقش مهمی در توسعه نسخه‌های بعدی خواهد داشت. ⭐️ اگر پروژه را دوست داشتید، فراموش نکنید به آن در GitHub یک Star بدهید.   لینک گیت هاب پروژه : https://github.com/Amin98Hosseini/AMTerminal_Rust @Amin_Techlab
221
4
🦀 برسی کدهای اضافه شده Rust : یکی از قسمت‌های جالب این RFC، نحوه طراحی APIها در Rust است. تقریباً تمام بخش‌های Unsafe فقط در مرز ارتباط با کدهای C قرار گرفته‌اند و منطق Driver کاملاً با Safe Rust نوشته شده است. ۱- Wrapper ایمن روی APIهای C به جای اینکه Driver مستقیماً تابع C زیر را فراخوانی کند: i2c_smbus_read_byte_data(...) یک Wrapper امن در I2cClient اضافه شده است: pub fn smbus_read_byte_data(&self, command: u8) -> Result<u8> { let ret = unsafe { bindings::i2c_smbus_read_byte_data(self.as_raw(), command) }; if ret < 0 { Err(Error::from_errno(ret)) } else { Ok(ret as u8) } } در اینجا تنها بخش Unsafe همان فراخوانی تابع C است. بعد از آن، مقدار بازگشتی به یک Result<u8> استاندارد Rust تبدیل می‌شود و Driver دیگر نیازی به بررسی کدهای خطای C ندارد. ۲- پیاده‌سازی الگوی معروف Read → Modify → Write یکی از متدهای بسیار کاربردی که اضافه شده: pub fn smbus_update_bits(...) درون آن ابتدا مقدار رجیستر خوانده می‌شود: let old = self.smbus_read_byte_data(command)?; سپس فقط بیت‌های موردنظر تغییر می‌کنند: let new = (old & !mask) | (value & mask); و تنها در صورتی که مقدار واقعاً تغییر کرده باشد: if new != old { self.smbus_write_byte_data(command, new)?; } نوشتن روی سخت‌افزار انجام می‌شود. این دقیقاً همان الگویی است که تقریباً در اکثر Driverهای C لینوکس نیز دیده می‌شود. ۳- طراحی Driver با Trait به جای استفاده از Structهای متعدد و Callbackهای C، تنها کافی است Driver این Trait را پیاده‌سازی کند: pub trait Driver { const NAME: &'static CStr; const TYPE: Type; const PROPERTIES: &'static [Property]; fn get_property(...) } این طراحی باعث می‌شود هر Driver فقط روی منطق خود تمرکز کند و تمام جزئیات مربوط به Power Supply Core در پشت Abstraction پنهان بماند. ۴- استفاده از RAII برای مدیریت Driver در Rust یک Struct به نام Registration معرفی شده است. نکته جالب این است که هنگام از بین رفتن این شیء: impl Drop for Registration { fn drop(&mut self) { unsafe { bindings::power_supply_unregister(self.psy); } } } تابع power_supply_unregister() به صورت خودکار اجرا می‌شود. در نتیجه توسعه‌دهنده دیگر نگران آزادسازی منابع یا فراموش کردن Unregister کردن Driver نخواهد بود؛ این کار به کمک مکانیزم Drop در Rust انجام می‌شود. ۵- تعریف Driver واقعی فقط در چند خط در Driver مربوط به SMB347 تنها با چند ثابت می‌توان مشخص کرد که Driver چه قابلیت‌هایی دارد: const NAME: &'static CStr = c"smb347-mains"; const PROPERTIES: &'static [Property] = &[ PROP_STATUS, PROP_ONLINE, PROP_CHARGE_TYPE, ]; و سپس تنها تابعی که باید پیاده‌سازی شود: fn get_property(...) که وضعیت شارژر را از رجیسترهای سخت‌افزار می‌خواند و آن را به Power Supply Framework گزارش می‌دهد. این طراحی باعث شده حجم زیادی از کدهای تکراری Driverهای C حذف شوند و توسعه Driver بیشتر شبیه پیاده‌سازی یک Trait در Rust باشد تا کار با Callbackها و Pointerهای متعدد. ۶- نکته‌ای که توجه Maintainerها را جلب کرد یکی از جالب‌ترین قسمت‌های Review این RFC مربوط به همین چند خط بود: pub fn register<T: Driver + 'static>(dev: &Device) Alice Ryhl پیشنهاد داد که به جای &Device از &Device<Bound> استفاده شود تا از نظر Type System تضمین شود که Driver فقط روی Deviceهایی که کاملاً Bind شده‌اند ثبت می‌شود. همچنین پیشنهاد کرد این تابع به جای یک تابع آزاد، به شکل: Registration::new(...) بازطراحی شود تا با سایر APIهای Rust Kernel هماهنگ باشد. این بازخوردها نشان می‌دهد که در پروژه Rust for Linux، علاوه بر عملکرد، طراحی API و سازگاری با الگوهای Rust نیز اهمیت بسیار زیادی دارد. @Amin_Techlab
299
5
🔍 نگاهی به تغییرات کد برای Power Supply و اولین Driver شارژر باتری با Rust : این RFC تنها یک ایده تئوری نیست؛ در مجموع بیش از ۳۷۰ خط کد جدید به هسته لینوکس اضافه می‌کند که شامل دو ماژول Rust جدید و یک Driver کامل است. در Patch اول، کلاس I2cClient قابلیت‌های جدیدی برای کار با رجیسترهای سخت‌افزار دریافت می‌کند. سه متد جدید شامل: smbus_read_byte_data() smbus_write_byte_data() smbus_update_bits() اضافه شده‌اند. دو تابع اول، Wrapperهای ایمن (Safe Wrapper) روی توابع C مربوط به SMBus هستند و تمامی عملیات Unsafe و FFI را در داخل خود مخفی می‌کنند. به این ترتیب، توسعه‌دهنده Driver بدون نیاز به کار با Pointerها یا فراخوانی مستقیم APIهای C می‌تواند رجیسترهای سخت‌افزار را بخواند یا تغییر دهد. تابع smbus_update_bits() نیز یکی از پرکاربردترین الگوهای توسعه Driver را پیاده‌سازی می‌کند؛ یعنی عملیات Read → Modify → Write. این تابع ابتدا مقدار رجیستر را می‌خواند، تنها بیت‌های موردنظر را تغییر می‌دهد و در صورتی که مقدار واقعاً تغییر کرده باشد، دوباره آن را روی سخت‌افزار می‌نویسد. این همان الگویی است که در بسیاری از Driverهای C کرنل نیز استفاده می‌شود. در Patch دوم، فایل جدید rust/kernel/power_supply.rs اضافه شده که در واقع یک لایه Abstraction برای زیرسیستم Power Supply است. در این فایل یک Trait جدید با نام Driver تعریف شده که هر Driver تنها کافی است چهار بخش اصلی را پیاده‌سازی کند: نام Device نوع Power Supply لیست Propertyهای قابل پشتیبانی تابع get_property() سپس یک Callback عمومی (get_property_trampoline) ارتباط بین Power Supply Core که در C نوشته شده و Driver نوشته‌شده با Rust را برقرار می‌کند. به این ترتیب تمام جزئیات FFI در یک نقطه متمرکز شده و بقیه Driver کاملاً Safe Rust باقی می‌ماند. یکی دیگر از نکات جالب این Patch، استفاده از الگوی RAII است. ساختار Registration مسئول ثبت Driver در Power Supply Core است و هنگام Drop شدن، به صورت خودکار power_supply_unregister() را فراخوانی می‌کند. بنابراین مدیریت چرخه عمر Driver نیز مطابق الگوهای Rust انجام می‌شود. در Patch سوم نیز یک Driver واقعی برای شارژر SMB347 پیاده‌سازی شده است. این Driver مجموعه‌ای از ثابت‌های مربوط به رجیسترهای سخت‌افزار، بیت‌ها و وضعیت‌های مختلف چیپ را تعریف می‌کند و سپس با استفاده از Abstractionهای جدید، وضعیت شارژ، آنلاین بودن منبع تغذیه و نوع شارژ را از روی رجیسترهای سخت‌افزار استخراج کرده و از طریق Power Supply Framework در اختیار فضای کاربر قرار می‌دهد. در واقع این Driver اولین مصرف‌کننده (First Consumer) از Abstraction جدید Power Supply است و نشان می‌دهد که API طراحی‌شده واقعاً برای توسعه Driverهای واقعی قابل استفاده است، نه صرفاً یک نمونه آزمایشی. @Amin_Techlab
246
6
🚀 گام مهم Rust for Linux: اولین Abstraction برای Power Supply و اولین Driver شارژر باتری با Rust پروژه Rust for Linux یک RFC جدید منتشر کرده که یکی از مهم‌ترین قدم‌ها برای گسترش Rust در زیرسیستم‌های کرنل محسوب می‌شود. در این RFC، سه تغییر مهم پیشنهاد شده است: ✅ اضافه شدن APIهای SMBus برای I2C در Rust ✅ طراحی اولین Abstraction برای Power Supply Class ✅ پیاده‌سازی اولین Driver شارژر باتری (SMB347) با زبان Rust چرا این RFC مهم است؟ تا امروز، اگر قصد داشتید یک Driver مربوط به باتری یا شارژر را با Rust بنویسید، دو مانع اساسی وجود داشت: • هیچ Abstraction امنی برای Power Supply Class در Rust وجود نداشت. • I2C Client فقط عملیات مدیریت Device را انجام می‌داد و امکان خواندن یا نوشتن Registerهای سخت‌افزار را فراهم نمی‌کرد. در نتیجه، توسعه Driverهای واقعی تقریباً غیرممکن بود. بخش اول: توسعه APIهای I2C در این RFC توابعی مانند: • smbus_read_byte_data() • smbus_write_byte_data() • smbus_update_bits() به I2C Client اضافه شده‌اند. تابع smbus_update_bits() همان الگوی معروف Read → Modify → Write را پیاده‌سازی می‌کند؛ الگویی که تقریباً در تمام Driverهای کرنل برای تغییر بخشی از بیت‌های یک Register استفاده می‌شود. بخش دوم: Power Supply Abstraction مهم‌ترین قسمت این RFC، معرفی یک Abstraction جدید برای زیرسیستم Power Supply است. در این طراحی، Driver فقط کافی است: • نام Device را مشخص کند. • نوع Power Supply را تعیین کند. • لیست Propertyهای قابل پشتیبانی را معرفی کند. • تابع get_property() را پیاده‌سازی کند. تمام جزئیات ارتباط با Power Supply Core در پشت یک API ایمن پنهان شده است. علاوه بر این، Registration نیز با الگوی RAII طراحی شده تا فرآیند Register و Unregister شدن Driver به‌صورت خودکار مدیریت شود. بخش سوم: اولین Driver واقعی برای اثبات کارایی این Abstraction، توسعه‌دهنده یک نسخه Rust از Driver مربوط به شارژر Summit SMB347 پیاده‌سازی کرده است. این Driver: • از طریق I2C با چیپ ارتباط برقرار می‌کند. • Registerهای وضعیت را می‌خواند. • وضعیت شارژ را به Power Supply Core گزارش می‌دهد. • اطلاعاتی مانند: STATUS ONLINE CHARGE_TYPE را از طریق مسیر استاندارد: /sys/class/power_supply در اختیار فضای کاربر قرار می‌دهد. از دید کاربران لینوکس، این Driver دقیقاً مانند Driverهای نوشته‌شده با C رفتار می‌کند. نکته جالب در فرآیند Review، یکی از توسعه‌دهندگان Rust for Linux پیشنهاد کرد که APIهای SMBus به‌صورت توابع مستقل اضافه نشوند و در عوض، I2cClient مستقیماً Trait استاندارد kernel::io::Io را پیاده‌سازی کند. نویسنده RFC نیز این پیشنهاد را پذیرفت و اعلام کرد که نسخه بعدی Patch بر اساس معماری جدید بازنویسی خواهد شد. این دقیقاً همان چیزی است که توسعه کرنل لینوکس را جذاب می‌کند؛ طراحی APIها قبل از Merge شدن، چندین بار توسط Maintainerها بازبینی و اصلاح می‌شوند تا در نهایت بهترین معماری ممکن وارد Mainline شود. اگر این RFC در نهایت پذیرفته شود، یکی از زیرسیستم‌های مهم کرنل یعنی Power Supply نیز به فهرست بخش‌هایی اضافه خواهد شد که توسعه Driverهای آن با Rust امکان‌پذیر است؛ گامی دیگر در مسیر گسترش تدریجی Rust در هسته لینوکس. @Amin_Techlab
160
7
Harmonic Firmware Initiative (HFI) aims to create, standardize, and maintain — for RISC-V systems — something the x86 world has taken for granted for forty years: a genuine power-on firmware experience @Amin_Techlab
207
8
Harmonic Firmware Initiative (HFI) aims to create, standardize, and maintain — for RISC-V systems — something the x86 world h
Harmonic Firmware Initiative (HFI) aims to create, standardize, and maintain — for RISC-V systems — something the x86 world has taken for granted for forty years: a genuine power-on firmware experience. You switch the machine on and it presents itself on its own display — a firmware identity and POST screen, hardware enumeration, a Set-Up configuration editor, and boot selection — the way every PC has since the 1980s, and the way no bare RISC-V board does today. @Amin_Techlab
205
9
🔬 بخش دوم | معماری فنی؛ PostgreSQL روی یک Runtime جدید اگر تصور می‌کنید Turso در حال Rewrite کردن PostgreSQL با Rust است، در واقع داستان بسیار جذاب‌تر از این است. ایده اصلی، جداسازی رابط PostgreSQL از موتور اجرای دیتابیس است. در معماری فعلی PostgreSQL تقریباً تمام اجزای سیستم—از Parser و Planner گرفته تا Executor، Storage Engine، Buffer Manager و MVCC—به‌شدت به یکدیگر وابسته هستند. همین وابستگی باعث شده توسعه قابلیت‌های جدید، اجرای Async، استفاده در Edge و WebAssembly یا حتی تغییرات اساسی در Storage بسیار دشوار باشد. تیم Turso این وابستگی را می‌شکند. معماری پیشنهادی به این صورت است: PostgreSQL Wire Protocol │ SQL Parser │ PostgreSQL AST │ Query Planner/Translator │ Turso IR / Bytecode │ Limbo Runtime Engine │ Storage + MVCC + Replication به جای اجرای مستقیم Queryها در Executor سنتی PostgreSQL، دستورات SQL پس از Parse شدن به یک Intermediate Representation (IR) یا Bytecode تبدیل می‌شوند و سپس توسط Runtime موتور Limbo اجرا خواهند شد. این دقیقاً همان ایده‌ای است که LLVM در دنیای کامپایلرها پیاده‌سازی کرد؛ یعنی یک لایه میانی که چندین Frontend و Backend می‌توانند از آن استفاده کنند. مزیت‌های این معماری عبارت‌اند از: ✅ Memory Safety تمام هسته سیستم با Rust توسعه داده می‌شود؛ بنابراین بخش بزرگی از باگ‌های رایج C مانند Buffer Overflow، Use-after-Free و Data Race حذف می‌شوند. ✅ Storage Engine مستقل Storage دیگر محدود به معماری قدیمی PostgreSQL نیست و می‌تواند برای SSD، Edge Computing و Object Storage بهینه شود. ✅ Async Runtime واقعی برخلاف PostgreSQL که مدل Process-per-Connection دارد، Runtime جدید می‌تواند بر پایه Async I/O و Event Loop اجرا شود؛ موضوعی که مصرف حافظه و تعداد Connectionهای همزمان را بهبود می‌دهد. ✅ ماژولار شدن اجزای دیتابیس Planner، Executor، Storage Engine و Replication به صورت مؤلفه‌های مستقل طراحی می‌شوند؛ بنابراین اضافه کردن قابلیت‌هایی مانند Vector Search، Columnar Storage یا موتورهای Query جدید بسیار ساده‌تر خواهد بود. ✅ Embedded و WebAssembly از آنجا که Limbo یک Runtime سبک مبتنی بر Rust است، امکان اجرای دیتابیس داخل Browser (WASM)، Edge Functionها و حتی اپلیکیشن‌های Local-first فراهم می‌شود؛ قابلیتی که با PostgreSQL کلاسیک تقریباً غیرممکن است. نکته مهم اینجاست که هدف Turso جایگزین کردن اکوسیستم PostgreSQL نیست؛ بلکه حفظ سازگاری کامل با پروتکل شبکه، Dialect زبان SQL و ابزارهای موجود (مانند pgAdmin، libpq، ORMها و Driverهای PostgreSQL) است، در حالی که موتور اجرا کاملاً مدرن و بازطراحی شده خواهد بود. اگر این معماری به بلوغ برسد، احتمالاً برای اولین بار شاهد جدایی واقعی بین PostgreSQL به‌عنوان Interface و PostgreSQL به‌عنوان Database Engine خواهیم بود؛ تغییری که می‌تواند آینده دیتابیس‌های رابطه‌ای را مانند نقش LLVM در دنیای کامپایلرها متحول کند. @Amin_Techlab
265
10
🚀 بخش اول | آیا وقت بازنویسی PostgreSQL رسیده است؟ سال‌هاست PostgreSQL به عنوان یکی از قدرتمندترین دیتابیس‌های متن‌باز شناخت
🚀 بخش اول | آیا وقت بازنویسی PostgreSQL رسیده است؟ سال‌هاست PostgreSQL به عنوان یکی از قدرتمندترین دیتابیس‌های متن‌باز شناخته می‌شود؛ اما معماری آن متعلق به دهه‌ها قبل است. تیم Turso حالا پروژه‌ای جاه‌طلبانه را آغاز کرده است: ساخت یک نسخه مدرن از PostgreSQL با زبان Rust، اما نه با یک Rewrite ساده! ایده اصلی این است که هسته Rust-based تورسو به یک "LLVM برای دیتابیس‌ها" تبدیل شود؛ یعنی یک Core مشترک که موتورهای مختلف دیتابیس بتوانند روی آن پیاده‌سازی شوند. در این معماری: ✅ PostgreSQL فقط یک Frontend خواهد بود. ✅ SQLite اولین Frontend این هسته است. ✅ در آینده حتی موتورهای دیگری نیز می‌توانند روی همین زیرساخت ساخته شوند. هدف فقط سازگاری با PostgreSQL نیست؛ بلکه ارائه معماری‌ای مدرن‌تر با قابلیت‌هایی مانند: اجرای Async واقعی بهره‌گیری از ایمنی حافظه Rust اجرای Embedded و Browser Native حذف محدودیت‌های معماری قدیمی PostgreSQL امکان توسعه ساده‌تر قابلیت‌های جدید به بیان دیگر، این پروژه می‌خواهد PostgreSQL را برای نسل بعدی نرم‌افزارها بازتعریف کند، نه اینکه صرفاً آن را به Rust ترجمه کند. @Amin_Techlab
227
11
💻 یکی از خطرناک‌ترین مفاهیم در زبان C: Sequence Point و Unsequenced Modifications (بخش دوم) در بخش قبل دیدیم که اگر یک متغیر در یک عبارت چند بار تغییر کند و ترتیب اجرای این تغییرات مشخص نباشد، برنامه وارد Undefined Behavior می‌شود. حالا چند مثال دیگر را بررسی کنیم. مثال اول: int i = 0; i = i++; بسیاری تصور می‌کنند مقدار i برابر ۱ خواهد شد. اما این کد نیز Undefined Behavior است. مثال دوم: int i = 3; printf("%d %d\n", i++, i++); به نظر شما خروجی چیست؟ 3 4 یا 4 3 یا حتی 4 4 واقعیت این است که استاندارد C هیچ‌کدام از این خروجی‌ها را تضمین نمی‌کند. چون ترتیب ارزیابی آرگومان‌های یک تابع در زبان C مشخص نشده است. مثال سوم: int a = 10; a = a++ + 5; در این مثال نیز مقدار a هم‌زمان خوانده و تغییر داده می‌شود، بدون اینکه ترتیب اجرای این عملیات مشخص باشد. نتیجه؟ باز هم Undefined Behavior. چرا کامپایلر این کدها را خطا نمی‌گیرد؟ زیرا استاندارد C فقط مشخص می‌کند که رفتار این برنامه تعریف‌نشده است؛ اما کامپایلر را مجبور نمی‌کند که حتماً برای آن خطا یا حتی Warning صادر کند. کامپایلر فرض می‌کند شما قوانین زبان را رعایت کرده‌اید و بر همان اساس کد را بهینه می‌کند. به همین دلیل ممکن است: gcc main.c یک خروجی مشخص تولید کند. اما همان برنامه با: gcc main.c -O2 یا حتی روی کامپایلر دیگری، رفتار کاملاً متفاوتی داشته باشد. چگونه از این مشکل جلوگیری کنیم؟ به جای نوشتن عبارت‌های پیچیده مانند: printf("%d\n", i++ + ++i); کد را به چند مرحله ساده تقسیم کنید: int a = i; i++; int b = i; i++; printf("%d\n", a + b); نوشتن چند خط کد بیشتر، همیشه بهتر از تکیه بر رفتاری است که استاندارد C آن را تضمین نمی‌کند. جمع‌بندی یکی از مهم‌ترین قوانین زبان C این است: هرگز یک متغیر را بیش از یک بار در یک عبارت تغییر ندهید، مگر اینکه ترتیب اجرای تمام آن عملیات توسط استاندارد C تضمین شده باشد. شاید این نوع کدها امروز روی سیستم شما درست اجرا شوند، اما هیچ تضمینی وجود ندارد که فردا، با یک نسخه جدید از کامپایلر یا فقط با فعال کردن Optimization، همان رفتار را حفظ کنند. به همین دلیل برنامه‌نویسان حرفه‌ای C و Embedded از نوشتن عبارت‌های پیچیده‌ای که هم‌زمان یک متغیر را چند بار تغییر می‌دهند، خودداری می‌کنند. در زبان C، ساده نوشتن فقط خوانایی را افزایش نمی‌دهد؛ بلکه از ورود برنامه به یکی از خطرناک‌ترین انواع Undefined Behavior نیز جلوگیری می‌کند. ⚠️ @Amin_TechLab
341
12
💻 یکی از خطرناک‌ترین مفاهیم در زبان C: Sequence Point و Unsequenced Modifications (بخش اول) اگر سال‌ها با زبان C کار کرده باشید، احتمالاً حداقل یک بار با کدی مواجه شده‌اید که روی یک کامپایلر درست کار می‌کند، اما روی کامپایلر یا سطح Optimization دیگر، نتیجه متفاوتی می‌دهد. در بسیاری از موارد، دلیل این رفتار یک مفهوم مهم در استاندارد C است: Unsequenced Modifications این مفهوم یکی از رایج‌ترین دلایل Undefined Behavior در زبان C محسوب می‌شود. فرض کنید کد زیر را بنویسید: #include <stdio.h> int main() { int i = 5; printf("%d\n", i++ + ++i); return 0; } به نظر شما خروجی چیست؟ 12 ؟ 13 ؟ 14 ؟ پاسخ صحیح این است: هیچ پاسخ درستی وجود ندارد. طبق استاندارد C، این کد Undefined Behavior است. چرا؟ بیایید عبارت زیر را بررسی کنیم: i++ + ++i در این عبارت، متغیر i دو بار تغییر می‌کند: i++ و ++i اما استاندارد C مشخص نمی‌کند که کدام‌یک باید ابتدا اجرا شود. کامپایلر می‌تواند: ابتدا i++ را اجرا کند. ابتدا ++i را اجرا کند. حتی بخشی از هر دو را هم‌زمان بهینه‌سازی کند. هیچ ترتیب مشخصی وجود ندارد. Sequence Point چیست؟ در زبان C، بعضی نقاط وجود دارند که استاندارد تضمین می‌کند تمام عملیات قبلی کامل شده‌اند و سپس اجرای بخش بعدی آغاز می‌شود. به این نقاط می‌گویند: Sequence Point در استانداردهای جدید C بیشتر از اصطلاح‌های Sequenced Before و Unsequenced استفاده می‌شود، اما هنوز هم بسیاری از منابع آموزشی از عبارت Sequence Point استفاده می‌کنند. چه زمانی مشکل ایجاد می‌شود؟ اگر در یک عبارت: یک متغیر را بیش از یک بار تغییر دهید. یا آن را هم‌زمان بخوانید و تغییر دهید. بدون اینکه ترتیب اجرای این عملیات مشخص باشد. برنامه وارد Undefined Behavior می‌شود. این یعنی استاندارد C دیگر هیچ تضمینی درباره نتیجه اجرای برنامه نمی‌دهد. در بخش دوم، چند مثال واقعی دیگر را بررسی می‌کنیم و خواهیم دید چرا این دسته از باگ‌ها فقط با تغییر کامپایلر یا فعال کردن Optimization ظاهر می‌شوند. @Amin_TechLab
254
13
💻حالا همان Mini Shell که در پست قبل با زبان C نوشتیم را با Rust بازنویسی می‌کنیم. جالب است بدانید که در Rust دیگر خبری از fork() و execvp() نیست. کتابخانه‌ی استاندارد Rust این جزئیات سطح پایین را پشت یک API ساده مخفی کرده و ما فقط با std::process::Command کار می‌کنیم. کد زیر دقیقاً همان Shell ساده‌ی پست قبل را پیاده‌سازی می‌کند: use std::io::{self, Write}; use std::process::Command; fn main() { loop { print!("mini-shell> "); io::stdout().flush().unwrap(); let mut line = String::new(); io::stdin().read_line(&mut line).unwrap(); let line = line.trim(); if line.is_empty() { continue; } if line == "exit" { break; } let args: Vec<&str> = line.split_whitespace().collect(); let status = Command::new(args[0]) .args(&args[1..]) .status(); match status { Ok(status) => { println!("Exit Status: {}", status); } Err(e) => { eprintln!("Error: {}", e); } } } } این برنامه چگونه کار می‌کند؟ 🔹 دریافت ورودی ابتدا یک خط از کاربر خوانده می‌شود: let mut line = String::new(); io::stdin().read_line(&mut line).unwrap(); 🔹 تجزیه دستور مانند نسخه‌ی C، ورودی بر اساس فاصله جدا می‌شود: let args: Vec<&str> = line.split_whitespace().collect(); اگر کاربر بنویسد: ls -l /home بردار args به این شکل خواهد بود: ["ls", "-l", "/home"] 🔹 اجرای برنامه در این قسمت تفاوت اصلی Rust و C دیده می‌شود: Command::new(args[0]) .args(&args[1..]) .status(); کافی است نام برنامه و آرگومان‌ها را مشخص کنیم. کتابخانه‌ی استاندارد Rust در سیستم‌های شبه‌یونیکس خودش در پشت صحنه عملیات fork → exec → wait را انجام می‌دهد و نتیجه‌ی اجرای برنامه را به ما برمی‌گرداند. به عبارت دیگر، ما همان رفتار Shell را داریم، اما بدون اینکه مستقیماً با fork() یا execvp() کار کنیم. چرا این کد این‌قدر کوتاه‌تر است؟ در نسخه‌ی C مجبور بودیم: حافظه را مدیریت کنیم. رشته را Tokenize کنیم. fork() را صدا بزنیم. execvp() را اجرا کنیم. خطاها را مدیریت کنیم. با waitpid() منتظر پایان فرآیند بمانیم. اما در Rust، بیشتر این پیچیدگی‌ها داخل std::process::Command پنهان شده‌اند و API ساده‌تری در اختیار برنامه‌نویس قرار می‌گیرد. آیا Rust واقعاً از fork استفاده نمی‌کند؟ یکی از سؤالات رایج همین است. خیر؛ این فقط ظاهر ماجراست. در لینوکس، زمانی که از Command::status() یا Command::spawn() استفاده می‌کنید، Rust در نهایت از مکانیزم‌های سیستم‌عامل برای ایجاد فرآیند جدید استفاده می‌کند. بسته به سیستم‌عامل و نسخه‌ی آن، این کار ممکن است با fork()، vfork()، posix_spawn() یا مکانیزم‌های مشابه انجام شود. بنابراین Rust چیزی را جایگزین سیستم‌عامل نکرده است؛ فقط یک Abstraction سطح بالا روی همان APIهای قدیمی یونیکس ساخته تا توسعه‌دهنده با کد کمتر، خواناتر و ایمن‌تر همان نتیجه را به دست آورد. به همین دلیل اگر مفاهیم fork، exec و wait را در C یاد بگیرید، درک رفتار Command در Rust نیز بسیار ساده‌تر خواهد بود. @Amin_TechLab
227
14
💻 ساخت یک Mini Shell در کمتر از ۵۰ خط کد! اگر بخواهیم ساده‌ترین نسخه‌ی یک Shell را بنویسیم، فقط به چند قابلیت نیاز داریم: دریافت یک دستور از کاربر ساخت یک Process جدید (fork) اجرای برنامه (execvp) منتظر ماندن تا پایان اجرای برنامه (waitpid) کد زیر دقیقاً همین کار را انجام می‌دهد. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> int main(void) { char line[256]; while (1) { printf("mini-shell> "); if (!fgets(line, sizeof(line), stdin)) break; line[strcspn(line, "\n")] = '\0'; if (strcmp(line, "exit") == 0) break; char *argv[32]; int argc = 0; char *token = strtok(line, " "); while (token && argc < 31) { argv[argc++] = token; token = strtok(NULL, " "); } argv[argc] = NULL; pid_t pid = fork(); if (pid == 0) { execvp(argv[0], argv); perror("execvp"); exit(EXIT_FAILURE); } else { waitpid(pid, NULL, 0); } } return 0; } این کد چگونه کار می‌کند؟ 🔹 fgets() یک خط از کاربر دریافت می‌کند. 🔹 strtok() رشته را بر اساس فاصله به آرگومان‌های مختلف تقسیم می‌کند. برای مثال: ls -l /home به این تبدیل می‌شود: argv[0] = "ls" argv[1] = "-l" argv[2] = "/home" 🔹 fork() از فرآیند فعلی یک نسخه‌ی جدید ایجاد می‌کند. بعد از این تابع دو Process وجود دارد: Parent (شل) Child (برنامه‌ای که قرار است اجرا شود) 🔹 execvp() فرآیند فرزند را با برنامه‌ی موردنظر جایگزین می‌کند. اگر بنویسید: ls -l در واقع Child دیگر Mini Shell نیست؛ تبدیل به برنامه‌ی ls می‌شود. 🔹 waitpid() شل اصلی منتظر می‌ماند تا اجرای برنامه تمام شود و سپس دوباره Prompt را نمایش می‌دهد. این دقیقاً همان چرخه‌ای است که تقریباً تمام Shellهای لینوکس (مانند Bash و Zsh) برای اجرای برنامه‌های خارجی انجام می‌دهند: Read Command │ ▼ Parse Arguments │ ▼ fork() │ ▼ execvp() │ ▼ Program Runs │ ▼ waitpid() │ ▼ Show Prompt Again این نمونه فقط یک Shell آموزشی است و قابلیت‌هایی مثل Pipeline (|)، Redirect (>)، متغیرهای محیطی، Wildcard (*)، Job Control، Builtins و Quote Parsing را پیاده‌سازی نمی‌کند؛ اما برای درک نحوه‌ی اجرای دستورات در لینوکس، یکی از بهترین نقطه‌های شروع محسوب می‌شود. @Amin_TechLab
208
15
💻حالا همان Mini Shell که در پست قبل با زبان C نوشتیم را با Rust بازنویسی می‌کنیم. جالب است بدانید که در Rust دیگر خبری از fork() و execvp() نیست. کتابخانه‌ی استاندارد Rust این جزئیات سطح پایین را پشت یک API ساده مخفی کرده و ما فقط با std::process::Command کار می‌کنیم. کد زیر دقیقاً همان Shell ساده‌ی پست قبل را پیاده‌سازی می‌کند: use std::io::{self, Write}; use std::process::Command; fn main() { loop { print!("mini-shell> "); io::stdout().flush().unwrap(); let mut line = String::new(); io::stdin().read_line(&mut line).unwrap(); let line = line.trim(); if line.is_empty() { continue; } if line == "exit" { break; } let args: Vec<&str> = line.split_whitespace().collect(); let status = Command::new(args[0]) .args(&args[1..]) .status(); match status { Ok(status) => { println!("Exit Status: {}", status); } Err(e) => { eprintln!("Error: {}", e); } } } } این برنامه چگونه کار می‌کند؟ 🔹 دریافت ورودی ابتدا یک خط از کاربر خوانده می‌شود: let mut line = String::new(); io::stdin().read_line(&mut line).unwrap(); 🔹 تجزیه دستور مانند نسخه‌ی C، ورودی بر اساس فاصله جدا می‌شود: let args: Vec<&str> = line.split_whitespace().collect(); اگر کاربر بنویسد: ls -l /home بردار args به این شکل خواهد بود: ["ls", "-l", "/home"] 🔹 اجرای برنامه در این قسمت تفاوت اصلی Rust و C دیده می‌شود: Command::new(args[0]) .args(&args[1..]) .status(); کافی است نام برنامه و آرگومان‌ها را مشخص کنیم. کتابخانه‌ی استاندارد Rust در سیستم‌های شبه‌یونیکس خودش در پشت صحنه عملیات fork → exec → wait را انجام می‌دهد و نتیجه‌ی اجرای برنامه را به ما برمی‌گرداند. به عبارت دیگر، ما همان رفتار Shell را داریم، اما بدون اینکه مستقیماً با fork() یا execvp() کار کنیم. چرا این کد این‌قدر کوتاه‌تر است؟ در نسخه‌ی C مجبور بودیم: حافظه را مدیریت کنیم. رشته را Tokenize کنیم. fork() را صدا بزنیم. execvp() را اجرا کنیم. خطاها را مدیریت کنیم. با waitpid() منتظر پایان فرآیند بمانیم. اما در Rust، بیشتر این پیچیدگی‌ها داخل std::process::Command پنهان شده‌اند و API ساده‌تری در اختیار برنامه‌نویس قرار می‌گیرد. آیا Rust واقعاً از fork استفاده نمی‌کند؟ یکی از سؤالات رایج همین است. خیر؛ این فقط ظاهر ماجراست. در لینوکس، زمانی که از Command::status() یا Command::spawn() استفاده می‌کنید، Rust در نهایت از مکانیزم‌های سیستم‌عامل برای ایجاد فرآیند جدید استفاده می‌کند. بسته به سیستم‌عامل و نسخه‌ی آن، این کار ممکن است با fork()، vfork()، posix_spawn() یا مکانیزم‌های مشابه انجام شود. بنابراین Rust چیزی را جایگزین سیستم‌عامل نکرده است؛ فقط یک Abstraction سطح بالا روی همان APIهای قدیمی یونیکس ساخته تا توسعه‌دهنده با کد کمتر، خواناتر و ایمن‌تر همان نتیجه را به دست آورد. به همین دلیل اگر مفاهیم fork، exec و wait را در C یاد بگیرید، درک رفتار Command در Rust نیز بسیار ساده‌تر خواهد بود. @Amin_TechLab
8
16
💻 ساخت یک Mini Shell در کمتر از ۵۰ خط کد! اگر بخواهیم ساده‌ترین نسخه‌ی یک Shell را بنویسیم، فقط به چند قابلیت نیاز داریم: دریافت یک دستور از کاربر ساخت یک Process جدید (fork) اجرای برنامه (execvp) منتظر ماندن تا پایان اجرای برنامه (waitpid) کد زیر دقیقاً همین کار را انجام می‌دهد. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> int main(void) { char line[256]; while (1) { printf("mini-shell> "); if (!fgets(line, sizeof(line), stdin)) break; line[strcspn(line, "\n")] = '\0'; if (strcmp(line, "exit") == 0) break; char *argv[32]; int argc = 0; char *token = strtok(line, " "); while (token && argc < 31) { argv[argc++] = token; token = strtok(NULL, " "); } argv[argc] = NULL; pid_t pid = fork(); if (pid == 0) { execvp(argv[0], argv); perror("execvp"); exit(EXIT_FAILURE); } else { waitpid(pid, NULL, 0); } } return 0; } این کد چگونه کار می‌کند؟ 🔹 fgets() یک خط از کاربر دریافت می‌کند. 🔹 strtok() رشته را بر اساس فاصله به آرگومان‌های مختلف تقسیم می‌کند. برای مثال: ls -l /home به این تبدیل می‌شود: argv[0] = "ls" argv[1] = "-l" argv[2] = "/home" 🔹 fork() از فرآیند فعلی یک نسخه‌ی جدید ایجاد می‌کند. بعد از این تابع دو Process وجود دارد: Parent (شل) Child (برنامه‌ای که قرار است اجرا شود) 🔹 execvp() فرآیند فرزند را با برنامه‌ی موردنظر جایگزین می‌کند. اگر بنویسید: ls -l در واقع Child دیگر Mini Shell نیست؛ تبدیل به برنامه‌ی ls می‌شود. 🔹 waitpid() شل اصلی منتظر می‌ماند تا اجرای برنامه تمام شود و سپس دوباره Prompt را نمایش می‌دهد. این دقیقاً همان چرخه‌ای است که تقریباً تمام Shellهای لینوکس (مانند Bash و Zsh) برای اجرای برنامه‌های خارجی انجام می‌دهند: Read Command │ ▼ Parse Arguments │ ▼ fork() │ ▼ execvp() │ ▼ Program Runs │ ▼ waitpid() │ ▼ Show Prompt Again این نمونه فقط یک Shell آموزشی است و قابلیت‌هایی مثل Pipeline (|)، Redirect (>)، متغیرهای محیطی، Wildcard (*)، Job Control، Builtins و Quote Parsing را پیاده‌سازی نمی‌کند؛ اما برای درک نحوه‌ی اجرای دستورات در لینوکس، یکی از بهترین نقطه‌های شروع محسوب می‌شود. @Amin_TechLab
7
17
خب حالا که ساختار Shell در لینوکس رو باهاش آشنا شدیم . وقتی خوبیه که Mini-Shell بنویسیم تا دقیقا عملکردش رو درک کنیم . در ادا
خب حالا که ساختار Shell در لینوکس رو باهاش آشنا شدیم . وقتی خوبیه که Mini-Shell بنویسیم تا دقیقا عملکردش رو درک کنیم . در ادامه ابتدا این Mini-Shell را به زبان C می‌نویسیم و بعد همان را با زبان Rust بازنویسی میکنیم . @Amin_TechLab
221
18
پشت صحنه‌ی یک دستور در لینوکس! (۱۰/۱۰) اگر تمام این مسیر را مرور کنیم، می‌بینیم پشت یک خط فرمان ساده، دنیای بزرگی پنهان شده است. ابتدا ترمینال ورودی ما را دریافت می‌کند. شل آن را می‌خواند، تجزیه و تحلیل می‌کند، متغیرها را گسترش می‌دهد، مسیر برنامه را پیدا می‌کند و تصمیم می‌گیرد چه چیزی باید اجرا شود. در ادامه، با استفاده از مفاهیمی مانند fork و exec فرآیندهای جدید ایجاد می‌شوند و پس از پایان اجرا، شل دوباره کنترل ترمینال را در اختیار می‌گیرد. از طرف دیگر، شل خودش نیز یک زبان برنامه‌نویسی کامل است؛ زبانی که امکان تعریف متغیر، شرط، حلقه، توابع و خودکارسازی بسیاری از کارها را فراهم می‌کند. شاید به همین دلیل است که با وجود ده‌ها محیط گرافیکی مدرن، هنوز هم خط فرمان یکی از قدرتمندترین ابزارهای تعامل با سیستم‌عامل محسوب می‌شود. شل یک ابزار قدیمی نیست؛ بلکه یکی از بنیادی‌ترین بخش‌های دنیای یونیکس است که بعد از چند دهه، همچنان همان سادگی، انعطاف و قدرت روز اول را حفظ کرده است. پایان. @Amin_TechLab
205
19
پشت صحنه‌ی یک دستور در لینوکس! (۹/۱۰) کار شل با اجرای یک برنامه تمام نمی‌شود. اگر برنامه در Foreground اجرا شده باشد، شل تا پایان اجرای آن منتظر می‌ماند و کنترل ترمینال را دوباره پس می‌گیرد. به همین دلیل هنگام اجرای برنامه‌های زمان‌بر، تا پایان کار خبری از Prompt جدید نیست. اما وظایف شل فقط به انتظار کشیدن محدود نمی‌شود. اگر کاربر کلیدهای Ctrl+C یا Ctrl+Z را فشار دهد، این شل است که باید سیگنال مناسب را مدیریت کند. همچنین اگر برنامه‌ای را با & در پس‌زمینه اجرا کنیم، باز هم شل مسئول مدیریت آن خواهد بود. او باید: وضعیت Jobها را نگه دارد. پایان اجرای آن‌ها را تشخیص دهد. در صورت نیاز به کاربر اطلاع دهد. و هم‌زمان Prompt را برای اجرای دستورات بعدی نمایش دهد. به همین دلیل شل صرفاً یک اجراکننده‌ی برنامه نیست؛ بلکه مدیر چرخه‌ی اجرای آن‌ها نیز محسوب می‌شود. ادامه دارد... @Amin_TechLab
197
20
پشت صحنه‌ی یک دستور در لینوکس! (۸/۱۰) شل فقط مسئول اجرای برنامه‌ها نیست؛ خودش یک زبان برنامه‌نویسی کامل است. برای مثال: name="Ali" echo "$name" یا: if [ -f /etc/passwd ]; then echo "exists" fi و حتی: for file in *.txt; do echo "$file" done در این مثال‌ها دیگر فقط در حال اجرای یک برنامه نیستیم. شل متغیر تعریف می‌کند، شرط اجرا می‌کند، حلقه می‌سازد و بر اساس قواعد زبانی خودش تصمیم می‌گیرد چه اتفاقی بیفتد. به همین دلیل Shell Script فقط مجموعه‌ای از دستورات پشت سر هم نیست؛ بلکه یک زبان اسکریپت‌نویسی واقعی است. با استفاده از آن می‌توان برنامه‌های مختلف را به هم متصل کرد، روی خروجی آن‌ها تصمیم گرفت، عملیات تکراری را خودکار کرد و حتی ابزارهای نسبتاً پیچیده ساخت. همین ویژگی است که شل را از یک Command Runner ساده فراتر می‌برد. ادامه دارد... @Amin_TechLab
200