Linux中的时间函数

参考:

几种取时间的方法(附代码)

Linux中gmtime和localtime的区别(time_t格式转换为tm格式)

C 库函数 - time()

mktime和localtime_r能在多线程环境下使用么?

Linux_C环境编程:时间日期函数总结

细说时间测量RDTSC和RDTSCP

一、C标准库time.h

    C的标准库定义了函数的获取方法,关于函数的数据类型有:time_t、clock_t和struct tm。

  1. time_t:long long型
  2. clock_t:long long型
  3. struct tm

 1.1 时间获取函数tmie()

time_t time( time_t *arg );// 用法
time_t t;
time(&t);
t = time(NULL);

time()返回自 1970年来的时间,以秒为单位。

1.2 时间转换函数

 图中展示了时间转换函数:(1)gmtime、loacltime;(2)mktime;(3)asctime;(4)strftime。

gmtime和loacltime函数

struct tm *gmtime(const time_t *timer)
struct tm *localtime(const time_t *timer)

从函数声明上两个函数一样。但是区别在于:gmtime是将输入转换成0时区,如果想转换成北京时间,需要在年数上加1900,月份上加1(因为是从0开始的),小时数加上8;而localtime是转换成本地时区,还句话来说,是根据系统设置的时区tzname/timezone/daylight转换成设定的时区,如果想转换成北京时间,需要在年数上加1900,月份上加1;

    这里有没有好奇,输入是time_t的指针,但是输出一个指向struct tm的指针,这个指针哪来的?指向哪?看一个示例

#include <stdio.h>
#include <time.h>int main()
{time_t t = time(NULL);struct tm* ttm = localtime(&t);printf("%d-%d-%d %d:%d:%d\n", ttm->tm_year + 1900, ttm->tm_mon + 1,ttm->tm_mday, ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
}

这也是这两个函数的问题所在:其返回的结果是time.h文件中定义的全局静态变量。这就造成了多线程安全的问题。因此多线程下,可能获得时间被其他线程修改了。因此linux中有了线程安全的函数,也在头文件time.h中。

struct tm *gmtime_r( const time_t *timer, struct tm *buf );
struct tm *localtime_r(const time_t *timep, struct tm *buf);

也就是输入变成两个,返回的指针就是输入的buf指针。例如

#include <stdio.h>
#include <time.h>int main()
{time_t t = time(NULL);struct tm ttm;localtime_r(&t, &ttm);printf("%d-%d-%d %d:%d:%d\n", ttm->tm_year + 1900, ttm->tm_mon + 1,ttm->tm_mday, ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
}

这样就是线程安全的?然而并不是的,在mktime和localtime_r能在多线程环境下使用么?文章中说这localtime_r函数都考虑了时区转换,而时区的计算要使用全局变量tzname/timezone/daylight。这本质上就是线程不安全的。

mktime函数

time_t mktime(struct tm *timeptr)

mktime是一个逆过程,将tm的时间转换成秒数。同样,考虑了时区转换,本质上就是线程不安全的。

asctime和strftime函数

    这两个函数都是将tm结构体转换成字符串。

char* asctime(const struct tm *timeptr);
char* asctime_r( const struct tm* time_ptr, char* buf );

从函数原型上看,上面第一个函数也是线程不安全的,莫名其妙输出一个指向指针。下面则是线程安全版本。首先,第一个函数输出固定形式是

Www Mmm dd hh:mm:ss yyyy\nWww ——来自 time_ptr->tm_wday 的星期之日的三字母英文缩写, Mon 、 Tue 、 Wed 、 Thu 、 Fri 、 Sat 、 Sun 之一。 
Mmm ——来自 time_ptr->tm_mon 的月名的三字母英文缩写, Jan 、 Feb 、 Mar 、 Apr 、 May 、 Jun 、 Jul 、 Aug 、 Sep 、 Oct 、 Nov 、 Dec 之一。
dd ——来自 timeptr->tm_mday 的 2 位月之日,如同由 sprintf 以 %2d 打印
hh ——来自 timeptr->tm_hour 的 2 位时,如同由 sprintf 以 %.2d 打印
mm ——来自 timeptr->tm_min 的 2 位分,如同由 sprintf 以 %.2d 打印
ss ——来自 timeptr->tm_sec 的 2 位秒,如同由 sprintf 以 %.2d 打印
yyyy ——来自 timeptr->tm_year + 1900 的 4 位年,如同由 sprintf 以 %4d 打印

而下面那个函数输出形式和第一个一样,但是就存在一个问题,就是buf大小小于26可能就会越界。因此,推荐使用strftime函数。

    strftime函数原型如下:

size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
str -- 这是指向目标数组的指针,用来复制产生的 C 字符串。
maxsize -- 这是被复制到 str 的最大字符数。
format -- 这是 C 字符串,包含了普通字符和特殊格式说明符的任何组合。这些格式说明符由函数替换为表示 tm 中所指定时间的相对应值。格式说明符是:
说明符替换为实例
%a缩写的星期几名称Sun
%A完整的星期几名称Sunday
%b缩写的月份名称Mar
%B完整的月份名称March
%c日期和时间表示法Sun Aug 19 02:56:02 2012
%d一月中的第几天(01-31)19
%H24 小时格式的小时(00-23)14
%I12 小时格式的小时(01-12)05
%j一年中的第几天(001-366)231
%m十进制数表示的月份(01-12)08
%M分(00-59)55
%pAM 或 PM 名称PM
%S秒(00-61)02
%U一年中的第几周,以第一个星期日作为第一周的第一天(00-53)33
%w十进制数表示的星期几,星期日表示为 0(0-6)4
%W一年中的第几周,以第一个星期一作为第一周的第一天(00-53)34
%x日期表示法08/19/12
%X时间表示法02:50:06
%y年份,最后两个数字(00-99)01
%Y年份2012
%Z时区的名称或缩写CDT
%%一个 % 符号%

 例如

#include <stdio.h>
#include <time.h>int main ()
{time_t rawtime;struct tm *info;char buffer[80];time( &rawtime );info = localtime( &rawtime );strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);printf("格式化的日期 & 时间 : |%s|\n", buffer );return(0);
}// 输出
// 格式化的日期 & 时间 : |2018-09-19 08:59:07|

ctime函数

char *ctime(const time_t *timer)

同理,输出是Www Mmm dd hh:mm:ss yyyy\n形式

1.3 时间差值函数difftime()

    time.h还提供了计算两个时间差的函数difftime()

double difftime(time_t time1, time_t time2)

有点奇怪,为什么返回值是double,解释说:在POSIX系统中,time_t是以秒为单位计量的,并且difftime相当于算术减法,但C和C++允许time_t具有分数单位。

1.4 clock函数

    在time.h中还定义了一种用于程序计时的函数clock

clock_t clock(void)

需要配合CLOCKS_PER_SEC一起使用,这个返回的tick数目是从程序开始运行时的tick数,在 32 位系统中,CLOCKS_PER_SEC 等于 1000000,该函数大约每 72 分钟会返回相同的值。在64位系统中clock_t时64位。

#include <time.h>
#include <stdio.h>int main()
{clock_t start_t, end_t;double total_t;int i;start_t = clock();printf("程序启动,start_t = %ld\n", start_t);printf("开始一个大循环,start_t = %ld\n", start_t);for(i=0; i< 10000000; i++){}end_t = clock();printf("大循环结束,end_t = %ld\n", end_t);total_t = (double)(end_t - start_t) / CLOCKS_PER_SEC;printf("CPU 占用的总时间:%f\n", total_t  );printf("程序退出...\n");return(0);
}/*输出
程序启动,start_t = 2614
开始一个大循环,start_t = 2614
大循环结束,end_t = 28021
CPU 占用的总时间:0.025407
程序退出...
*/

1.5 timespec_get函数

    前面的时间精度很低,只到秒,有时候,需要更精确的时间,所以可以有了这个函数

int timespec_get(struct timespec *ts, int base);

这里涉及到另一个数据结构struct timespec

struct timespec {time_t tv_sec;  // 秒long   tv_nsec; // 纳秒
};

base:时间基准常量,C11 标准定义了 TIME_UTC,表示协调世界时 (UTC)。

若成功则为 base 的值,否则为零。

1.6 clock_gettime函数

    time.h文件另一个用于获取高分辨率时间的函数,但需要 POSIX 支持(至少对于unix系统是满足的)。

int clock_gettime(clockid_t clk_id, struct timespec *tp);

 cld_id有四种类型:

  • CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变
  • CLOCK_MONOTONIC,从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
  • CLOCK_PROCESS_CPUTIME_ID,本进程到当前代码系统CPU花费的时间
  • CLOCK_THREAD_CPUTIME_ID,本线程到当前代码系统CPU花费的时间

成功时,这些函数返回 0;否则,这些函数返回 -1 ,并设置 errno,其时间精度依然可以达到纳秒。实际上,还有类似用于设置时间和进程休眠的函数clock_settime、clock_nanosleep。而clock_getres则是用于获取时钟分辨率的

int clock_getres(clockid_t clk_id, struct timespec *res);

函数返回由 clk_id 指定的时钟的分辨率,并将其放置在res指向的位置。但是,如果res为 NULL,则不返回任何分辨率。 

二、linux提供的时间函数

    linux系统本身提供了一个时间接口函数在头文件sys/time.h中,函数名称:gettimeofday

int gettimeofday(struct timeval *tv, struct timezone *tz);

gettimeofday()会把目前的时间用tv 结构体返回,当地时区的信息则放到tz所指的结构中,日常使用中并不会获得时区信息,所以第二个参数一般为null。

struct timeval{long tv_sec;  /*秒*/long tv_usec; /*微妙*/
};struct timezone{int tz_minuteswest;/*和greenwich 时间差了多少分钟*/int tz_dsttime;    /*type of DST correction*/
}

两个时间结构体如上。相比时间库time.h,比time精度高,但比timespec_get精度低。同样,可以配合tm结构一起使用,例如

#include <stdio.h>
#include <sys/time.h>
#include <time.h>// gcc -o time_2 time_2.cint main()
{struct timeval tm_now;//1.获取当前时间戳(tv_sec, tv_usec)gettimeofday(&tm_now,NULL); // 第二个参数是时区//2.转换成本地时间,精确到秒struct tm *p_local_tm;p_local_tm = localtime(&tm_now.tv_sec) ;printf("now datetime: %04d-%02d-%02d %02d:%02d:%02d.%06ld\n",p_local_tm->tm_year+1900, p_local_tm->tm_mon+1, p_local_tm->tm_mday, p_local_tm->tm_hour, p_local_tm->tm_min, p_local_tm->tm_sec,tm_now.tv_usec); // 有微秒时间戳了return 0;
}

三、更高级版本

    更高级的时间获取版本不在经过系统或者库函数,而是通过内联汇编直接读取cpu内部保存时间的寄存器。

uint64_t get_tsc() // TSC == Time Stamp Counter寄存器
{
#ifdef __i386__uint64_t x;__asm__ volatile("rdtsc" : "=A"(x));return x;
#elif defined(__amd64__) || defined(__x86_64__)uint64_t a, d;__asm__ volatile("rdtsc" : "=a"(a), "=d"(d));return (d << 32) | a;
#else // ARM架构CPUuint32_t cc = 0;__asm__ volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (cc));return (uint64_t)cc; 
#endif
}

以x86_64为例(因为看不懂其他的),通过rdtsc指令,可以将TSC的数值存放在EDX:EAX中,然后读取寄存器的数值。

-----------------------------------以下内容全部来自 细说时间测量RDTSC和RDTSCP----------------------

3.1 TSC的坑

    TSC曾经是一个极高精度,极低开销的取时间的方法,但是随着CPU往多核、多处理器、低功耗的方向上走,在使用TSC时就会遇到很多坑。

【坑1】比如有的CPU会根据机器负载情况动态调节工作频率, 那么单位时间CPU的指令周期数就会发生变化,也就很难将其转换成时间。另外,CPU进入休眠再次重启后,TSC会清零。

【坑2】再比如,在同一处理器的多个核心之间,以及不同处理器的不同核心之间,rdtsc的结果是否是同步的呢?如果不同步,那么取时的结果就不能用来相互比较。

【坑3】再比如,Intel的处理器自Pentium Pro开始,引入了乱序执行的功能,导致程序读取的TSC结果可能不准。如果编写测试程序的时候没有主动回避,也可能会掉到坑里。

3.2 官方填坑

在较新版本的CPU中,引入了常量速率TSC的特性(constant rate TSC)。可以通过如下命令查看你的CPU是否支持(我的机器有四个核,因此输出了四条):cat /proc/cpuinfo | grep constant_tsc

 支持该特性的CPU,其TSC是按照其标称频率流逝的,与CPU的实际工作频率与状态无关。如果你的CPU也是支持constant_tsc特性的,那么【坑1】算是填上了。

    关于【坑2】,即不同核心读取的tsc是否同步,目前没有找到统一的说法,Intel的官方手册也没有明说,比如:vol 3b,17.15.1 Invariant TSC章节

  The time stamp counter in newer processors may support an enhancement, referred to as invariant TSC. Processor’s support for invariant TSC is indicated by CPUID.80000007H:EDX[8].
The invariant TSC will run at a constant rate in all ACPI P-, C-. and T-states. This is the architectural behavior moving forward. On processors with invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers). TSC reads are much more efficient and do not incur the overhead associated with a ring transition or access to a platform resource.

    但这里面只是说TSC能够在CPU处于任何(电源)状态下都能保证以标称速率递增,并没有明确说明TSC能够在多核甚至多处理器的情况下保持同步。另一个蛛丝马迹是在Linux内核代码中

这里有一个unsynchronized_tsc()函数,用于判断系统的TSC是不是同步的,代码实现如下:

/** Make an educated guess if the TSC is trustworthy and synchronized* over all CPUs.*/
int unsynchronized_tsc(void)
{if (!boot_cpu_has(X86_FEATURE_TSC) || tsc_unstable)return 1;#ifdef CONFIG_SMPif (apic_is_clustered_box())return 1;
#endifif (boot_cpu_has(X86_FEATURE_CONSTANT_TSC))return 0;if (tsc_clocksource_reliable)return 0;/** Intel systems are normally all synchronized.* Exceptions must mark TSC as unstable:*/if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) {/* assume multi socket systems are not synchronized: */if (num_possible_cpus() > 1)return 1;}return 0;
}

这里有几个有意思的点:

  • 开头的注释说,“make an educated guess”,即有根据的猜测,即这里是不是TSC同步的判断依然是一个猜测
  • 中间的代码判断了是否开启了CONSTANT TSC特性,如果开启就直接返回0,即TSC是同步的,也就是说,只要我们在cpuinfo里看到constant_tsc的flag,就证明我们的机器的TSC是同步的
  • 后面还有一句注释“Intel systems are normally all synchronized.Exceptions must mark TSC as unstable:”,即Intel的系统,只要用户没有手动禁用TSC同步,一般都是同步的。
  • 在Intel CPU下还有一个注释“assume multi socket systems are not synchronized”,即在多处理器系统上,不同CPU(处理器、socket、NUMA节点)之间的TSC是不同步的。

看到这里,我们基本上可以确定了,即:

  • 如果你的cpuinfo里有constant_tsc的flag,那么无论在同一CPU不同核心之间,还是在不同CPU的不同核心之间,TSC都是同步的,可以随便用
  • 如果你用的是Intel的CPU,但是cpuinfo里没有constant_tsc的flag,那么在同一处理器的不同核心之间,TSC仍然是同步的,但是不同CPU的不同核心之间不同步,尽量不要用

至此,【坑2】也基本上解决了。

关于【坑3】,即乱序执行问题,可以使用RDTSCP命令来代替RDTSC,前者开销虽然略高,但胜在稳定好用。另外,如果不想用这个指令,还可以用memory barrier技术(后面的文章中我们将详细解释该技术)或者CPUID指令来实现,不过这两者我都没试,据说开销也不小,详细资料可以就参见参考资料中的wiki页面和intel的官方手册。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> 
#include <stdint.h>
#include <time.h>
#include <sys/time.h>// gcc -o time_6 time_6.cuint64_t get_tsc()
{uint64_t a, d;__asm__ volatile("rdtsc" : "=a"(a), "=d"(d));return (d << 32) | a;
}uint64_t get_tscp()
{uint64_t a, d;__asm__ volatile("rdtscp" : "=a"(a), "=d"(d));return (d << 32) | a;
}#define LOOP_TIMES 1000000000int main(int argc, char **argv)
{uint64_t beg_tsc, end_tsc;long loop;long sum;printf("-------------rdtsc-------------\n");loop = LOOP_TIMES;sum = 0;while(loop--){beg_tsc = get_tsc(); end_tsc = get_tsc();sum += (end_tsc - beg_tsc);}printf("AVG_CYCLE : %ld\n", sum / LOOP_TIMES);sleep(1);printf("-------------rdtscp-------------\n");loop = LOOP_TIMES;sum = 0;while(loop--){beg_tsc = get_tscp(); end_tsc = get_tscp();sum += (end_tsc - beg_tsc);}printf("AVG_CYCLE : %ld\n", sum / LOOP_TIMES);return 0;
}

测试结果如下:

 

我一共跑了三次,每次差别都不大,RDTSCP指令比RDTSC多耗费10个指令周期左右,慢不到1倍。如果你能接受这点差别,建议还是用RDTSCP命令吧。另外,RDTSCP指令也是需要平台支持的,是否支持可以使用cat /proc/cpuinfo | grep rdtscp命令查看

3.3 使用建议

  • 如果你的cpuinfo里面没有constant_tsc的flag,建议老老实实用clock_gettime吧,或者换台支持constant_tsc的机器
  • 如果你的cpuinfo里面有constant_tsc的flag,那么在同一处理器的不同核心之间可以放心使用TSC,跨处理器的不同核之间,尽量避免使用,可能会有未知的问题
  • 如果不是对性能极其敏感,尽量使用RDTSCP代替RDTSC,前者略慢,但能避免CPU乱序执行问题

参考资料

再论 Time stamp counter - 一念天堂 - 博客园

Pitfalls of TSC usage | Oliver Yang

linux - rdtsc accuracy across CPU cores - Stack Overflow

另外C++封装代码:GitHub - MengRao/tscns: A low overhead nanosecond clock based on x86 TSC

 -----------------------------------------至此,上面全部来自那篇博客的内容已结束-----------------------------

四、总结

最后放上还是上面那个博客测试的上面的时间函数的性能(没办法,这个人太厉害了.....)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> 
#include <stdint.h>
#include <time.h>
#include <sys/time.h>// gcc -o time_5 time_5.cuint64_t get_by_time()
{time_t tm_now;time(&tm_now);return tm_now;
}uint64_t get_by_gettimeofday()
{struct timeval tm_now;gettimeofday(&tm_now,NULL);return tm_now.tv_sec;
}uint64_t get_by_clock_gettime()
{struct timespec tm_now;clock_gettime(CLOCK_REALTIME, &tm_now);return tm_now.tv_sec;
}uint64_t get_cpu_freq()
{FILE *fp=popen("lscpu | grep CPU | grep MHz | awk  {'print $3'}","r");if(fp == NULL)return 0;char cpu_mhz_str[200] = { 0 };fgets(cpu_mhz_str,80,fp);fclose(fp);return atof(cpu_mhz_str) * 1000 * 1000;
}uint64_t get_by_tsc()
{uint64_t a, d;__asm__ volatile("rdtsc" : "=a"(a), "=d"(d));return (d << 32) | a;
}void print_diff(uint64_t loop_times, uint64_t beg_tsc, uint64_t end_tsc)
{   double tt_ns = (end_tsc - beg_tsc) * 1.0 * 1000 * 1000 * 1000 / get_cpu_freq();printf("Number Loop :   %lu\n", loop_times);printf("Total Time  :   %.02lf ns\n", tt_ns);printf("Avg Time    :   %.02lf ns\n", tt_ns / loop_times);
}#define LOOP_TIMES 1000000000int main(int argc, char **argv)
{uint64_t beg_tsc, end_tsc;long loop;printf("-------------time()-------------\n");loop = LOOP_TIMES;beg_tsc = get_by_tsc(); while(loop--)get_by_time();end_tsc = get_by_tsc();print_diff(LOOP_TIMES, beg_tsc, end_tsc);printf("-------------gettimeofday()-------------\n");loop = LOOP_TIMES;beg_tsc = get_by_tsc(); while(loop--)get_by_gettimeofday();end_tsc = get_by_tsc();print_diff(LOOP_TIMES, beg_tsc, end_tsc);printf("-------------clock_gettime()-------------\n");loop = LOOP_TIMES;beg_tsc = get_by_tsc(); while(loop--)get_by_clock_gettime();end_tsc = get_by_tsc();print_diff(LOOP_TIMES, beg_tsc, end_tsc);printf("-------------rdtsc-------------\n");loop = LOOP_TIMES;beg_tsc = get_by_tsc(); while(loop--)get_by_tsc();end_tsc = get_by_tsc();print_diff(LOOP_TIMES, beg_tsc, end_tsc);return 0;
}

 

可以看到:

  • time函数最快,但是精度太低
  • gettimeofday和clock_gettime虽然精度高,但是都比较慢
  • rdtsc精度和速度都十分优秀

另外需要注意一点的是,上述测试结果跟机器配置有很大关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1485951.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

乐鑫AWS IoT ExpressLink方案,简化物联网设备连接AWS IoT服务

在现代科技迅速发展的今天&#xff0c;物联网&#xff08;IoT&#xff09;已经成为连接物理世界与数字世界的重要桥梁&#xff0c;越来越多的设备开始接入网络&#xff0c;实现智能化控制。 在这个大背景下&#xff0c;乐鑫携手亚马逊&#xff0c;推出了AWS IoT ExpressLink方…

免费视频批量横转竖工具

简介 视频处理器 v1.3 是一款由是貔貅呀开发的视频编辑和处理工具&#xff0c;提供高效便捷的视频批量横转竖&#xff0c;主要功能&#xff1a; 导入与删除文件&#xff1a;轻松导入多个视频文件&#xff0c;删除不必要的文件。暂停与继续处理&#xff1a;随时暂停和继续处理。…

开源AI智能名片S2B2C商城小程序在社群团购模式中的应用与探索

摘要 本文深入探讨了开源AI智能名片S2B2C商城小程序在社群团购模式中的创新应用与未来发展。通过详细分析社群团购模式的特征、发展趋势及其面临的挑战&#xff0c;结合开源AI智能名片S2B2C商城小程序的技术优势与实际应用案例&#xff0c;本文提出了一系列旨在提升社群团购效…

centos中zabbix安装、卸载及遇到的问题

目录 Zabbix简介Zabbix5.0和Zabbix7.0的区别监控能力方面模板和 API 方面性能、速度方面 centos7安装Zabbix(5.0)安装zabbix遇到的问题卸载Zabbix Zabbix简介 Zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。zabbix 能监视各种网络参…

Android ContentResolver.loadThumbnail转Kotlin

Android ContentResolver.loadThumbnail转Kotlin loadThumbnail原先是Java实现的&#xff0c;现在抠出来转Kotlin实现。 private fun loadThumbnail(uri: Uri, size: Size, signal: CancellationSignal): Bitmap {return myLoadThumbnail(mContext?.contentResolver!!, uri, s…

『 Linux 』信号概念与信号的产生

文章目录 信号概念前台进程与后台进程信号的本质硬件理解信号的产生 信号概念 "信号"一词指用来传达信息或只是的各种形式的提示或标志; 在生活中常见的信号例如红绿灯,交通标志,短信通知等 在操作系统中,"信号"是一种用于异步通知进程发生特定事件的机制;…

DevExpress中文教程 - 如何在.NET MAUI应用中实现Material Design 3?

DevExpress .NET MAUI多平台应用UI组件库提供了用于Android和iOS移动开发的高性能UI组件&#xff0c;该组件库包括数据网格、图表、调度程序、数据编辑器、CollectionView和选项卡组件等。 获取DevExpress v24.1正式版下载 Material Design是一个由Google开发的跨平台指南系统…

MySQL_CRUD

目录 一、新增 (Create) 1.1 全列插入 1.2 指定列插入 二、查询 (Retrieve) 2.1 全列查询 2.2 指定列查询 2.3 查询字段为表达式 2.4 别名 2.5 去重&#xff1a;distinct 2.6 排序&#xff1a;order by 2.7 条件查询&#xff1a;where 2.8 分页查询&#xff1a;lim…

安装Ubuntu24.04服务器版本

Ubuntu系统安装 一.启动安装程序二.执行 Ubuntu Server 安装向导1.选择安装程序语言&#xff0c;通常选择「English」2.设置键盘布局&#xff0c;默认「English US」即可3.选择安装方式 三.配置网络1.按Tab键选择网络接口&#xff08;例如 ens160&#xff09;&#xff0c;然后按…

项目实战二 HIS项目

目标&#xff1a; 项目的操作流程&#xff1a; 开发体系 前端开发&#xff1a;负责页面的编写 HTML CSS JavaScript 后端开发&#xff1a;看不到 摸不着的功能 常用开发语言 PHP JAVA Python 框架 &#xff1a; 半成品 做好的功能模块 版本控制 Git 分布式版本控…

乐理基础知识

为了学习无源蜂鸣器播放音乐&#xff0c;我去学习了乐理知识&#xff0c;发现只要把握了音调和音值&#xff0c;也不算太难&#xff0c;我整理了笔记&#xff0c;现在分享出来 声音 声音是由物体振动产生的声波。 其主要特征如下&#xff1a; 1.音调指声音的高低&#xff0…

数据编织 VS 数据仓库 VS 数据湖

目录 1. 什么是数据编织?2. 数据编织的工作原理3. 代码示例4. 数据编织的优势5. 应用场景6. 数据编织 vs 数据仓库6.1 数据存储方式6.2 数据更新和实时性6.3 灵活性和可扩展性6.4 查询性能6.5 数据治理和一致性6.6 适用场景6.7 代码示例比较 7. 数据编织 vs 数据湖7.1 数据存储…

分享一个最近在进行前后端联调时改了2天的bug...

场景再现 我们这边前端端口是8080 后端端口是8121 我们在前端里在首页面写了一个任务 当进入网页三秒后 发起一个叫getLoginUser的请求 我们的getLoginUser是调用的这里 一个异步请求 这边我们前端调用后端的接口也已经写好 我们先把后端跑起来 访问前端页面 接收到了这个…

Air780EP-AT开发-HTTP应用指南

简介 关联文档和使用工具&#xff1a; AT固件获取AT指令手册 概述 4G模块支持HTTP和HTTPS协议&#xff0c; HTTP应用的基本流程如下&#xff1a; 1、激活PDP&#xff08;参考&#xff1a;http://oldask.openluat.com/article/937&#xff09;2、初始化HTTP服务3、设置HTTP会话…

Http 和 Https 的区别(图文详解)

在现代网络通信中&#xff0c;保护数据的安全性和用户的隐私是至关重要的。HTTP&#xff08;Hypertext Transfer Protocol&#xff09;和 HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是两种常见的网络通信协议&#xff0c;但它们在数据保护方面的能力存在…

剧本杀小程序搭建,互联网下的游戏新体验,实现新增收!

近几年&#xff0c;桌游备受大众青睐&#xff0c;剧本杀行业更是瞬间曝火&#xff01;拥有强大社交体验与沉浸式游戏体验的剧本杀成为了众多年轻人的新宠&#xff0c;无论是外出游玩还是好友聚会&#xff0c;剧本杀游戏都成为了首选方式。 随着互联网的发展&#xff0c;线上小…

基于DPUSmartNic的云原生SDN解决方案

1. 方案背景与挑战 随着云计算&#xff0c;大数据和人工智能等技术的蓬勃发展&#xff0c;数据中心面临着前所未有的数据洪流和计算压力&#xff0c;这对SDN提出了更高的性能和效率要求。自云原生概念被提出以来&#xff0c;Kubernetes为云原生应用的落地提供了一个轻量级&am…

视频汇聚平台EasyCVR启动出现报错“cannot open shared object file”的原因排查与解决

安防视频监控EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、fmp4等&#xf…

物联网mqtt网关搭建背后的技术原理

前言 物联网是现在比较热门的软件领域&#xff0c;众多物联网厂商都有自己的物联网平台&#xff0c;而物联网平台其中一个核心的模块就是Mqtt网关。这篇文章的目的是手把手教大家写书写一个mqtt网关&#xff0c;后端存储支持Kafka/Pulsar&#xff0c;支持mqtt 连接、断链、发送…

【Java】中的List集合

目录 一、什么是List集合二、List的常用方法List的初始化元素操作1.添加元素2.删除元素3.修改元素4.查询元素 三、List集合的遍历1.for循环遍历2.增强for循环3.迭代器遍历 一、什么是List集合 List集合是最常用的一种数据结构之一。它具有动态扩容、元素添加、删除和查询等基础…