امروز میخوایم ببینیم FD چی هستش و اصلا syscall ها چطور کار میکنن؟؟
یک File Descriptor یا FD تو کرنل لینوکس یک عدد صحیح غیر منقی هستش که به عنوان یک شناسه برای یک Open Resource در یک فرآیند عمل میکنه و یه جمله که همیشه درباره لینوکس میگن اینه که همه چیز یک فایل است پس FD میتونه به انواع مختلفی از منابع اشاره کنه: یک فایل معمولی روی دیسک، یک دایرکتوری، یک پایانه ترمینال، یک سوکت شبکه، یک pipe یا حتی یک دستگاه سختافزاری.
توی کرنل هر پراسس یک file descriptor table داره که مثل یه آرایه عمل میکنه و این جدول هر عدد صحیح به ساختار داخلی به نام struct file تبدیل میکنه . struct file حاوی تموم اطلاعات مورد نیاز کرنل برای مدیریت اون ریسورس هستش که برای read , write , open استفاده میشه . یکم بخوایم دیپ بشیم زمانی این FD میاد رو کار که این (path)OPEN اگه این بخش false باشه خب exit میشه ولی در غیر این صورت اگه درست باشه میاد و یه عدد میگیره که 3 تاشون خیلی معروفن و رزرو شده هستن => 0 و 1و2 => STDIN , STDOUT , STDERR که توی کد پایین نمونش رو داریم میبینیم:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main() {
int fd = open("test.txt", O_RDONLY | O_CREAT, 0644);
if (fd == -1) {
printf("err: %s\n", strerror(errno));
return 1;
}
printf("success! FD: %d\n", fd);
close(fd);
printf("closed.\n");
return 0;
}
اینجا ما یه syscall داریم که ()open هستش . بخش بعدی مربوط به O_RDONLY هستش که یعنی فقط دسترسیمون درحد خوندن باشه و O_CREAT هست که اگه فایل مورد نظر موجود نبود ساخته بشه.0644 هم منظورش دسترسی سازنده فایل هستش که فقط مالک دسترسی read , write داره و بقیه فقط Readonly هستش.
تا اینجا حدودی با FD , دلیل وجود و کاربردش و ... اشنا شدیم و الان بریم سر وقت syscall.
در واقع syscall یه مترجم یا بخوام خیلی بخوام مرامی بگم API بین user space , kernel space هستش . userspace بخوام بگم یه محیطی هستش که حالا وب سرور ها و ادیتورها و ... توی اون بخش اجرا میشن و یه بخش محدود هستش و دسترسی کامل به فضای آدرس دهی نداریم و دسترسیمون به سخت افزار خیلی محدوده. kernel space میشه گفت همه چیز زیر دستشه و میتونه کنترلشون کنه مثل حافظه و سخت افزار و ... و ارتباط مستقیم با cpu , ram داره و دسترسی نا محدود به منابع کل سیستم داره.
برنامه هایی که توی userspace هستن برای برای انجام کارشون نیاز به kernlspace دارن که syscall میاد وسط مثل fork , kill , ioctl , getpid , ...
خب اصلا ساختار syscall چطوریه و چی هستش؟
ببین ادرس دهی توی x86-x64 لینوکس برای انجام یه syscall از دستور syscall استفاده میشه و قبل از اجرای این دستور رجیستر ها به ترتیب زیر ادرس دهی میشن :
RAX => شماره syscall(میتونین با دستور ausyscall شماره syscall ها و عدد هاشونو ببینید.)
بقیه رجیستر ها rdi , rsi , rdx , r10 , r8 , r9 متغیر هارو میگیرن و تمام. اگه خطایی رخ بده rax منفی میشه (مثلاً -1) و errno توی یه جای دیگه ذخیره میشه.
اینجا میخوایم یه hello world رو write کنیم با ساختار خودش تا کامل این بخش هم درک کنیم:
section .data
msg db 'Hello, World!', 0x0A
len equ $ - msg
section .text
global _start
_start:
; Syscall write
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, len
syscall
mov rax, 60
mov rdi, 0
syscall
خب توی این بخش RAX برابر 1 شده که منظورش syscall write هستش rdi هم 1 هست که منظورش STDOUT هست بخش rsi برابر hello world و بخش rdx هم برابر len هست و این کد میتونین با دستور:
nasm -f elf64 hello.asm -o hello.o
ld hello.o -o hello
./hello
اجراش کنین.
امیدوارم لذت برده باشین.
Join channel ⚡