Merge branch 'systime-dev' into 'dev'

[ADD] systime dbus signal

See merge request kysdk/kysdk-system!1
This commit is contained in:
liu yunhe 2021-10-26 11:06:12 +00:00
commit 6eb7f6d723
23 changed files with 2182 additions and 0 deletions

19
systemtime/CMakeLists.txt Normal file
View File

@ -0,0 +1,19 @@
FILE(GLOB SRC_LIST_C ${PROJECT_SOURCE_DIR}/src/*.c)
FILE(GLOB TIMER_LIST_C ${PROJECT_SOURCE_DIR}/src/timer/*.c)
include(FindPkgConfig)
pkg_check_modules(PKGS REQUIRED
dbus-1
glib-2.0
dbus-glib-1
)
message("pkg cflags: ${PKGS_CFLAGS}")
message ("pkg include dirs: ${PKGS_INCLUDE_DIRS}")
find_library(GLIBC_LIB glib-2.0)
message("library :${GLIBC_LIB}")
find_library(DBUS_LIB dbus-1)
find_library(DBUS_GLIB_LIB dbus-glib-1)
find_library(THREAD_LIB pthread)
include_directories(${PKGS_INCLUDE_DIRS})
SET( EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ADD_EXECUTABLE(systime m_systime.c)
target_link_libraries(systime ${GLIBC_LIB} ${DBUS_LIB} ${DBUS_GLIB_LIB} ${THREAD_LIB} ${TIMER_LIST_C})

21
systemtime/Makefile Normal file
View File

@ -0,0 +1,21 @@
FILE=$(file)
CFLAGS=$(cflags)
CFLAGS_DBUS = $(shell pkg-config --cflags --libs dbus-1)
CFLAGS_DBUS_GLIB = $(shell pkg-config --cflags --libs dbus-glib-1)
CFLAGS += -g -Wall -Werror -O0 -std=gnu11 -Wall
CLIBS=-lpthread -pthread -lrt
CLIBS += $(clibs)
all: m_systime
m_systime: m_systime.c ./timer/libkytimer.c
gcc m_systime.c ./timer/libkytimer.c -o $@ $(CFLAGS) $(CFLAGS_DBUS) $(CFLAGS_DBUS_GLIB) $(CLIBS)
clean:
rm -f m_systime
.PHONY: all clean

BIN
systemtime/bin/systime Executable file

Binary file not shown.

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only user root can own the alsa service -->
<policy user="root">
<allow own="com.kylin.kysdk.TimeServer" />
<allow send_destination="com.kylin.kysdk.TimeServer" />
<allow receive_sender="com.kylin.kysdk.TimeServer" />
</policy>
<policy context="default">
<allow send_destination="com.kylin.kysdk.TimeServer" />
<!-- remove the following line for debug -->
<!--
<allow send_interface="com.jd.ccis"/>
-->
</policy>
</busconfig>

View File

@ -0,0 +1,12 @@
[Unit]
Description=KYLIN SDK SYSTIME DBUS
[Service]
Type=dbus
Restart=always
RestartSec=0
BusName=com.kylin.kysdk.TimeServer
ExecStart=/usr/bin/systime
[Install]
WantedBy=multi-user.target

479
systemtime/m_systime.c Normal file
View File

@ -0,0 +1,479 @@
#include <string.h>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h> /* for glib main loop */
#include "m_systime.h"
#include "./timer/libkytimer.h"
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/timerfd.h>
#include <errno.h>
pthread_mutex_t lock;
u_int8_t g_Flag;
u_int8_t g_Quit;
sem_t g_Wait;
u_int8_t g_TimeChanged;
u_int8_t g_TimeSync;
void sig_Handler(int sig)
{
g_Quit = 1;
}
static void *printClock(void *ptr)
{
DBusConnection *conn = ptr;
DBusMessage *msg;
DBusMessageIter args;
// DBusError err;
dbus_uint32_t serial = 0;
struct tm *now;
time_t current;
time(&current);
now = localtime(&current);
// printf("%04d/%02d/%02d %02d:%02d:%02d\n", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
// printf("g_TimeChanged is %d\n", g_TimeChanged);
// printf("%d",now->tm_sec);
// 如果时间发生改变发送TimeVhangeSignal信号
if (g_TimeChanged == 1)
{
char *buf = calloc(1, 128);
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
printf("%s\n", buf);
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
"com.kylin.kysdk.TimeInterface",
"TimeChangeSignal");
if (NULL == msg)
{
fprintf(stderr, "Message Null\n");
}
// append arguments onto signal
dbus_message_iter_init_append(msg, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
dbus_connection_send(conn, msg, &serial);
dbus_connection_flush(conn);
dbus_message_unref(msg);
}
// 非整点情况
if (now->tm_sec != 0)
{
pthread_mutex_lock(&lock);
// 如果出现了非整点的报时,且
// 之前处于整分钟报时的情况,说明
// 出现了唤醒偏差。重新执行一下对时
if (g_Flag == 1)
{
g_Flag = 0;
g_TimeSync = 1;
}
pthread_mutex_unlock(&lock);
sem_post(&g_Wait);
}
// 整点的情况发送TimeSignal
else
{
char *buf = calloc(1, 128);
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
// printf("%s\n", buf);
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
"com.kylin.kysdk.TimeInterface",
"TimeSignal");
if (NULL == msg)
{
fprintf(stderr, "Message Null\n");
}
// append arguments onto signal
dbus_message_iter_init_append(msg, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
dbus_connection_send(conn, msg, &serial);
dbus_connection_flush(conn);
dbus_message_unref(msg);
if (g_Flag == 0)
{
pthread_mutex_lock(&lock);
g_Flag = (1 & ~g_TimeChanged);
pthread_mutex_unlock(&lock);
sem_post(&g_Wait);
}
}
return NULL;
}
void *startBroadcastSystemTimePerMin(void *tmp)
{
DBusConnection *conn = tmp;
size_t timerID = -1;
while (!g_Quit)
{
sem_wait(&g_Wait);
if (g_TimeChanged || g_TimeSync)
{
printf("Get Time Changed signal or mis-synced. stop timerID %zd\n", timerID);
kyStopTimer(timerID);
g_TimeChanged = 0;
g_TimeSync = 0;
timerID = -1;
}
if (!g_Flag)
kyStartTimer(200, printClock, KTIMER_SINGLESHOT, KTIMER_RELATIVE, conn, 0);
else
{
timerID = kyStartTimer(1000 * 60, printClock, KTIMER_PERIODIC, KTIMER_RELATIVE, conn, 0);
printf("start periodic timer with ID %zd\n", timerID);
}
}
return NULL;
}
int monitorSystemTimeChange()
{
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
struct itimerspec its = {.it_value.tv_sec = TIME_T_MAX};
int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
if (fd < 0)
{
return -1;
}
if (timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
{
close(fd);
return -1;
}
u_int64_t dep;
ssize_t ret = read(fd, &dep, sizeof(u_int64_t));
if (ret == -1 && errno == ECANCELED)
return 1;
return 0;
}
void *actionTimeChanged(void *ptr)
{
DBusConnection *conn = ptr;
while (!g_Quit)
{
if (monitorSystemTimeChange() == 1)
{
printf("System Time Changed.\n");
g_TimeChanged = 1;
g_Flag = 0;
printClock(conn);
}
}
return NULL;
}
const char *version = "0.1";
GMainLoop *mainloop;
/*
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
* client can inspect the properties/attributes of 'TestInterface'.
*/
/*
* This is the XML string describing the interfaces, methods and
* signals implemented by our 'Server' object. It's used by the
* 'Introspect' method of 'org.freedesktop.DBus.Introspectable'
* interface.
*
* Currently our tiny server implements only 3 interfaces:
*
* - org.freedesktop.DBus.Introspectable
* - org.freedesktop.DBus.Properties
* - org.example.TestInterface
*
* 'org.example.TestInterface' offers 3 methods:
*
* - Ping(): makes the server answering the string 'Pong'.
* It takes no arguments.
*
* - Echo(): replies the passed string argument.
*
* - EmitSignal(): send a signal 'OnEmitSignal'
*
* - Quit(): makes the server exit. It takes no arguments.
*/
const char *server_introspection_xml =
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
"<node>\n"
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
" <method name='Introspect'>\n"
" <arg name='data' type='s' direction='out' />\n"
" </method>\n"
" </interface>\n"
" <interface name='org.freedesktop.DBus.Properties'>\n"
" <method name='Get'>\n"
" <arg name='interface' type='s' direction='in' />\n"
" <arg name='property' type='s' direction='in' />\n"
" <arg name='value' type='s' direction='out' />\n"
" </method>\n"
" <method name='GetAll'>\n"
" <arg name='interface' type='s' direction='in'/>\n"
" <arg name='properties' type='a{sv}' direction='out'/>\n"
" </method>\n"
" </interface>\n"
" <interface name='com.kylin.kysdk.TimeInterface'>\n"
" <signal name='TimeSignal'>\n"
" <arg name='tim' type='s' />\n"
" </signal>"
" <signal name='TimeChangeSignal'>\n"
" <arg name='tim' type='s' />\n"
" </signal>"
" </interface>\n"
"</node>\n";
/*
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
* client can inspect the properties/attributes of 'TestInterface'.
*/
DBusHandlerResult server_get_properties_handler(const char *property, DBusConnection *conn, DBusMessage *reply)
{
if (!strcmp(property, "Version"))
{
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &version,
DBUS_TYPE_INVALID);
}
else
/* Unknown property */
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (!dbus_connection_send(conn, reply, NULL))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
}
/*
* This implements 'GetAll' method of DBUS_INTERFACE_PROPERTIES. This
* one seems required by g_dbus_proxy_get_cached_property().
*/
DBusHandlerResult server_get_all_properties_handler(DBusConnection *conn, DBusMessage *reply)
{
DBusHandlerResult result;
DBusMessageIter array, dict, iter, variant;
const char *property = "Version";
/*
* All dbus functions used below might fail due to out of
* memory error. If one of them fails, we assume that all
* following functions will fail too, including
* dbus_connection_send().
*/
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
/* Append all properties name/value pairs */
property = "Version";
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property);
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &version);
dbus_message_iter_close_container(&dict, &variant);
dbus_message_iter_close_container(&array, &dict);
dbus_message_iter_close_container(&iter, &array);
if (dbus_connection_send(conn, reply, NULL))
result = DBUS_HANDLER_RESULT_HANDLED;
return result;
}
/*
* This function implements the 'TestInterface' interface for the
* 'Server' DBus object.
*
* It also implements 'Introspect' method of
* 'org.freedesktop.DBus.Introspectable' interface which returns the
* XML string describing the interfaces, methods, and signals
* implemented by 'Server' object. This also can be used by tools such
* as d-feet(1) and can be queried by:
*
* $ gdbus introspect --session --dest org.example.TestServer --object-path /org/example/TestObject
*/
DBusHandlerResult server_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
{
// DBusMessage *msg;
// DBusMessageIter args;
DBusError err;
// dbus_uint32_t serial = 0;
// char *time_now = "test";
DBusHandlerResult result;
DBusMessage *reply = NULL;
bool quit = false;
fprintf(stderr, "Got D-Bus request: %s.%s on %s\n",
dbus_message_get_interface(message),
dbus_message_get_member(message),
dbus_message_get_path(message));
/*
* Does not allocate any memory; the error only needs to be
* freed if it is set at some point.
*/
dbus_error_init(&err);
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &server_introspection_xml,
DBUS_TYPE_INVALID);
}
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get"))
{
const char *interface, *property;
if (!dbus_message_get_args(message, &err,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID))
goto fail;
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
result = server_get_properties_handler(property, conn, reply);
dbus_message_unref(reply);
return result;
}
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll"))
{
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
result = server_get_all_properties_handler(conn, reply);
dbus_message_unref(reply);
return result;
}
else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
fail:
if (dbus_error_is_set(&err))
{
if (reply)
dbus_message_unref(reply);
reply = dbus_message_new_error(message, err.name, err.message);
dbus_error_free(&err);
}
/*
* In any cases we should have allocated a reply otherwise it
* means that we failed to allocate one.
*/
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
/* Send the reply which might be an error one too. */
result = DBUS_HANDLER_RESULT_HANDLED;
if (!dbus_connection_send(conn, reply, NULL))
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_unref(reply);
if (quit)
{
fprintf(stderr, "Server exiting...\n");
g_main_loop_quit(mainloop);
}
return result;
}
const DBusObjectPathVTable server_vtable = {
.message_function = server_message_handler};
int main(void)
{
DBusConnection *conn;
DBusError err;
int rv;
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (!conn)
{
fprintf(stderr, "Failed to get a system DBus connection: %s\n", err.message);
goto fail;
}
rv = dbus_bus_request_name(conn, "com.kylin.kysdk.TimeServer", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
fprintf(stderr, "Failed to request name on bus: %s\n", err.message);
goto fail;
}
if (!dbus_connection_register_object_path(conn, "/com/kylin/kysdk/Timer", &server_vtable, NULL))
{
fprintf(stderr, "Failed to register a object path for 'TestObject'\n");
goto fail;
}
dbus_error_init(&err);
sem_init(&g_Wait, 0, 1);
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, actionTimeChanged, conn);
pthread_mutex_init(&lock, NULL);
/* connect to the daemon bus */
/*
* For the sake of simplicity we're using glib event loop to
* handle DBus messages. This is the only place where glib is
* used.
*/
pthread_attr_t tt;
pthread_t dd;
pthread_attr_init(&tt);
pthread_attr_setdetachstate(&tt, PTHREAD_CREATE_DETACHED);
pthread_create(&dd, &tt, startBroadcastSystemTimePerMin, conn);
printf("Starting dbus tiny server v%s\n", version);
mainloop = g_main_loop_new(NULL, false);
/* Set up the DBus connection to work in a GLib event loop */
dbus_connection_setup_with_g_main(conn, NULL);
/* Start the glib event loop */
g_main_loop_run(mainloop);
return EXIT_SUCCESS;
fail:
dbus_error_free(&err);
return EXIT_FAILURE;
}

13
systemtime/m_systime.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef DBUS_M_SYSTIME_SERVER_H__
#define DBUS_M_SYSTIME_SERVER_H__
// extern void *startBroadcastSystemTimePerMin();
/**
* @brief 1
*
* @return int
*/
extern int monitorSystemTimeChange();
#endif

479
systemtime/src/m_systime.c Normal file
View File

@ -0,0 +1,479 @@
#include <string.h>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h> /* for glib main loop */
#include "m_systime.h"
#include "./timer/libkytimer.h"
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/timerfd.h>
#include <errno.h>
pthread_mutex_t lock;
u_int8_t g_Flag;
u_int8_t g_Quit;
sem_t g_Wait;
u_int8_t g_TimeChanged;
u_int8_t g_TimeSync;
void sig_Handler(int sig)
{
g_Quit = 1;
}
static void *printClock(void *ptr)
{
DBusConnection *conn = ptr;
DBusMessage *msg;
DBusMessageIter args;
// DBusError err;
dbus_uint32_t serial = 0;
struct tm *now;
time_t current;
time(&current);
now = localtime(&current);
// printf("%04d/%02d/%02d %02d:%02d:%02d\n", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
// printf("g_TimeChanged is %d\n", g_TimeChanged);
// printf("%d",now->tm_sec);
// 如果时间发生改变发送TimeVhangeSignal信号
if (g_TimeChanged == 1)
{
char *buf = calloc(1, 128);
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
printf("%s\n", buf);
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
"com.kylin.kysdk.TimeInterface",
"TimeChangeSignal");
if (NULL == msg)
{
fprintf(stderr, "Message Null\n");
}
// append arguments onto signal
dbus_message_iter_init_append(msg, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
dbus_connection_send(conn, msg, &serial);
dbus_connection_flush(conn);
dbus_message_unref(msg);
}
// 非整点情况
if (now->tm_sec != 0)
{
pthread_mutex_lock(&lock);
// 如果出现了非整点的报时,且
// 之前处于整分钟报时的情况,说明
// 出现了唤醒偏差。重新执行一下对时
if (g_Flag == 1)
{
g_Flag = 0;
g_TimeSync = 1;
}
pthread_mutex_unlock(&lock);
sem_post(&g_Wait);
}
// 整点的情况发送TimeSignal
else
{
char *buf = calloc(1, 128);
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
// printf("%s\n", buf);
msg = dbus_message_new_signal("/com/kylin/kysdk/Timer",
"com.kylin.kysdk.TimeInterface",
"TimeSignal");
if (NULL == msg)
{
fprintf(stderr, "Message Null\n");
}
// append arguments onto signal
dbus_message_iter_init_append(msg, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf);
dbus_connection_send(conn, msg, &serial);
dbus_connection_flush(conn);
dbus_message_unref(msg);
if (g_Flag == 0)
{
pthread_mutex_lock(&lock);
g_Flag = (1 & ~g_TimeChanged);
pthread_mutex_unlock(&lock);
sem_post(&g_Wait);
}
}
return NULL;
}
void *startBroadcastSystemTimePerMin(void *tmp)
{
DBusConnection *conn = tmp;
size_t timerID = -1;
while (!g_Quit)
{
sem_wait(&g_Wait);
if (g_TimeChanged || g_TimeSync)
{
printf("Get Time Changed signal or mis-synced. stop timerID %zd\n", timerID);
kyStopTimer(timerID);
g_TimeChanged = 0;
g_TimeSync = 0;
timerID = -1;
}
if (!g_Flag)
kyStartTimer(200, printClock, KTIMER_SINGLESHOT, KTIMER_RELATIVE, conn, 0);
else
{
timerID = kyStartTimer(1000 * 60, printClock, KTIMER_PERIODIC, KTIMER_RELATIVE, conn, 0);
printf("start periodic timer with ID %zd\n", timerID);
}
}
return NULL;
}
int monitorSystemTimeChange()
{
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
struct itimerspec its = {.it_value.tv_sec = TIME_T_MAX};
int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
if (fd < 0)
{
return -1;
}
if (timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
{
close(fd);
return -1;
}
u_int64_t dep;
ssize_t ret = read(fd, &dep, sizeof(u_int64_t));
if (ret == -1 && errno == ECANCELED)
return 1;
return 0;
}
void *actionTimeChanged(void *ptr)
{
DBusConnection *conn = ptr;
while (!g_Quit)
{
if (monitorSystemTimeChange() == 1)
{
printf("System Time Changed.\n");
g_TimeChanged = 1;
g_Flag = 0;
printClock(conn);
}
}
return NULL;
}
const char *version = "0.1";
GMainLoop *mainloop;
/*
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
* client can inspect the properties/attributes of 'TestInterface'.
*/
/*
* This is the XML string describing the interfaces, methods and
* signals implemented by our 'Server' object. It's used by the
* 'Introspect' method of 'org.freedesktop.DBus.Introspectable'
* interface.
*
* Currently our tiny server implements only 3 interfaces:
*
* - org.freedesktop.DBus.Introspectable
* - org.freedesktop.DBus.Properties
* - org.example.TestInterface
*
* 'org.example.TestInterface' offers 3 methods:
*
* - Ping(): makes the server answering the string 'Pong'.
* It takes no arguments.
*
* - Echo(): replies the passed string argument.
*
* - EmitSignal(): send a signal 'OnEmitSignal'
*
* - Quit(): makes the server exit. It takes no arguments.
*/
const char *server_introspection_xml =
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
"<node>\n"
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
" <method name='Introspect'>\n"
" <arg name='data' type='s' direction='out' />\n"
" </method>\n"
" </interface>\n"
" <interface name='org.freedesktop.DBus.Properties'>\n"
" <method name='Get'>\n"
" <arg name='interface' type='s' direction='in' />\n"
" <arg name='property' type='s' direction='in' />\n"
" <arg name='value' type='s' direction='out' />\n"
" </method>\n"
" <method name='GetAll'>\n"
" <arg name='interface' type='s' direction='in'/>\n"
" <arg name='properties' type='a{sv}' direction='out'/>\n"
" </method>\n"
" </interface>\n"
" <interface name='com.kylin.kysdk.TimeInterface'>\n"
" <signal name='TimeSignal'>\n"
" <arg name='tim' type='s' />\n"
" </signal>"
" <signal name='TimeChangeSignal'>\n"
" <arg name='tim' type='s' />\n"
" </signal>"
" </interface>\n"
"</node>\n";
/*
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
* client can inspect the properties/attributes of 'TestInterface'.
*/
DBusHandlerResult server_get_properties_handler(const char *property, DBusConnection *conn, DBusMessage *reply)
{
if (!strcmp(property, "Version"))
{
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &version,
DBUS_TYPE_INVALID);
}
else
/* Unknown property */
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (!dbus_connection_send(conn, reply, NULL))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
}
/*
* This implements 'GetAll' method of DBUS_INTERFACE_PROPERTIES. This
* one seems required by g_dbus_proxy_get_cached_property().
*/
DBusHandlerResult server_get_all_properties_handler(DBusConnection *conn, DBusMessage *reply)
{
DBusHandlerResult result;
DBusMessageIter array, dict, iter, variant;
const char *property = "Version";
/*
* All dbus functions used below might fail due to out of
* memory error. If one of them fails, we assume that all
* following functions will fail too, including
* dbus_connection_send().
*/
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
/* Append all properties name/value pairs */
property = "Version";
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property);
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &version);
dbus_message_iter_close_container(&dict, &variant);
dbus_message_iter_close_container(&array, &dict);
dbus_message_iter_close_container(&iter, &array);
if (dbus_connection_send(conn, reply, NULL))
result = DBUS_HANDLER_RESULT_HANDLED;
return result;
}
/*
* This function implements the 'TestInterface' interface for the
* 'Server' DBus object.
*
* It also implements 'Introspect' method of
* 'org.freedesktop.DBus.Introspectable' interface which returns the
* XML string describing the interfaces, methods, and signals
* implemented by 'Server' object. This also can be used by tools such
* as d-feet(1) and can be queried by:
*
* $ gdbus introspect --session --dest org.example.TestServer --object-path /org/example/TestObject
*/
DBusHandlerResult server_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
{
// DBusMessage *msg;
// DBusMessageIter args;
DBusError err;
// dbus_uint32_t serial = 0;
// char *time_now = "test";
DBusHandlerResult result;
DBusMessage *reply = NULL;
bool quit = false;
fprintf(stderr, "Got D-Bus request: %s.%s on %s\n",
dbus_message_get_interface(message),
dbus_message_get_member(message),
dbus_message_get_path(message));
/*
* Does not allocate any memory; the error only needs to be
* freed if it is set at some point.
*/
dbus_error_init(&err);
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &server_introspection_xml,
DBUS_TYPE_INVALID);
}
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get"))
{
const char *interface, *property;
if (!dbus_message_get_args(message, &err,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID))
goto fail;
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
result = server_get_properties_handler(property, conn, reply);
dbus_message_unref(reply);
return result;
}
else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll"))
{
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
result = server_get_all_properties_handler(conn, reply);
dbus_message_unref(reply);
return result;
}
else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
fail:
if (dbus_error_is_set(&err))
{
if (reply)
dbus_message_unref(reply);
reply = dbus_message_new_error(message, err.name, err.message);
dbus_error_free(&err);
}
/*
* In any cases we should have allocated a reply otherwise it
* means that we failed to allocate one.
*/
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
/* Send the reply which might be an error one too. */
result = DBUS_HANDLER_RESULT_HANDLED;
if (!dbus_connection_send(conn, reply, NULL))
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_unref(reply);
if (quit)
{
fprintf(stderr, "Server exiting...\n");
g_main_loop_quit(mainloop);
}
return result;
}
const DBusObjectPathVTable server_vtable = {
.message_function = server_message_handler};
int main(void)
{
DBusConnection *conn;
DBusError err;
int rv;
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (!conn)
{
fprintf(stderr, "Failed to get a system DBus connection: %s\n", err.message);
goto fail;
}
rv = dbus_bus_request_name(conn, "com.kylin.kysdk.TimeServer", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
fprintf(stderr, "Failed to request name on bus: %s\n", err.message);
goto fail;
}
if (!dbus_connection_register_object_path(conn, "/com/kylin/kysdk/Timer", &server_vtable, NULL))
{
fprintf(stderr, "Failed to register a object path for 'TestObject'\n");
goto fail;
}
dbus_error_init(&err);
sem_init(&g_Wait, 0, 1);
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, actionTimeChanged, conn);
pthread_mutex_init(&lock, NULL);
/* connect to the daemon bus */
/*
* For the sake of simplicity we're using glib event loop to
* handle DBus messages. This is the only place where glib is
* used.
*/
pthread_attr_t tt;
pthread_t dd;
pthread_attr_init(&tt);
pthread_attr_setdetachstate(&tt, PTHREAD_CREATE_DETACHED);
pthread_create(&dd, &tt, startBroadcastSystemTimePerMin, conn);
printf("Starting dbus tiny server v%s\n", version);
mainloop = g_main_loop_new(NULL, false);
/* Set up the DBus connection to work in a GLib event loop */
dbus_connection_setup_with_g_main(conn, NULL);
/* Start the glib event loop */
g_main_loop_run(mainloop);
return EXIT_SUCCESS;
fail:
dbus_error_free(&err);
return EXIT_FAILURE;
}

View File

@ -0,0 +1,13 @@
#ifndef DBUS_M_SYSTIME_SERVER_H__
#define DBUS_M_SYSTIME_SERVER_H__
// extern void *startBroadcastSystemTimePerMin();
/**
* @brief 1
*
* @return int
*/
extern int monitorSystemTimeChange();
#endif

View File

@ -0,0 +1,22 @@
CLIBS=-lpthread -lrt
CFLAGS=-g -O0 -Wall -fPIC -shared
CC=gcc
TARGET=libkytimer.so
LIBINSTALLNAME=libkytimer.so.1.0.0
LIBINSTALLDIR=/usr/lib/
HEADERINSTALLDIR=/usr/include/
HEADERS=kytimer.h
.PHONY:all
.PHONY:clean
.PHONY:lib
all:lib
lib:
$(CC) $(CFLAGS) $(CLIBS) -o $(TARGET) libkytimer.c
mkdir -p ../lib/
mv $(TARGET) ../lib/$(LIBINSTALLNAME)
clean:
-rm ../lib/$(LIBINSTALLNAME)

View File

@ -0,0 +1,340 @@
#include <stdio.h>
#include <string.h>
#include <sys/timerfd.h>
#include <time.h>
#include <pthread.h>
#include <stdint.h>
#include <poll.h>
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include "libkytimer.h"
static void* timerCoreThread(void* data); //定时器循环核心
static KTimerNode* findNodeByFD(unsigned int fd); //根据fd查找节点
static int testNodeInList(KTimerNode* node);
static void insertIntoList(KTimerNode* node); //插入节点,按照间隔时间的顺序从小到大排序
static void deleteFromList(KTimerNode* node , int locked); //从链表中删除节点
static pthread_t g_coreThreadID; //核心循环线程
static KTimerNode* g_list = NULL;
static int epollfd; //事件池句柄
static int curTimerCounts; //当前已注册定时器数量
int kyTimerInitialize(); //初始化定时器核心
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
void kyStopTimer(size_t timerfd); //停止一个定时器
void kyResetTickCount(size_t timerfd , unsigned int intervalms);
void kyTimerDestroy(); //销毁定时器核心
#ifndef TFD_TIMER_CANCEL_ON_SET
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
int kyTimerInitialize()
{
if (g_coreThreadID > 0)
return 0;
epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd <= 0)
{
printf("Epoll事件池创建失败%s\n" , strerror(errno));
return errno;
}
if (pthread_create(&g_coreThreadID , NULL , timerCoreThread , NULL))
{
printf("kyTimer 定时器核心初始化失败:%s\n" , strerror(errno));
return errno;
}
return 0;
}
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete)
{
if (g_coreThreadID <= 0 && kyTimerInitialize())
{
printf("定时器注册失败:定时器全局句柄初始化失败!\n");
return 0;
}
if (curTimerCounts >= KTIMER_MAXTIMERFD)
{
printf("定时器注册失败:定时器注册数量已超限!\n");
return 0;
}
KTimerNode* node = (KTimerNode*)calloc(1 , sizeof(KTimerNode));
if (!node)
{
printf("定时器注册失败:无法分配内存,%s\n" , strerror(errno));
return 0;
}
pthread_mutex_init(&node->lock , NULL);
node->callback = callback;
node->userdata = userdata;
node->intervalms = intervalms;
node->attr = attr;
node->type = type;
node->freeOnDelete = freeOnDelete;
if (type == KTIMER_ABSOLUTE)
node->fd = timerfd_create(CLOCK_MONOTONIC , TFD_CLOEXEC|TFD_NONBLOCK);
else
node->fd = timerfd_create(CLOCK_REALTIME , TFD_CLOEXEC|TFD_NONBLOCK);
if ((int)node->fd <= 0)
{
printf("定时器注册失败timerfd_create失败%s\n" , strerror(errno));
free(node);
return 0;
}
struct itimerspec tval;
tval.it_value.tv_sec = intervalms / 1000;
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
if (attr == KTIMER_PERIODIC) //多次触发时设置interval项
{
tval.it_interval.tv_sec = tval.it_value.tv_sec;
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
}
else
{
tval.it_interval.tv_sec = 0;
tval.it_interval.tv_nsec = 0;
}
timerfd_settime(node->fd, 0, &tval, NULL);
//注册到Epoll池中
struct epoll_event ev;
memset(&ev , 0 , sizeof(struct epoll_event));
if (attr == KTIMER_SINGLESHOT)
ev.events = EPOLLIN | EPOLLONESHOT;
else
ev.events = EPOLLIN;
ev.data.ptr = node;
if (epoll_ctl(epollfd , EPOLL_CTL_ADD , node->fd , &ev))
{
printf("定时器注册失败epoll_ctl错误%s\n" , strerror(errno));
free(node);
return 0;
}
insertIntoList(node);
return node->fd;
}
//停止一个定时器,并从列表中移除
void kyStopTimer(size_t timerfd)
{
if (timerfd <= 0)
return;
KTimerNode* node = findNodeByFD(timerfd);
if (node)
{
if (epoll_ctl(epollfd , EPOLL_CTL_DEL , timerfd , NULL))
{
printf("无法注销文件句柄%zd : %s\n" , timerfd , strerror(errno));
return;
}
deleteFromList(node , 0);
}
return;
}
//定时器核心线程,负责循环处理已到期的定时器
static void* timerCoreThread(void* data)
{
pthread_detach(pthread_self());
KTimerNode* knode = NULL;
int read_fds = 0;
while (1)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
pthread_testcancel();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE , NULL);
struct epoll_event activeEvs[KTIMER_MAXTIMERFD];
read_fds = epoll_wait(epollfd , activeEvs , KTIMER_MAXTIMERFD , -1);
if (read_fds < 0)
{
printf("epoll wait error , %s\n" , strerror(errno));
continue;
}
for (int i = 0 ; i < read_fds ; i ++)
{
knode = activeEvs[i].data.ptr;
pthread_mutex_lock(&knode->lock);
if (!testNodeInList(knode))
continue;
uint64_t dep;
read(knode->fd , &dep , sizeof(uint64_t));
if (knode && knode->attr != KTIMER_NEVER && knode->callback)
{
#if 1
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
pthread_create(&tid , &attr , knode->callback , knode->userdata);
knode->freeOnDelete = 0;
#else
knode->callback(knode->userdata);
#endif
}
if (knode->attr == KTIMER_SINGLESHOT) //单次触发的在触发后就从链表中移除
{
// FixMe: 如果设置了freeOnDelete并且采用上面的线程执行模式
// 则会发生段错误。因此只能在启用线程执行模式的时候强制禁止
// freeOnDelete。
deleteFromList(knode , 1);
}
if (testNodeInList(knode))
pthread_mutex_unlock(&knode->lock);
}
}
return NULL;
}
static KTimerNode* findNodeByFD(unsigned int fd)
{
KTimerNode* tmp = g_list;
while (tmp)
{
if (tmp->fd == fd)
return tmp;
tmp = tmp->next;
}
return NULL;
}
static int testNodeInList(KTimerNode* node)
{
KTimerNode* pos = g_list;
while (pos)
{
if (pos == node)
return 1;
pos = pos->next;
}
return 0;
}
static void insertIntoList(KTimerNode* node)
{
curTimerCounts ++;
if (!g_list)
{
g_list = node;
}
else
{
if (g_list->intervalms > node->intervalms)
{
node->next = g_list;
g_list = node;
return;
}
KTimerNode* cur = g_list->next;
KTimerNode* prev = g_list;
while (cur)
{
if (cur->intervalms > node->intervalms)
{
node->next = cur;
prev->next = node;
return;
}
prev = cur;
cur = cur->next;
}
prev->next = node;
node->next = NULL;
}
return;
}
static void deleteFromList(KTimerNode* node , int locked)
{
if (!g_list)
return;
curTimerCounts --;
if (!locked)
pthread_mutex_lock(&node->lock);
if (node == g_list)
{
g_list = g_list->next;
if (node->userdata && node->freeOnDelete)
free(node->userdata);
free(node);
return;
}
KTimerNode* pos = g_list;
while (pos)
{
if (pos->next && pos->next == node)
{
pos->next = pos->next->next;
if (node->userdata && node->freeOnDelete)
free(node->userdata);
free(node);
return;
}
pos = pos->next;
}
return;
}
void kyResetTickCount(size_t timerfd , unsigned int intervalms)
{
KTimerNode* node = findNodeByFD(timerfd);
if (!node)
return;
node->intervalms = intervalms;
struct itimerspec tval;
tval.it_value.tv_sec = intervalms / 1000;
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
if (node->attr == KTIMER_PERIODIC) //多次触发时设置interval项
{
tval.it_interval.tv_sec = tval.it_value.tv_sec;
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
}
else
{
tval.it_interval.tv_sec = 0;
tval.it_interval.tv_nsec = 0;
}
timerfd_settime(timerfd , 0 , &tval , NULL);
return;
}
void kyTimerDestroy() //停止所有定时器,并销毁定时器核心
{
//终止线程运行
if (g_coreThreadID > 0)
pthread_cancel(g_coreThreadID);
//销毁链表
KTimerNode* node = g_list;
while (node)
{
deleteFromList(node , 0);
node = g_list;
}
//关闭epoll池
close(epollfd);
return;
}

View File

@ -0,0 +1,48 @@
#ifndef __KY_TIMER_H__
#define __KY_TIMER_H__
#include <stdlib.h>
#include <pthread.h>
typedef enum
{
KTIMER_SINGLESHOT = 0,
KTIMER_PERIODIC = 1,
KTIMER_NEVER = 2
}KTimerAttribute;
typedef enum
{
KTIMER_ABSOLUTE = 0,
KTIMER_RELATIVE
}KTimerType;
#define KTIMER_MAXTIMERFD 1000 //最多支持注册1000个定时器
typedef void* (*time_handler)(void* user_data); //回调函数规则
typedef struct _KTimerNode{
pthread_mutex_t lock;
size_t fd; //序列号
time_handler callback; //到期后执行的函数
int freeOnDelete; //删除定时器时自动释放userdata
void* userdata; //callback使用的参数外部使用alloc分配内存在delete定时器的时候会根据配置自动释放
unsigned int intervalms; //定时器间隔时间,单位毫秒
KTimerAttribute attr; //触发类型,单次触发或多次触发
KTimerType type; //时钟类型,绝对时间还是相对时间
struct _KTimerNode* next;
}KTimerNode;
#ifdef __cplusplus
extern "C"
{
#endif
extern int kyTimerInitialize(); //初始化定时器核心
extern size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
extern void kyStopTimer(size_t timerfd); //停止一个定时器
extern void kyResetTickCount(size_t timerfd , unsigned int intervalms); //重置一个定时器的时间
extern void kyTimerDestroy(); //销毁定时器核心
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,5 @@
all:
gcc -g -O0 -o demo main.c -lkytimer -lpthread
clean:
rm demo

View File

@ -0,0 +1,62 @@
#include <libkytimer.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
void func(char* comment)
{
char date[32] = {0};
time_t now;
time(&now);
if (ctime_r(&now , date))
date[strlen(date) - 1] = '\0';
printf("[%s]这是%s\n" , date , comment);
}
void stop(size_t* fdpoll)
{
printf("开始停止定时器\n");
for (int i = 0 ; i < 10 ; i ++)
{
kyStopTimer(fdpoll[i]);
}
}
int main()
{
assert(kyTimerInitialize() == 0);
//测试1 -- 基本功能
size_t fdpoll[10] = {0};
for (int i = 1 ; i <= 10 ; i ++)
{
char* data = (char*)malloc(10);
assert(data);
sprintf(data , "%d号" , i);
fdpoll[i - 1] = kyStartTimer(i * 1000 , (time_handler)func , KTIMER_PERIODIC , (void*)data , 1);
assert(fdpoll[i - 1]);
}
kyStartTimer(10000 , (time_handler)stop , KTIMER_SINGLESHOT , (void*)fdpoll , 0);
sleep(11);
//测试3 -- 单次触发
printf("单次触发测试:\n");
kyStartTimer(2000 , (time_handler)func , KTIMER_SINGLESHOT , "2号" , 0);
sleep(5);
//测试2 -- 重置定时器时间
printf("修改时间测试:\n");
int persec = kyStartTimer(1000 , (time_handler)func , KTIMER_PERIODIC , "1号" , 0);
int sec3 = kyStartTimer(3000 , (time_handler)func , KTIMER_SINGLESHOT , "3号" , 0);
sleep(2);
kyResetTickCount(sec3 , 4000);
printf("sec3 时钟已被重置为4000ms\n");
sleep(10);
printf("正在销毁定时器核心...\n");
kyTimerDestroy();
return 0;
}

View File

@ -0,0 +1,14 @@
FILE=$(file)
CFLAGS=$(cflags)
CFLAGS += -g -O0 -std=gnu11 -Wall
CLIBS=-lpthread -pthread -lrt
CLIBS += $(clibs)
.PHONY:main test
all:systemtime.c
gcc -o demo systemtime.c ../timer/libkytimer.c $(CFLAGS) $(CLIBS)
.PHONY:clean
clean:
-rm main
-rm *.o

BIN
systemtime/systemtime/demo Executable file

Binary file not shown.

View File

@ -0,0 +1,141 @@
#include "systemtime.h"
#include "../timer/libkytimer.h"
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/timerfd.h>
#include <errno.h>
pthread_mutex_t lock; //互斥锁
u_int8_t g_Flag; //任何平台都是8位无符号数
u_int8_t g_Quit;
sem_t g_Wait;
u_int8_t g_TimeChanged;
void sig_Handler(int sig)
{
g_Quit = 1;
printf("%s----%d",__func__,sig);
}
static void* printClock(void* ptr)
{
struct tm *now;
time_t current;
time(&current);
now = localtime(&current);
printf("%s,%d",__func__,__LINE__);
printf("%d/%d/%d %d:%d:%d\n", now->tm_year, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
if (!g_Flag)
{
pthread_mutex_lock(&lock);
if(now->tm_sec == 0)
{
g_Flag = 1;
}
else
{
g_Flag = 0;
}
pthread_mutex_unlock(&lock);
sem_post(&g_Wait);
}
return NULL;
}
void startBroadcastSystemTimePerMin()
{
size_t timerID = -1;
while (!g_Quit)
{
sem_wait(&g_Wait);
if (g_TimeChanged)
{
printf("Get Time Changed signal. stop timerID %zd\n", timerID);
kyStopTimer(timerID);
g_TimeChanged = 0;
timerID = -1;
}
if (! g_Flag)
kyStartTimer(500, printClock, KTIMER_SINGLESHOT, KTIMER_RELATIVE, NULL, 0);
else
{
timerID = kyStartTimer(1000 * 60, printClock, KTIMER_PERIODIC, KTIMER_RELATIVE, NULL, 0);
printf("start periodic timer with ID %zd\n", timerID);
}
}
}
int monitorSystemTimeChange()
{
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
struct itimerspec its = {.it_value.tv_sec = TIME_T_MAX};
int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
if (fd < 0)
{
return -1;
}
if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
{
close(fd);
return -1;
}
u_int64_t dep;
ssize_t ret = read(fd, &dep, sizeof(u_int64_t));
if (ret == -1 && errno == ECANCELED)
return 1;
return 0;
}
void* actionTimeChanged(void* ptr)
{
while (!g_Quit)
{
if (monitorSystemTimeChange() == 1)
{
printf("System Time Changed.\n");
g_TimeChanged = 1;
g_Flag = 0;
printClock(NULL);
}
}
return NULL;
}
int main()
{
signal(SIGTERM, sig_Handler);
signal(SIGQUIT, sig_Handler);
signal(SIGKILL, sig_Handler);
signal(SIGINT, sig_Handler);
sem_init(&g_Wait, 0, 1);
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, actionTimeChanged, NULL);
pthread_mutex_init(&lock, NULL);
startBroadcastSystemTimePerMin();
pthread_mutex_destroy(&lock);
sem_destroy(&g_Wait);
return 0;
}

View File

@ -0,0 +1,13 @@
#ifndef KYSDK_SYSTIME_H__
#define KYSDK_SYSTIME_H__
extern void startBroadcastSystemTimePerMin();
/**
* @brief 1
*
* @return int
*/
extern int monitorSystemTimeChange();
#endif

22
systemtime/timer/Makefile Normal file
View File

@ -0,0 +1,22 @@
CLIBS=-lpthread -lrt
CFLAGS=-g -O0 -Wall -fPIC -shared
CC=gcc
TARGET=libkytimer.so
LIBINSTALLNAME=libkytimer.so.1.0.0
LIBINSTALLDIR=/usr/lib/
HEADERINSTALLDIR=/usr/include/
HEADERS=kytimer.h
.PHONY:all
.PHONY:clean
.PHONY:lib
all:lib
lib:
$(CC) $(CFLAGS) $(CLIBS) -o $(TARGET) libkytimer.c
mkdir -p ../lib/
mv $(TARGET) ../lib/$(LIBINSTALLNAME)
clean:
-rm ../lib/$(LIBINSTALLNAME)

View File

@ -0,0 +1,340 @@
#include <stdio.h>
#include <string.h>
#include <sys/timerfd.h>
#include <time.h>
#include <pthread.h>
#include <stdint.h>
#include <poll.h>
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include "libkytimer.h"
static void* timerCoreThread(void* data); //定时器循环核心
static KTimerNode* findNodeByFD(unsigned int fd); //根据fd查找节点
static int testNodeInList(KTimerNode* node);
static void insertIntoList(KTimerNode* node); //插入节点,按照间隔时间的顺序从小到大排序
static void deleteFromList(KTimerNode* node , int locked); //从链表中删除节点
static pthread_t g_coreThreadID; //核心循环线程
static KTimerNode* g_list = NULL;
static int epollfd; //事件池句柄
static int curTimerCounts; //当前已注册定时器数量
int kyTimerInitialize(); //初始化定时器核心
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
void kyStopTimer(size_t timerfd); //停止一个定时器
void kyResetTickCount(size_t timerfd , unsigned int intervalms);
void kyTimerDestroy(); //销毁定时器核心
#ifndef TFD_TIMER_CANCEL_ON_SET
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
int kyTimerInitialize()
{
if (g_coreThreadID > 0)
return 0;
epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd <= 0)
{
printf("Epoll事件池创建失败%s\n" , strerror(errno));
return errno;
}
if (pthread_create(&g_coreThreadID , NULL , timerCoreThread , NULL))
{
printf("kyTimer 定时器核心初始化失败:%s\n" , strerror(errno));
return errno;
}
return 0;
}
size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete)
{
if (g_coreThreadID <= 0 && kyTimerInitialize())
{
printf("定时器注册失败:定时器全局句柄初始化失败!\n");
return 0;
}
if (curTimerCounts >= KTIMER_MAXTIMERFD)
{
printf("定时器注册失败:定时器注册数量已超限!\n");
return 0;
}
KTimerNode* node = (KTimerNode*)calloc(1 , sizeof(KTimerNode));
if (!node)
{
printf("定时器注册失败:无法分配内存,%s\n" , strerror(errno));
return 0;
}
pthread_mutex_init(&node->lock , NULL);
node->callback = callback;
node->userdata = userdata;
node->intervalms = intervalms;
node->attr = attr;
node->type = type;
node->freeOnDelete = freeOnDelete;
if (type == KTIMER_ABSOLUTE)
node->fd = timerfd_create(CLOCK_MONOTONIC , TFD_CLOEXEC|TFD_NONBLOCK);
else
node->fd = timerfd_create(CLOCK_REALTIME , TFD_CLOEXEC|TFD_NONBLOCK);
if ((int)node->fd <= 0)
{
printf("定时器注册失败timerfd_create失败%s\n" , strerror(errno));
free(node);
return 0;
}
struct itimerspec tval;
tval.it_value.tv_sec = intervalms / 1000;
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
if (attr == KTIMER_PERIODIC) //多次触发时设置interval项
{
tval.it_interval.tv_sec = tval.it_value.tv_sec;
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
}
else
{
tval.it_interval.tv_sec = 0;
tval.it_interval.tv_nsec = 0;
}
timerfd_settime(node->fd, 0, &tval, NULL);
//注册到Epoll池中
struct epoll_event ev;
memset(&ev , 0 , sizeof(struct epoll_event));
if (attr == KTIMER_SINGLESHOT)
ev.events = EPOLLIN | EPOLLONESHOT;
else
ev.events = EPOLLIN;
ev.data.ptr = node;
if (epoll_ctl(epollfd , EPOLL_CTL_ADD , node->fd , &ev))
{
printf("定时器注册失败epoll_ctl错误%s\n" , strerror(errno));
free(node);
return 0;
}
insertIntoList(node);
return node->fd;
}
//停止一个定时器,并从列表中移除
void kyStopTimer(size_t timerfd)
{
if (timerfd <= 0)
return;
KTimerNode* node = findNodeByFD(timerfd);
if (node)
{
if (epoll_ctl(epollfd , EPOLL_CTL_DEL , timerfd , NULL))
{
printf("无法注销文件句柄%zd : %s\n" , timerfd , strerror(errno));
return;
}
deleteFromList(node , 0);
}
return;
}
//定时器核心线程,负责循环处理已到期的定时器
static void* timerCoreThread(void* data)
{
pthread_detach(pthread_self());
KTimerNode* knode = NULL;
int read_fds = 0;
while (1)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
pthread_testcancel();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE , NULL);
struct epoll_event activeEvs[KTIMER_MAXTIMERFD];
read_fds = epoll_wait(epollfd , activeEvs , KTIMER_MAXTIMERFD , -1);
if (read_fds < 0)
{
printf("epoll wait error , %s\n" , strerror(errno));
continue;
}
for (int i = 0 ; i < read_fds ; i ++)
{
knode = activeEvs[i].data.ptr;
pthread_mutex_lock(&knode->lock);
if (!testNodeInList(knode))
continue;
uint64_t dep;
read(knode->fd , &dep , sizeof(uint64_t));
if (knode && knode->attr != KTIMER_NEVER && knode->callback)
{
#if 1
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
pthread_create(&tid , &attr , knode->callback , knode->userdata);
knode->freeOnDelete = 0;
#else
knode->callback(knode->userdata);
#endif
}
if (knode->attr == KTIMER_SINGLESHOT) //单次触发的在触发后就从链表中移除
{
// FixMe: 如果设置了freeOnDelete并且采用上面的线程执行模式
// 则会发生段错误。因此只能在启用线程执行模式的时候强制禁止
// freeOnDelete。
deleteFromList(knode , 1);
}
if (testNodeInList(knode))
pthread_mutex_unlock(&knode->lock);
}
}
return NULL;
}
static KTimerNode* findNodeByFD(unsigned int fd)
{
KTimerNode* tmp = g_list;
while (tmp)
{
if (tmp->fd == fd)
return tmp;
tmp = tmp->next;
}
return NULL;
}
static int testNodeInList(KTimerNode* node)
{
KTimerNode* pos = g_list;
while (pos)
{
if (pos == node)
return 1;
pos = pos->next;
}
return 0;
}
static void insertIntoList(KTimerNode* node)
{
curTimerCounts ++;
if (!g_list)
{
g_list = node;
}
else
{
if (g_list->intervalms > node->intervalms)
{
node->next = g_list;
g_list = node;
return;
}
KTimerNode* cur = g_list->next;
KTimerNode* prev = g_list;
while (cur)
{
if (cur->intervalms > node->intervalms)
{
node->next = cur;
prev->next = node;
return;
}
prev = cur;
cur = cur->next;
}
prev->next = node;
node->next = NULL;
}
return;
}
static void deleteFromList(KTimerNode* node , int locked)
{
if (!g_list)
return;
curTimerCounts --;
if (!locked)
pthread_mutex_lock(&node->lock);
if (node == g_list)
{
g_list = g_list->next;
if (node->userdata && node->freeOnDelete)
free(node->userdata);
free(node);
return;
}
KTimerNode* pos = g_list;
while (pos)
{
if (pos->next && pos->next == node)
{
pos->next = pos->next->next;
if (node->userdata && node->freeOnDelete)
free(node->userdata);
free(node);
return;
}
pos = pos->next;
}
return;
}
void kyResetTickCount(size_t timerfd , unsigned int intervalms)
{
KTimerNode* node = findNodeByFD(timerfd);
if (!node)
return;
node->intervalms = intervalms;
struct itimerspec tval;
tval.it_value.tv_sec = intervalms / 1000;
tval.it_value.tv_nsec = (intervalms % 1000) * 1000000;
if (node->attr == KTIMER_PERIODIC) //多次触发时设置interval项
{
tval.it_interval.tv_sec = tval.it_value.tv_sec;
tval.it_interval.tv_nsec = tval.it_value.tv_nsec;
}
else
{
tval.it_interval.tv_sec = 0;
tval.it_interval.tv_nsec = 0;
}
timerfd_settime(timerfd , 0 , &tval , NULL);
return;
}
void kyTimerDestroy() //停止所有定时器,并销毁定时器核心
{
//终止线程运行
if (g_coreThreadID > 0)
pthread_cancel(g_coreThreadID);
//销毁链表
KTimerNode* node = g_list;
while (node)
{
deleteFromList(node , 0);
node = g_list;
}
//关闭epoll池
close(epollfd);
return;
}

View File

@ -0,0 +1,48 @@
#ifndef __KY_TIMER_H__
#define __KY_TIMER_H__
#include <stdlib.h>
#include <pthread.h>
typedef enum
{
KTIMER_SINGLESHOT = 0,
KTIMER_PERIODIC = 1,
KTIMER_NEVER = 2
}KTimerAttribute;
typedef enum
{
KTIMER_ABSOLUTE = 0,
KTIMER_RELATIVE
}KTimerType;
#define KTIMER_MAXTIMERFD 1000 //最多支持注册1000个定时器
typedef void* (*time_handler)(void* user_data); //回调函数规则
typedef struct _KTimerNode{
pthread_mutex_t lock;
size_t fd; //序列号
time_handler callback; //到期后执行的函数
int freeOnDelete; //删除定时器时自动释放userdata
void* userdata; //callback使用的参数外部使用alloc分配内存在delete定时器的时候会根据配置自动释放
unsigned int intervalms; //定时器间隔时间,单位毫秒
KTimerAttribute attr; //触发类型,单次触发或多次触发
KTimerType type; //时钟类型,绝对时间还是相对时间
struct _KTimerNode* next;
}KTimerNode;
#ifdef __cplusplus
extern "C"
{
#endif
extern int kyTimerInitialize(); //初始化定时器核心
extern size_t kyStartTimer(unsigned int intervalms, time_handler callback, KTimerAttribute attr, KTimerType type, void* userdata, int freeOnDelete); //注册并开始一个定时器,返回定时器句柄号
extern void kyStopTimer(size_t timerfd); //停止一个定时器
extern void kyResetTickCount(size_t timerfd , unsigned int intervalms); //重置一个定时器的时间
extern void kyTimerDestroy(); //销毁定时器核心
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,5 @@
all:
gcc -g -O0 -o demo main.c -lkytimer -lpthread
clean:
rm demo

View File

@ -0,0 +1,62 @@
#include <libkytimer.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
void func(char* comment)
{
char date[32] = {0};
time_t now;
time(&now);
if (ctime_r(&now , date))
date[strlen(date) - 1] = '\0';
printf("[%s]这是%s\n" , date , comment);
}
void stop(size_t* fdpoll)
{
printf("开始停止定时器\n");
for (int i = 0 ; i < 10 ; i ++)
{
kyStopTimer(fdpoll[i]);
}
}
int main()
{
assert(kyTimerInitialize() == 0);
//测试1 -- 基本功能
size_t fdpoll[10] = {0};
for (int i = 1 ; i <= 10 ; i ++)
{
char* data = (char*)malloc(10);
assert(data);
sprintf(data , "%d号" , i);
fdpoll[i - 1] = kyStartTimer(i * 1000 , (time_handler)func , KTIMER_PERIODIC , (void*)data , 1);
assert(fdpoll[i - 1]);
}
kyStartTimer(10000 , (time_handler)stop , KTIMER_SINGLESHOT , (void*)fdpoll , 0);
sleep(11);
//测试3 -- 单次触发
printf("单次触发测试:\n");
kyStartTimer(2000 , (time_handler)func , KTIMER_SINGLESHOT , "2号" , 0);
sleep(5);
//测试2 -- 重置定时器时间
printf("修改时间测试:\n");
int persec = kyStartTimer(1000 , (time_handler)func , KTIMER_PERIODIC , "1号" , 0);
int sec3 = kyStartTimer(3000 , (time_handler)func , KTIMER_SINGLESHOT , "3号" , 0);
sleep(2);
kyResetTickCount(sec3 , 4000);
printf("sec3 时钟已被重置为4000ms\n");
sleep(10);
printf("正在销毁定时器核心...\n");
kyTimerDestroy();
return 0;
}