تی‌پاوز (دستور در معماری x86)

**تی‌پاوز یا وقفه تی** به شکل کاربردی **tpause،** یک فرمان خاص معماری x86 اینتل است که برای توقف در پردازه از سمت کاربر استفاده می‌شود. با اجرای این دستور، پردازه در حال اجرا برای مدت کوتاهی از به اندازه چند میکروثانیه متوقف می‌شود و پردازنده به حالت ذخیره انرژی  می‌رود و انرژی کمتری مصرف می‌کند. این دستور توسط کامپایلر مستقیماً به یک دستور زبان اسمبلی تبدیل می‌شود و فراخوانی سیستم عاملی برای این دستور صورت نمی‌گیرد.

## توقف بهینهٔ یک پردازه
اگر یک پردازه سمت کاربر بخواهد برای مدتی متوقف شود، سازوکارهای زیادی برای آن وجود دارد. یک روش می‌تواند استفاده از دستور sleep باشد؛ بدین ترتیب پردازه برای مدتی از صف اجرای سیستم عامل خارج شده تا زمانی به اندازه زمان مقرر بگذرد تا دوباره توسط زمان‌بند در صف اجرای سیستم عامل قرار بگیرد و دوباره اجرا شود.

مشکل این روش است که یک پردازه را کوتاه‌تر از چند ده میکروثانیه نمی‌توان متوقف کرد. اگر بخواهیم برای مدت کوتاه‌تری یک پردازه را متوقف کنیم، یک روش دیگر استفاده از یک حلقه با بدنهٔ خالی یا همان انتظار مشغول است. مشکل انتظار مشغول این است که انرژی مصرف می‌کند و در عمل کار مفیدی انجام نمی‌دهد. به منظور ارائهٔ یک راه‌حل برای این مسئله، شرکت اینتل دستور **tpause** را به معماری پردازنده‌های سری ترِمونت اضافه کرده‌است.

این دستور می‌تواند پردازنده را در دو حالت ذخیره‌سازی انرژی ببرد. حالت C0.2 و حالت C0.1. حالت C0.1 ذخیره‌سازی انرژی کمتری نسبت به C0.2 دارد اما خارج شدن از آن حالت سریع است. در مقابل دستور C0.2 قرار دارد که ذخیره‌سازی انرژی آن بیشتر است اما خارج شدن از آن کندتر است.

## اجرای دستور در هستهٔ لینوکس
برای اجرای این دستور اولا باید این ویژگی در پردازنده وجود داشته باشد. به همین منظور می‌توان از کد زیر که در هسته لینوکس نسخه ۵٫۱۲٫۴ در مسیر /lib/raid6/x86.h وجود دارد استفاده کرد.
```c
#include <stdio.h>

static inline int boot_cpu_has(int flag)
{
unsigned int eax, ebx, ecx, edx;

eax = (flag & 0x100) ? 7 :
(flag & 0x20) ? 0x80000001 : 1;
ecx = 0;

asm volatile("cpuid"
     : "+a" (eax), "=b" (ebx), "=d" (edx), "+c" (ecx));

return ((flag & 0x100 ? ebx :
(flag & 0x80) ? ecx : edx) >> (flag & 31)) & 1;
}
#define X86_FEATURE_WAITPKG  (16*32+ 5) /* UMONITOR/UMWAIT/TPAUSE Instructions */

int main(int argc, char* argv[]) {
    printf("%d\n", boot_cpu_has(X86_FEATURE_WAITPKG));
}
```
تابع `boot_cpu_has` یک عدد ورودی می‌گیرد که عدد مشخص‌کننده یک ویژگی خاص در معماری x86 است و یک عدد خروجی می‌دهد. در صورتی که عدد خروجی برابر با ۱ باشد، یعنی آن ویژگی در پردازنده وجود دارد واگر برابر با ۰ باشد، به معنای عدم وجود آن ویژگی است. این عدد برای ویژگی WAITPKG که شامل دستور tpause می‌شود برابر ۵۱۷ است. فهرست اعداد ویژگی‌های معماری را می‌توان در مسیر /arch/x86/include/asm/cpufeatures.h از کد هسته سیستم عامل لینوکس پیدا کرد. با اجرای کد زیر در یک پردازنده حاوی ویژگی WAITPKG، دستور tpause اجرا می‌شود. 
```c
#include <immintrin.h>
#include <stdio.h>

unsigned long long rdtsc(void)
{
  unsigned hi, lo;
  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
  return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#define TPAUSE_C01_STATE 0
#define TPAUSE_C02_STATE 1

int main(int argc, char* argv[]) {
unsigned long long now_tsc = rdtsc();
unsigned long long target_tsc = now_tsc + 10000;
printf("now in tsc: %llu  release time in tsc: %llu\n", now_tsc, target_tsc);
_tpause (TPAUSE_C01_STATE, target_tsc);
}
```
کد بالا، مقدار شمارندهٔ زمان را از تابع rdtsc می‌گیرد. این شمارنده، تعداد تیک‌های زده شده توسط پردازنده را نشان می‌دهد و همواره با نرخی ثابت اضافه می‌شود. دستور tpause دو عدد می‌گیرد که عدد اول، نشان‌دهندهٔ حالت ذخیره‌سازی انرژی است. این عدد اگر برابر ۱ باشد، معادل حالت C0.2 است وگرنه معادل حالت C0.1 است. ورودی دوم، عددی است که تا رسیدن شمارندهٔ زمان به آن، توقف بایستی ادامه پیدا کند.

## نقش سیستم عامل
tpause خود یک دستور اسمبلی x86 است و بنابراین استفاده از این دستور موجب فراخوانی سیستم عاملی نمی‌شود. بدین ترتیب به نحوی سیستم عامل باید کنترلی روی اجرای این دستور داشته باشد و یک پردازه نتواند بیشتر از یک مقدار ثابت در پردازه و بدون این که اجازهٔ اجرا را به پردازهٔ دیگری بدهد، متوقف شود. همچنین، ممکن است بخواهیم که پردازنده اجازهٔ وارد شدن به حالت C0.2 را نداشته باشد.

به همین منظور، سیستم عامل می‌تواند حداکثر زمانی را که یک پردازه می‌تواند در این حالت قرار بگیرد، بر حسب واحد شمارندهٔ زمان (TSC) در ثبات خاص منظوره `IA32_UMWAIT_CONTROL` بنویسد. همچنین، اگر سیستم عامل در این ثبات در بیت ابتدایی مقدار صفر بنویسد، برنامه‌های سمت کاربر نمی‌توانند وارد حالت ذخیره‌سازی C0.2 شوند و اگر چنین درخواستی داشته باشند، همان درخواست در حالت C0.1 برای آن‌ها اجرا می‌شود.

## منابع

رده:معماری اکس۸۶
رده:سیستم‌عامل‌های آزاد
رده:لینوکس
رده:سیستم‌عامل‌ها

رده:زبان ماشین