برنامه نویسی

لینوکس چگونه کار می کند: فصل 3 فرآیند زمانبندی (قسمت 1)

در فصل 2 اشاره کردیم که اکثر فرآیندهای سیستم در حالت خواب هستند. بنابراین، هسته چگونه باعث می شود که هر فرآیند بر روی CPU اجرا شود در حالی که چندین پردازش قابل اجرا در سیستم وجود دارد؟ در این فصل، زمان‌بندی فرآیند هسته لینوکس (که از این پس به عنوان زمان‌بندی نامیده می‌شود) را مورد بحث قرار می‌دهیم که مسئول تخصیص منابع CPU به فرآیندها است.

در کتابهای علوم کامپیوتر، زمانبندی به شرح زیر توضیح داده شده است:

  • فقط یک پردازش می تواند در یک زمان بر روی یک CPU منطقی اجرا شود
  • این اجازه می دهد تا چندین فرآیند اجرایی به نوبه خود از CPU در واحدهایی به نام “برش های زمانی” استفاده کنند.

برای مثال، اگر سه فرآیند p0، p1 و p2 وجود داشته باشد، مانند شکل زیر خواهد بود.

توضیحات تصویر

بیایید با انجام آزمایشی تأیید کنیم که آیا واقعاً در لینوکس چنین است یا خیر.

برای درک محتوای این فصل، درک مفاهیم زمان سپری شده و زمان CPU مربوط به فرآیندها ضروری است. این قسمت این زمان ها را توضیح می دهد. تعاریف آنها به شرح زیر است:

  • زمان سپری شده: زمانی که از شروع تا پایان یک فرآیند می گذرد. مانند زمان اندازه گیری شده با کرونومتر از شروع تا پایان یک فرآیند است.
  • زمان CPU: زمانی که یک فرآیند در واقع از یک CPU منطقی استفاده می کند.

این عبارات احتمالاً فقط با نگاه کردن به توضیح چندان معنی نخواهند داشت، بنابراین بیایید آنها را از طریق یک آزمایش درک کنیم. اگر فرآیندی را با استفاده از دستور time اجرا کنید، می توانید زمان سپری شده و زمان CPU را از شروع تا پایان فرآیند هدف دریافت کنید. به عنوان مثال، بیایید “load.py” زیر را اجرا کنیم که پس از مصرف برخی منابع CPU پایان می یابد.

#!/usr/bin/python3

# This parameter is to change the amount of CPU time used by this program. It is convenient to change this parameter to make the CPU time consumption several seconds.
NLOOP=100000000

for _ in range(NLOOP):
    pass
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه به شرح زیر است.

$ time ./load

real    0m2.357s
user    0m2.357s
sys     0m0.000s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خروجی شامل سه خط است که با real، user و sys شروع می شود. در این میان real نشان دهنده زمان سپری شده و user و sys زمان CPU را نشان می دهد. کاربر به زمانی اشاره دارد که فرآیند در حال اجرا بود. در مقابل، sys به زمانی اشاره دارد که هسته در نتیجه فراخوانی‌های سیستمی صادر شده توسط فرآیند کار می‌کرد. برنامه Load از ابتدا تا انتهای اجرای خود به استفاده از CPU ادامه می دهد و در این مدت هیچ تماس سیستمی صادر نمی کند ، بنابراین واقعی و کاربر تقریباً یکسان هستند و sys تقریباً صفر است. دلیل “تقریبا” بودن آن به این دلیل است که مفسر پایتون چند تماس سیستمی را در ابتدا و انتهای فرآیند فراخوانی می کند.

بیایید آزمایشی را نیز با دستور خواب اجرا کنیم که بیشتر اوقات خواب است.

$ time sleep 3

real    0m3.009s
user    0m0.002s
sys     0m0.000s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از آنجایی که پس از 3 ثانیه خوابیدن به پایان می رسد، “واقعی” نزدیک به 3 ثانیه است. از طرف دیگر، این دستور از زمان کم CPU صرفنظر می کند و بلافاصله پس از راه اندازی به حالت Sleep می رود و زمانی که شروع به استفاده از زمان کمی CPU می کند، 3 ثانیه بعد قبل از پایان کار دوباره شروع به استفاده از زمان می کند. بنابراین “user” و “sys” تقریبا 0 هستند. تفاوت دو مقدار در شکل زیر نشان داده شده است.

توضیحات تصویر

هنگام استفاده از تنها یک CPU منطقی

برای ساده‌تر شدن بحث، اجازه دهید ابتدا مورد یک CPU منطقی را در نظر بگیریم. برنامه “multiload.py” برای آزمایش استفاده خواهد شد. این برنامه اقدامات زیر را انجام می دهد:

Usage: ./multiload [-m] <number of processes>
  Executes a specified number of load processing processes for a set period of time and waits for all to finish.
  The time it took to execute each process is outputted.
  By default, all processes run only on a single logical CPU.

  Meaning of the option:
  -m: Allows each process to run on multiple CPUs.
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این کد منبع آن است.

#!/bin/bash

MULTICPU=0
PROGNAME=$0
SCRIPT_DIR=$(cd $(dirname $0) && pwd)

usage() {
    exec >&2
    echo Usage: ./multiload [-m] <number of processes>
    Executes a specified number of load processing processes for a set period of time and waits for all to finish.
    The time it took to execute each process is outputted.
    By default, all processes run only on a single logical CPU.

      Meaning of the option:
      -m: Allows each process to run on multiple CPUs."
    exit 1
}

while getopts "m" OPT ; do
    case $OPT in
        m)
            MULTICPU=1
            ;;
        \?)
            usage
            ;;
    esac
done

shift $((OPTIND - 1))

if [ $# -lt 1 ] ; then
    usage
fi

CONCURRENCY=$1

if [ $MULTICPU -eq 0 ] ; then
    # Pin the "load.py" program to CPU0
    taskset -p -c 0 $$ >/dev/null
fi

for ((i=0;i<CONCURRENCY;i++)) do
    time "${SCRIPT_DIR}/load.py" &
done

for ((i=0;i<CONCURRENCY;i++)) do
    wait
done
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ابتدا “” را روی 1 قرار می دهیم و آن را اجرا می کنیم. این تقریباً مشابه اجرای برنامه بارگذاری به تنهایی است.

$ ./multiload 1

real    0m2.359s
user    0m2.358s
sys     0m0.000s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در محیط من، زمان سپری شده 2.359 ثانیه بود. وقتی موازی 2 و 3 باشد چطور؟

$ ./multiload 2

real    0m4.730s
user    0m2.360s
sys     0m0.004s

real    0m4.739s
user    0m2.374s
sys     0m0.000s
$ ./multiload 3

real    0m7.095s
user    0m2.360s
sys     0m0.004s

real    0m7.374s
user    0m2.499s
sys     0m0.000s

real    0m7.541s
user    0m2.676s
sys     0m0.000s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با افزایش سطح موازی 2 برابر، 3 برابر، زمان CPU تغییر زیادی نکرد، اما زمان سپری شده حدود 2 برابر، 3 برابر افزایش یافت. این به این دلیل است که همانطور که در ابتدا ذکر شد، تنها یک فرآیند می تواند همزمان بر روی یک CPU منطقی اجرا شود و زمانبند به نوبه خود منابع CPU را به هر فرآیند می دهد.

هنگام استفاده از چندین CPU منطقی

در مرحله بعد، بیایید نگاهی به چند CPU منطقی نیز بیندازیم. هنگامی که برنامه چند باری با گزینه “-m” اجرا می شود، زمانبند سعی می کند فرآیندهای بارگذاری متعدد را به طور مساوی در بین تمام CPU های منطقی توزیع کند. در نتیجه، به عنوان مثال، اگر دو CPU منطقی و دو فرآیند بارگذاری وجود داشته باشد، همانطور که در شکل زیر نشان داده شده است، دو فرآیند بارگذاری هر کدام می توانند منابع یک CPU منطقی را در انحصار خود درآورند.

توضیحات تصویر

منطق تعادل بار بسیار پیچیده است، بنابراین این کتاب از توضیح دقیق اجتناب می کند.

بیایید در واقع این را تأیید کنیم. نتایج اجرای برنامه چندباره با گزینه -m و موازی سازی از 1 تا 3 در زیر نشان داده شده است.

$ ./multiload -m 1

real    0m2.361s
user    0m2.361s
sys     0m0.000s
$ ./multiload -m 2

real    0m2.482s
user    0m2.482s
sys     0m0.000s

real    0m2.870s
user    0m2.870s
sys     0m0.000s
$ ./multiload -m 3

real    0m2.694s
user    0m2.693s
sys     0m0.000s

real    0m2.857s
user    0m2.853s
sys     0m0.004s

real    0m2.936s
user    0m2.935s
sys     0m0.000s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای تمام فرآیندها، مقادیر real و user+sys تقریبا یکسان بود. به عبارت دیگر، می‌توانیم ببینیم که هر فرآیند می‌تواند منابع یک CPU منطقی را در انحصار خود درآورد.

برای تمام فرآیندها، مقادیر real و user+sys تقریبا یکسان بود. به عبارت دیگر، می‌توانیم ببینیم که هر فرآیند می‌تواند منابع یک CPU منطقی را در انحصار خود درآورد.

به طور شهودی، ممکن است فکر کنید که “real>= user + sys” همیشه برقرار است، اما در واقعیت، مواردی وجود دارد که مقدار “user + sys” کمی بزرگتر از مقدار “real” باشد. این به دلیل روش های مختلفی است که برای اندازه گیری هر بار استفاده می شود و دقت اندازه گیری آنچنان بالا نیست. نیازی به نگرانی زیاد در این مورد نیست. کافی است آگاه باشید که چنین اتفاقاتی ممکن است رخ دهد.

علاوه بر این، مواردی وجود دارد که “user + sys” می تواند به طور قابل توجهی بزرگتر از “واقعی” باشد. به عنوان مثال، زمانی که برنامه “multiload.py” را با گزینه “-m” اجرا می کنید و تعداد پردازش ها را روی 2 یا بیشتر تنظیم می کنید، این اتفاق می افتد. اکنون، بیایید “./multiload -m 2” را از طریق دستور زمان اجرا کنیم.

$ time ./multiload -m 2

real    0m2.510s
user    0m2.502s
sys     0m0.008s

real    0m2.725s
user    0m2.716s
sys     0m0.008s

real    0m2.728s
user    0m5.222s
sys     0m0.016s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ورودی های اول و دوم داده های مربوط به فرآیندهای پردازش بار برنامه “multiload.py” هستند. سومین ورودی اطلاعات مربوط به خود برنامه “multiload.py” است. همانطور که می بینید، ارزش کاربر حدود دو برابر “واقعی” است. در واقع، مقادیر «user» و «sys» که با دستور «time» به دست می‌آیند، مجموع مقادیر مربوط به فرآیند هدف و فرآیندهای فرزند پایان یافته آن هستند. بنابراین، اگر یک فرآیند پردازش های فرزند را تولید کند و هر کدام روی یک CPU منطقی متفاوت اجرا شوند، مقدار “user+sys” می تواند بیشتر از واقعی باشد. برنامه “multiload.py” دقیقاً با این شرایط مطابقت دارد.

قسمت قبلی

توجه: من فراموش کردم بخش نقد واقعی را بنویسم، “فصل 2 مدیریت فرآیند (بخش 2). لطفا کمی صبر کنید :-)”

این مقاله بر اساس کتاب من نوشته شده به زبان ژاپنی است. اگر مایل به انتشار نسخه انگلیسی این کتاب هستید، لطفاً از طریق satoru.takeuchi@gmail.com با من تماس بگیرید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا