2016-12-05 23 views
7

Tôi hy vọng rằng gettimeofday() sẽ gọi một cuộc gọi hệ thống để thực hiện công việc thực sự nhận được thời gian. Tuy nhiên, chạy chương trình sauGettimeofday() trên macOS có sử dụng cuộc gọi hệ thống không?

#include <stdlib.h> 
#include <sys/time.h> 
#include <stdio.h> 

int main(int argc, char const *argv[]) 
{ 
    struct timeval tv; 

    printf("Before gettimeofday() %ld!\n", tv.tv_sec); 

    int rc = gettimeofday(&tv, NULL); 

    printf("After gettimeofday() %ld\n", tv.tv_sec); 

    if (rc == -1) { 
     printf("Error: gettimeofday() failed\n"); 
     exit(1); 
    } 

    printf("Exiting ! %ld\n", tv.tv_sec); 
    return 0; 
} 

dưới dtruss -d trả về một danh sách dài các cuộc gọi hệ thống, người cuối cùng trong số đó là:

RELATIVE SYSCALL(args)   = return 

... lots of syscalls with earlier timestamps ... 

    3866 fstat64(0x1, 0x7FFF56ABC8D8, 0x11)  = 0 0 
    3868 ioctl(0x1, 0x4004667A, 0x7FFF56ABC91C)  = 0 0 
    3882 write_nocancel(0x1, "Before gettimeofday() 0!\n\0", 0x19)  = 25 0 
    3886 write_nocancel(0x1, "After gettimeofday() 1480913810\n\0", 0x20)  = 32 0 
    3887 write_nocancel(0x1, "Exiting ! 1480913810\n\0", 0x15)  = 21 0 

Dường như gettimeofday() không sử dụng một syscall, nhưng điều này dường như sai-chắc chắn hạt nhân chịu trách nhiệm của các đồng hồ hệ thống? dtruss có thiếu gì đó không? Tôi có đọc kết quả không chính xác không?

+2

Trên Solaris, 'gettimeofday()' chỉ cần đọc vị trí bộ nhớ. Nó cũng có thể là có một cái gì đó tương tự đang xảy ra với macOS Sierra (hoặc các phiên bản Mac OS X trước). Bạn có thể hoặc có thể không quan tâm để lưu ý rằng macOS Sierra (nhưng không phải phiên bản trước đó) hỗ trợ 'clock_gettime()' lâu dài nhất. –

+2

Bạn có thể thấy cuộc gọi hệ thống 'gettimeofday()' trong [mã nguồn] (https://opensource.apple.com/source/xnu/xnu-3789.1.32/bsd/kern/syscalls.master.auto.html) - # 116 – TheDarkKnight

+2

Trên Linux, một cuộc gọi hệ thống như 'gettimeofday()' có thể được thực hiện như một cuộc gọi chức năng bình thường * và một vài truy cập bộ nhớ "*: [vDSO] (http://man7.org/linux/ man-pages/man7/vdso.7.html) – jfs

Trả lời

6

Là TheDarkKnight pointed out, có gettimeofday cuộc gọi hệ thống. Tuy nhiên, chức năng của người dùng gettimeofday thường không không gọi gọi hệ thống tương ứng, nhưng thay vì __commpage_gettimeofday, cố gắng đọc thời gian từ một phần đặc biệt của không gian địa chỉ của quá trình được gọi là commpage. Chỉ khi cuộc gọi này không thành công, cuộc gọi hệ thống gettimeofday được sử dụng làm dự phòng. Điều này làm giảm chi phí của hầu hết các cuộc gọi đến gettimeofday từ cuộc gọi hệ thống thông thường đến chỉ đọc bộ nhớ.

Sách Mac OSX Internals: A Systems Approach mô tả trang tính. Tóm lại, nó là một khu vực đặc biệt của bộ nhớ hạt nhân được ánh xạ vào tám trang cuối cùng của không gian địa chỉ của mọi quá trình. Trong số những thứ khác, nó chứa các giá trị thời gian được "cập nhật không đồng bộ từ hạt nhân và đọc nguyên tử từ không gian người dùng, dẫn đến thất bại không thường xuyên trong việc đọc".

Để xem mức độ thường xuyên gọi gettimeofday() hệ thống được gọi bởi hàm userspace, tôi đã viết một chương trình thử nghiệm mà gọi gettimeofday() 100 triệu lần trong một vòng lặp chặt chẽ:

#include <sys/time.h> 
int main(int argc, char const *argv[]) 
{ 
    const int NUM_TRIALS = 100000000; 
    struct timeval tv; 
    for (int i = 0; i < NUM_TRIALS; i++) { 
     gettimeofday(&tv, NULL); 
    } 
    return 0; 
} 

Chạy này dưới dtruss -d trên máy tính của tôi cho thấy rằng điều này kích hoạt từ 10-20 cuộc gọi đến các cuộc gọi hệ thống gettimeofday() (0,0000001% -0.00002% của tất cả các cuộc gọi không gian người dùng).


Đối với những người quan tâm, các đường liên quan trong source code cho userspace gettimeofday() chức năng (đối với hệ điều hành MacOS 10.11 - El Capitan) là

if (__commpage_gettimeofday(tp)) {  /* first try commpage */ 
    if (__gettimeofday(tp, NULL) < 0) { /* if it fails, use syscall */ 
     return (-1); 
    } 
} 

Chức năng __commpage_gettimeofdaycombines dấu thời gian đọc từ commpage và đọc thời gian tem quầy đăng ký để tính toán thời gian kể từ kỷ nguyên trong vài giây và micro giây. (Hướng dẫn rdstc nằm trong số _mach_absolute_time.)

+0

Rất thú vị. Cảm ơn bạn. –

+0

Câu trả lời hay. Bạn đề cập đến: _Điều này làm giảm chi phí của hầu hết các cuộc gọi đến gettimeofday từ cuộc gọi hệ thống thông thường đến chỉ một bộ nhớ read._ Điều này có vẻ không đúng sự thật: các hàm '___ commpage_gettimeofday' và' _mach_absolute_time' (trước đây gọi hàm) là một vài chục hướng dẫn mỗi, với có lẽ 10 tổng số bộ nhớ đọc giữa chúng, và 'rdtsc' gọi chính nó là hai chục chu kỳ hoặc nhiều hơn (cộng với họ có hai' lfence' hướng dẫn trong đó vì lý do nào). Vì vậy, có lẽ bạn đang xem ít nhất 50 chu kỳ. Vẫn còn tốt hơn nhiều so với một cuộc gọi hạt nhân! – BeeOnRope

1

Việc sử dụng dtrace thay vì dtruss sẽ làm rõ sự nghi ngờ của bạn. gettimeofday() chính nó là một cuộc gọi hệ thống. Bạn có thể thấy cuộc gọi hệ thống này được gọi nếu bạn chạy tập lệnh dtrace.

Bạn có thể sử dụng sau dtrace kịch bản "dtrace1.d"

syscall:::entry 
/execname == "foo"/
{ 
} 

(foo là tên của thực thi của bạn)

để chạy trên sử dụng dtrace: -s dtrace dtrace1.d

và sau đó thực thi chương trình của bạn để xem tất cả cuộc gọi hệ thống được chương trình của bạn sử dụng

+0

Tôi đã làm theo hướng dẫn của bạn và 'dtrace' không xác định cuộc gọi hệ thống' gettimeofday'. Đầu ra của nó gần giống với 'dtruss', và ngoài việc liệt kê lệnh gọi' exit() 'ở cuối chương trình, năm cuộc gọi hệ thống cuối cùng (xem câu hỏi) là như nhau. Tôi đang chạy El Capitan (10.11.6). Bạn có thấy điều gì đó khác trên máy tính của mình không? – asnr

Các vấn đề liên quan