Ngày tính bằng mili giây trên OpenWRT trên Arduino YUN


4

Tôi đang sử dụng OpenWRT trên Arduino YUN và tôi đang cố gắng để có được ngày chính xác tính bằng mili giây (DD / MM / YYYY h: min: sec: ms) bằng cách lấy thời gian bằng máy chủ thời gian.

Thật không may, date +%Nchỉ trở lại %N, nhưng không phải là nano giây. Tôi nghe nói +%Nkhông được bao gồm trong ngày OpenWRT.

Vì vậy, có cách nào để có được ngày (bao gồm cả mili giây) như thế nào tôi muốn không?


Bạn códate --rfc-3339=ns
Costas

Không:date: unrecognized option --rfc-3339=ns
Tschwen

Có thể là ntptimegì?
Costas

Câu trả lời:


8

Trên OpenWRT, datebusybox, trong đó có những hạn chế, nhưng đây không phải là nghiêm chỉnh một trong số họ. Vấn đề cơ bản là libc (uClibc) không hỗ trợ phần mở rộng strftime GNU này . (Mặc dù không glibc, nhiều hơn về điều đó dưới đây.)

Bạn nên có luamặc định, nhưng điều đó sẽ không có ích nếu không có một số mô-đun không mặc định khác.

hwclockgọi gettimeofday()để so sánh / cài đặt RTC (đồng hồ phần cứng), nhưng nó sẽ không xuất độ phân giải phụ thứ hai (truy cập RTC có thể đủ chậm mà dù sao nó có thể không hữu ích). Khác với OpenWRT chỉ cung cấp bản cổ rdate, chỉ có độ phân giải toàn giây.

Dường như không có cách nào đơn giản để lấy dấu thời gian chính xác trực tiếp từ đó /proc, dấu thời gian hữu ích nhất nằm ở /proc/timer_list(dòng thứ 3) là thời gian hoạt động tính bằng nano giây (độ phân giải sẽ phụ thuộc vào nền tảng).

Nếu busybox của bạn được xây dựng với CONFIG_BUSYBOX_CONFIG_ADJTIMEXbộ, thì bạn sẽ có thể sử dụng adjtimexđể đọc đồng hồ kernel (mặc dù lưu ý rằng phiên bản busybox có cả hai đối số khác nhau và đầu ra khác nhau cho adjtimex tiêu chuẩn.

Phiên bản bình thường, adjtimex -pdòng cuối cùng của đầu ra:

   raw time:  1416419719s 146628us = 1416419719.146628

Phiên bản Busybox, adjtimex(không có -p!), 3 dòng cuối:

   [...]
   time.tv_sec:  1416420386
   time.tv_usec: 732653
   return value: 0 (clock synchronized)

Goldilocks là một giải pháp tốt, giả sử bạn có thiết lập xây dựng chéo OpenWRT (rất khuyến khích!). Giải pháp coreutils-date của bạn hoạt động vì trong khi coreutils nhận biết được glibc, nó không chỉ là glibc. Nó đi kèm với việc triển khai độc lập strftime(bắt nguồn từ glibc) và sử dụng nó để bọc (thông qua strftime_case()) bên dưới strftimeđể hỗ trợ các phần mở rộng khác nhau (và quay lại phiên bản uClibc khác).

Thậm chí glibc (tính đến hiện tại 2,23) không hỗ trợ %N, các coreutils strftime()có nguồn gốc từ các phiên bản glibc kinh điển thêm %N%:zvà một vài thay đổi khác. Biến thể và phiên bản vá của strftime()rất nhiều (bao gồm cả phiên bản trong bash và gawk).


1
Cảm ơn. awk 'NR==3 {print $3}' /proc/timer_listtrở nên hữu ích cho tôi khi tôi cần thời gian giải quyết tốt hơn bất kỳ tiện ích nào được cấp theo bản dựng tôi đang sử dụng.
rkagerer

Dòng /proc/timer_listthứ 3 hoạt động với tôi. Không sử dụng openwrt, chỉ là một linux nhúng trên busybox.
minghua

Bộ định tuyến OpenWRT của tôi không có /proc/timer_listvà cũng thiếu một adjtimexlệnh, nhưng tôi có thể sử dụng /proc/uptimecho độ phân giải 100 giây và logread -tcho độ phân giải mili giây; xem câu trả lời của tôi dưới đây Không có gói bổ sung hoặc biên dịch cần thiết.
Adam Katz

4

Nếu bạn có trình biên dịch C trên đó và không thể tìm thấy gì khác, điều này sẽ báo cáo, vd

> millitime && sleep 1 && millitime
14/11/2014 9:39:49:364
14/11/2014 9:39:50:368
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

int main (void) {
    struct timeval now;
    struct tm *parsed;

    if (gettimeofday(&now, NULL) == -1) {
        fprintf (
            stderr,
            "gettimeofday() failed: %s\n",
            strerror(errno)
        );
        return 1;
    }

    parsed = localtime((const time_t*)&now.tv_sec);
    if (!parsed) {
        fprintf (
            stderr,
            "localtime() failed: %s\n",
            strerror(errno)
        );
        return 1;
    }

    printf (
        "%d/%d/%d %d:%02d:%02d:%03d\n",
        parsed->tm_mday,
        parsed->tm_mon + 1,
        parsed->tm_year + 1900,
        parsed->tm_hour,
        parsed->tm_min,
        parsed->tm_sec,
        now.tv_usec / 1000
    );

    return 0;
}  

Với gcc, chỉ cần biên dịch nó gcc whatever.c -o millitime. Nếu có lỗi (sẽ rất kỳ lạ), nó sẽ báo cáo cho thiết bị lỗi chuẩn và thoát với trạng thái là 1. Nếu không, nó sẽ báo cáo về thiết bị xuất chuẩn và thoát 0.

Các mili giây được làm tròn xuống từ micro giây.


2

Thật ra cũng có một gói gọi là coreutils-date! Không biết về nó! Có tất cả các chức năng tiêu chuẩn được bao gồm!


1

Tôi thấy rằng tôi có thể lấy thông tin thời gian đến một phần trăm giây từ /proc/uptimemột hệ thống nhúng Linux với busybox, mặc dù việc chuyển đổi nó thành thời gian kỷ nguyên là khó khăn vì tôi không thể tìm thấy bất kỳ bản ghi nào về thời gian khởi động.

Đây là một số mã sh nhanh:

read UPTIME < /proc/uptime
UPTIME="${UPTIME%%[^0-9.]*}"

Nếu tất cả những gì bạn cần là một bộ đếm thời gian với độ chính xác đến 100 giây, thì điều này là đủ tốt (thời gian 0, thời gian địa phương của bạn, sẽ chỉ là thời gian hệ thống khởi động).

Nếu không, bạn có thể gian lận và kích hoạt một mục nhật ký:

printf '\u04' |nc $HOSTNAME 22 >/dev/null 2>&1 # trigger log entry by poking ssh
LOG="$(logread -l 1 -t)"                       # read last log entry w/ timestamp
LOG="${LOG##* 20?? \[}"                        # remove text before timestamp
echo "${LOG%%\]*}"                             # remove text after timestamp

Điều này tiêu tốn 0,026 giây (trung vị; min = 0,025s, max = 0,028s, trong số 12 lần chạy). Cũng lưu ý rằng nó làm ô nhiễm nhật ký của bạn ... và tôi không biết điều gì xảy ra khi nhật ký "quá lớn" (đối với bất kỳ giá trị nào); ghi nhớ nhật ký được lưu trữ trong bộ nhớ.

Do đó, nếu bạn sẽ bỏ phiếu nhiều thời gian, có lẽ bạn chỉ nên tạo một mục nhật ký xấu ngay từ đầu và kết hợp hai phương pháp trên như sau:

get_uptime_ms() {
  local MS
  read UPTIME < /proc/uptime
  UPTIME="${UPTIME%%[^0-9.]*}" # strip the other listed time
  MS="${UPTIME##*.}0"          # extra zero turns 100ths of seconds into 1000ths
  UPTIME="${UPTIME%%.*}$MS"    # the time since boot in milliseconds
}

get_uptime_ms
printf '\u04' |nc $HOSTNAME 22 >/dev/null 2>&1  # poke ssh to trigger log entry 
NOW="$(logread -l 1 -t)"                        # last log entry, with timestamp
NOW="${NOW#* 20?? \[}"                          # remove text before timestamp
NOW="${NOW%%\]*}"                               # remove text after timestamp
MS="${NOW##*.}"                                 # just the milliseconds
NOW="${NOW%%.*}$MS"                             # append ms to s w/out dot

BOOT=$(( NOW - UPTIME ))

date_ms() {
  local S
  get_uptime_ms
  NOW=$(( BOOT + UPTIME))
  S="${NOW%???}"           # $NOW in seconds
  echo "$S.${NOW#$S}"      # put the dot back in, then append milliseconds
}

# now you can run date_ms as much as you like
# and it'll have the epoch time with millisecond precision.

date_ms
date +%s
date_ms

Điều này thiết lập một cái gì đó để sử dụng làm thời gian khởi động ban đầu bằng cách trừ thời gian hoạt động (xuống 100 giây) từ dấu thời gian của trình kích hoạt nhật ký (chính xác đến 1000 giây) và sau đó cập nhật nó với thời gian hoạt động. Do đó, điều này mang lại 100 giây của một giây chứ không phải 1000 giây, nhưng nó chỉ đưa thêm một mục nhập vào logd và phương pháp khác không chính xác hơn nhiều vì nó tốn 0,026 giây để chạy. (Nó phù hợp với hệ thống của tôi, vì vậy bạn có thể xem xét việc chạy nó một loạt lần và trừ đi giá trị đó.)


0

Các adjtimexgiải pháp không làm việc cho tôi, vì tôi nhận được adjtimex: Operation not permitted(Tôi đang sử dụng một thùng chứa Docker leo núi). Nhưng tôi đã tìm thấy một giải pháp kỳ lạ nhưng không phụ thuộc bằng cách sử dụng nmetercó thể hiển thị lên đến micro giây.

#-d0 means print every 0 seconds ie immediately
# head -n1 makes sure it only does it once
nmeter -d0 '%3t' | head -n1

Vì vậy, kết hợp điều này với lệnh date:

date -u +%FT$(nmeter -d0 '%3t' | head -n1)Z
# 2017-05-03T04:10:57.863Z

Chúng tôi nhận được một chuỗi thời gian UTC rfc3339 với mili giây!


-1
#include <stdio.h>
#include <time.h>
void main (void){
    long            ms; 
    time_t          s; 

    struct timespec spec;
    clock_gettime(CLOCK_REALTIME, &spec);
    s  = spec.tv_sec;
    ms = (spec.tv_nsec / 1.0e6); 
    printf("%d%03d\n", s, ms);      
return 0;
}

Điều này trong ngôn ngữ C và hoạt động như coreutils-date date +%s%3N. Nó được biên dịch sang bộ định tuyến OpenWrt với OpenWrt-SDK.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.