<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>时光小栈 on 时光小栈</title><link>/</link><language>zh-CN</language><author>rinetd</author><rights>Copyright (c) 2015, rinetd; all rights reserved.</rights><updated>Tue, 24 Nov 2020 15:03:00 CST</updated><item><title>linux kernel timer</title><link>/hardware/kernel/linux-kernel-timer/</link><pubDate>Tue, 24 Nov 2020 15:03:00 CST</pubDate><author>rinetd</author><guid>/hardware/kernel/linux-kernel-timer/</guid><description>&lt;p&gt;linux驱动之定时器的使用&lt;br /&gt;
被文章摘自一下几位网友。非常感谢他们。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://blog.sina.com.cn/s/blog_57330c3401011cq3.html&#34; target=&#34;_blank&#34;&gt;http://blog.sina.com.cn/s/blog_57330c3401011cq3.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Linux的内核中定义了一个定时器的结构：&lt;/p&gt;
&lt;p&gt;#include&lt;linux/timer.h&gt;&lt;/p&gt;
&lt;p&gt;struct timer_list&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; struct list_head list;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;　　unsigned long expires; //定时器到期时间&lt;/p&gt;
&lt;p&gt;　　unsigned long data; //作为参数被传入定时器处理函数&lt;/p&gt;
&lt;p&gt;　　void (*function)(unsigned long);&lt;/p&gt;
&lt;p&gt;};&lt;/p&gt;
&lt;p&gt;利用这个结构我们可以在驱动中很方便的使用定时器。&lt;/p&gt;
&lt;p&gt;一： timer的API函数：&lt;/p&gt;
&lt;p&gt;初始化定时器：&lt;/p&gt;
&lt;p&gt;void init_timer(struct timer_list * timer);&lt;/p&gt;
&lt;p&gt;增加定时器：&lt;/p&gt;
&lt;p&gt;void add_timer(struct timer_list * timer);&lt;/p&gt;
&lt;p&gt;删除定时器：&lt;/p&gt;
&lt;p&gt;int del_timer(struct timer_list * timer);&lt;/p&gt;
&lt;p&gt;修改定时器的expire：&lt;/p&gt;
&lt;p&gt;int mod_timer(struct timer_list *timer, unsigned long expires);&lt;/p&gt;
&lt;p&gt;二：使用定时器的一般流程为：&lt;/p&gt;
&lt;p&gt;（1）创建timer、编写超时定时器处理函数function；&lt;/p&gt;
&lt;p&gt;（2）为timer的expires、data、function赋值；&lt;/p&gt;
&lt;p&gt;（3）调用add_timer将timer加入列表----添加一个定时器；&lt;/p&gt;
&lt;p&gt;（4）在定时器到期时，function被执行；&lt;/p&gt;
&lt;p&gt;（5）在程序中涉及timer控制的地方适当地调用del_timer、mod_timer删除timer或修改timer的expires。&lt;/p&gt;
&lt;p&gt;三：下面看一个例子：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;linux/module.h&amp;gt;
#include &amp;lt;linux/kernel.h&amp;gt;
#include &amp;lt;linux/init.h&amp;gt;
#include &amp;lt;linux/sched.h&amp;gt;//jiffies在此头文件中定义
#include &amp;lt;linux/init.h&amp;gt;
#include &amp;lt;linux/timer.h&amp;gt;
struct timer_list mytimer;//定义一个定时器
void mytimer_ok(unsigned long arg)
{
printk(&amp;quot;Mytimer is ok\n&amp;quot;);
printk(&amp;quot;receive data from timer: %d\n&amp;quot;,arg);
}
static int __init hello_init (void)
{
printk(&amp;quot;hello,world\n&amp;quot;);
init_timer(&amp;amp;mytimer); //初始化定时器
mytimer.expires = jiffies+100;//设定超时时间，100代表1秒
mytimer.data = 250; //传递给定时器超时函数的值
mytimer.function = mytimer_ok;//设置定时器超时函数
add_timer(&amp;amp;mytimer); //添加定时器，定时器开始生效
return 0;
}
static void __exit hello_exit (void)
{
del_timer(&amp;amp;mytimer);//卸载模块时，删除定时器
printk(&amp;quot;Hello module exit\n&amp;quot;);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR(&amp;quot;CXF&amp;quot;);
MODULE_LICENSE(&amp;quot;Dual BSD/GPL&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;四：交叉编译后，放到开发板上：&lt;/p&gt;
&lt;p&gt;#insmod timer.ko&lt;/p&gt;
&lt;p&gt;可以发现过一秒后定时器过期函数被执行了，打印出了信息，250也被正确传递了。&lt;/p&gt;
&lt;p&gt;#rmmod timer.ko&lt;/p&gt;
&lt;p&gt;我们也可以用lsmod | grep timer 来查看是否加载了timer驱动。&lt;/p&gt;
&lt;p&gt;可以用dmesg | tail -20 查看驱动打印的信息&lt;/p&gt;
&lt;p&gt;dmesg -c 清楚信息&lt;/p&gt;
&lt;p&gt;五：进一步理解定时器：&lt;/p&gt;
&lt;p&gt;在上面的定时器超时函数mytimer_ok(unsigned long arg)中，添加如下代码：&lt;/p&gt;
&lt;p&gt;mytimer.expires = jiffies+100;//设定超时时间，100代表1秒&lt;br /&gt;
mytimer.function = mytimer_ok;//设置定时器超时函数&lt;/p&gt;
&lt;p&gt;add_timer(&amp;amp;mytimer); //添加定时器，定时器开始生效&lt;/p&gt;
&lt;p&gt;交叉编译后，放到开发板上&lt;/p&gt;
&lt;p&gt;#insmod timer.o&lt;/p&gt;
&lt;p&gt;发现每隔一秒，mytimer_ok函数就执行一次，这是因为每次定时器到期后，都&lt;/p&gt;
&lt;p&gt;又重新给它设置了一个新的超时时间，并且新的超时函数指向自己，形成一个递&lt;/p&gt;
&lt;p&gt;归，所以就会一直执行下去。&lt;/p&gt;
&lt;p&gt;#rmmod timer&lt;/p&gt;
&lt;p&gt;可以卸载模块，当然打印也就结束了，注意因为定时器超时函数不停的打印信息&lt;/p&gt;
&lt;p&gt;，导致输入上面的命令时会被定时器超时函数不停的打印信息淹没，不用管他，&lt;/p&gt;
&lt;p&gt;耐心的把上面的命令输完就可以成功卸载。&lt;/p&gt;</description></item><item><title>libev_ev_async</title><link>/language/clang/libev/libev_ev_async/</link><pubDate>Sat, 31 Oct 2020 21:52:25 CST</pubDate><author>rinetd</author><guid>/language/clang/libev/libev_ev_async/</guid><description>&lt;pre&gt;&lt;code&gt;// 在其它线程发通知事件给EV LOOP主线程
ev_async async_w;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;ev.h&amp;gt;
struct ev_loop *loop = NULL;
static ev_async async_watcher;
static void async_cb(EV_P_ ev_async *w, int revents)
{
static int cb_count = 0;
printf(&amp;quot;async_cb() call, cb_count = %d %d\n&amp;quot;, cb_count++,revents);
}
void *ev_create(void *p)
{
printf(&amp;quot;ev_create() call, start!\n&amp;quot;);
loop = ev_loop_new(EVFLAG_AUTO);
ev_async_init(&amp;amp;async_watcher, async_cb);
ev_async_start(loop, &amp;amp;async_watcher);
ev_run(loop, 0); // 如果不在回调中调用stop或者break方法，ev_run后面的代码就永远不会被执行。
printf(&amp;quot;ev_create() call, ev_run end!\n&amp;quot;);
}
int main()
{
pthread_t tid;
pthread_create(&amp;amp;tid, NULL, ev_create, NULL);
sleep(1); // 要等子线程先注册完事件之后，才可以调用下面的ev_async_send函数，否则你懂的。
while(1)
{
static int num = 0;
printf(&amp;quot;num = %d\n&amp;quot;, num);
ev_async_send(loop, &amp;amp;async_watcher); // 既然可以在这里ev_async_send，那么也可以在其他需要的地方ev_async_send
sleep(2);
num++;
}
return 0;
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>linux c ipc eventfd</title><link>/language/clang/linux-c-ipc-eventfd/</link><pubDate>Sat, 31 Oct 2020 17:01:06 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-c-ipc-eventfd/</guid><description>&lt;p&gt;1.新内核版本为什么要增加eventfd?&lt;/p&gt;
&lt;p&gt;首先说明的一点是eventfd是用来实现多进程或多线程的之间的事件通知的，那么我们在没接触eventfd之前用到的事件通知机制都有那些？&lt;/p&gt;
&lt;p&gt;1.条件变量&lt;br /&gt;
2.管道&lt;/p&gt;
&lt;p&gt;我们来逐一比较此俩中机制与eventfd的效果方面的好坏，首先，条件变量必须和互斥锁结合使用，使用起来麻烦，而且性能未必比eventfd好，其次条件变量不能像eventfd一样为I/O事件驱动，因此不能和服务器的I/O模式很好的融合，所以在某些时候不如eventfd好用&lt;/p&gt;
&lt;p&gt;接着是管道，虽然管道能与I/O复用很好的融合，但很明显管道相比eventfd多用了一个文件描述符，而且管道的话内核还得给其管理的缓冲区，eventfd则不需要，所以单纯作为事件通知的话还是管道好用&lt;/p&gt;
&lt;p&gt;2.event的主要接口&lt;/p&gt;
&lt;p&gt;eventfd只有一个接口，形式如下&lt;/p&gt;
&lt;p&gt;int eventfd(unsigned int initval, int flags); //成功返回事件驱动的文件描述符fd&lt;br /&gt;
说明：initval的范围是0~0xfffffffffffffffe；flags的值可以是如下枚举值：&lt;/p&gt;
&lt;p&gt;enum&lt;br /&gt;
{&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EFD_SEMAPHORE = 00000001, //since Linux 2.6.30
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#define EFD_SEMAPHORE EFD_SEMAPHORE&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EFD_CLOEXEC = 02000000, //since Linux 2.6.27
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#define EFD_CLOEXEC EFD_CLOEXEC&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EFD_NONBLOCK = 00004000 //since Linux 2.6.27
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#define EFD_NONBLOCK EFD_NONBLOCK&lt;br /&gt;
};&lt;/p&gt;
&lt;p&gt;如果设置了EFD_SEMAPHORE，则不会出现粘包的情况，即write多少次，就需要read多少次；&lt;/p&gt;
&lt;p&gt;如果设置了0（内核2.6.27版本之前必须设置为0），则会出现粘包的可能，write多次，可能read到的是若干次的值的和；&lt;/p&gt;
&lt;p&gt;另外两种一般不需要，所以并未进行测试。&lt;/p&gt;
&lt;p&gt;eventfd()创建一个文件描述符，这个文件描述符用户可以通过等待其可读来实现事件通知，该通知靠内核来响应用户空间的应用事件。上述接口的第一个参数是一个由内核来保持的64位计数器，这个计数器有参数initval来初始化，关于此计数器的影响我在下文中的具体实例中给大家演示，一般我们可将其设为0&lt;/p&gt;
&lt;p&gt;第二个参数flags可以为EFD_NONBLOCK或EFD_CLOEXEC，其含义分别为阻塞文件描述符，和与普通文件的CLOEXEC标志一样的功能&lt;br /&gt;
一般来说进程往往会调用fork函数来执行一个子进程，调用execve()执行其他程序，这时候可能就导致子进程中存在一些无用的文件描述符问题。&lt;br /&gt;
父进程在fork函数的时候，子进程会拷贝跟父进程一样的地址空间，包括寄存器，文件描述符，堆，栈等。在一开始，父进程与子进程的文件描述符指向的是系统文件表中的同一项(包括文件状态，和文件偏移量)。&lt;br /&gt;
当我们用execve执行其他程序的时候，全新的程序会替换子进程中的地址空间，数据段，堆栈，此时保存与父进程文件描述符也就不存在了，也无法进行关闭，这时候就需要FD_CLOEXEC, 表示子进程在执行exec的时候，该文件描述符就需要进行关闭。&lt;/p&gt;
&lt;p&gt;通过fcntl设置FD_CLOEXEC标志有什么用？&lt;br /&gt;
close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC，使用execl执行的程序里，此描述符被关闭，不能再使用它，但是在使用fork调用的子进程中，此描述符并不关闭，仍可使用。&lt;/p&gt;
&lt;p&gt;实现代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;int fd=open(&amp;quot;1&amp;quot;,O_RDONLY);
//获取当前文件描述符的相关标志位
int flags=fcntl(fd,F_GETFD,NULL);
flags|=FD_CLOEXEC;
fcntl(fd,F_SETFD,flags);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3.具体实例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;sys/eventfd.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
int fd;
uint64_t buffer;
void threadFunc(void) //线程函数
{
int t;
while(1)
{
t = read(fd,&amp;amp;buffer,sizeof(buffer)); //阻塞等待fd可读，及通知事件发生
if(sizeof(buffer) &amp;lt; 8)
{
printf(&amp;quot;buffer错误\n&amp;quot;);
}
printf(&amp;quot;t = %llu buffer = %llu\n&amp;quot;,t,buffer);
if(t == 8)
{
printf(&amp;quot;唤醒成功\n&amp;quot;);
}
}
}
int main(void)
{
uint64_t buf = 1;
int ret;
pthread_t tid;
int init_val =3;
if((fd = eventfd(init_val,0)) == -1) //创建事件驱动的文件描述符
{
printf(&amp;quot;创建失败\n&amp;quot;);
}
//创建线程
if(pthread_create(&amp;amp;tid,NULL,threadFunc,NULL) &amp;lt; 0)
{
printf(&amp;quot;线程创建失败\n&amp;quot;);
}
while(1)
{
ret = write(fd,&amp;amp;buf,sizeof(buf)); //通过往fd里写东西来进行事件通知
if(ret != 8)
{
printf(&amp;quot;写错误\n&amp;quot;);
}
sleep(2); //没2s通知一次
}
return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码中，我们创建一个线程，通过主线程往fd里写数据，来通知另一个线程。&lt;br /&gt;
代码执行结果如下&lt;br /&gt;
这里写图片描述&lt;/p&gt;
&lt;p&gt;关于eventfd中的initval参数的作用，我的测试结果如下,前提把上述代码eventfd函数中的0改为3&lt;/p&gt;
&lt;p&gt;这里写图片描述&lt;br /&gt;
可以看出这个由内核管理的计数器，我们的初始化值，只会影响程序第一次buffer的值，后续buffer中的值依然为1.&lt;/p&gt;</description></item><item><title>gonggonghao</title><link>/wechat/gonggonghao/</link><pubDate>Sat, 31 Oct 2020 11:51:29 CST</pubDate><author>rinetd</author><guid>/wechat/gonggonghao/</guid><description>&lt;p&gt;订阅号：&lt;br /&gt;
封面背景 4d8ddd&lt;/p&gt;
&lt;p&gt;R 77 G 144 B 221 蓝色&lt;/p&gt;</description></item><item><title>makefile hisi</title><link>/language/clang/makefile-hisi/</link><pubDate>Wed, 28 Oct 2020 09:09:17 CST</pubDate><author>rinetd</author><guid>/language/clang/makefile-hisi/</guid><description>&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;####################################
# 所有与平台相关的编译参数全放在这里
####################################
#=======================================
#plateform
export CHIP_TYPE := HI_3531A
#=======================================
#tools chain
ifeq ($(CHIP_TYPE), HI_3531A)
TOOL_CHAIN_DIR := /home/share_disk/tool_chain/3531A_030/arm-hisiv300-linux/target/bin/
export CROSS_COMPILE := $(TOOL_CHAIN_DIR)arm-hisiv300-linux-
else
endif
#=======================================
export CC := $(CROSS_COMPILE)gcc
export CPP := $(CROSS_COMPILE)g++
export AR := $(CROSS_COMPILE)ar
export LD := $(CROSS_COMPILE)ld
export STRIP := $(CROSS_COMPILE)strip
export STRIPFLAGS:= -g --strip-unneeded
export MAKEDEPEND =$(CC) -MM -MT
export CFLAGS := -Wall
export CPPFLAGS := -Wall
export MACRO :=
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;include Makefile.param
include Makefile.srcs
include Makefile.libs
TARGET := SmartVC
all:$(TARGET)
$(TARGET):$(OBJS_C) $(OBJS_CPP)
$(CC) $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(SRCS_INC) $(LIBS_INC) $(LIBS_LIB) $(OBJS_C) $(OBJS_CPP) $(LDFLAGS) -o $@ -g
#$(STRIP) $(STRIPFLAGS) $@
libmdd.so:$(OBJS_C) $(OBJS_CPP)
$(CC) $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(OBJS_C) $(OBJS_CPP) $(LDFLAGS) -o $@ -shared -fPIC
#$(STRIP) $(STRIPFLAGS) $@
libmdd.a:$(OBJS_C) $(OBJS_CPP)
$(AR) rv $@ $(OBJS_C) $(OBJS_CPP)
#$(STRIP) $(STRIPFLAGS) $@
$(OBJS_C):%.o:%.c
$(CC) -o $@ -c $&amp;lt; $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(SRCS_INC) $(LIBS_INC)
$(OBJS_CPP):%.o:%.cpp
$(CC) -o $@ -c $&amp;lt; $(SRCS_MACRO) $(CFLAGS) $(CPPFLAGS) $(SRCS_INC) $(LIBS_INC)
$(DEF_C):%.d:%.c
$(MAKEDEPEND) $(&amp;lt;:.c=.o) $&amp;lt; $(SRCS_INC) $(LIBS_INC) &amp;gt; $@
$(DEF_CPP):%.d:%.cpp
$(MAKEDEPEND) $(&amp;lt;:.cpp=.o) $&amp;lt; $(SRCS_INC) $(LIBS_INC) &amp;gt; $@
include $(DEF_C) $(DEF_CPP)
clean:
rm -rf $(OBJS_C) $(OBJS_CPP)
rm -rf $(TARGET)
depend:
rm -rf $(DEF_C) $(DEF_CPP)
distclean:
rm -rf $(OBJS_C) $(OBJS_CPP)
rm -rf $(TARGET)
rm -rf $(DEF_C) $(DEF_CPP)
install:
cp src/*.h /home/disk2/zp/work_space/thirdlib/mdd/inc -rf
cp *.so /home/disk2/zp/work_space/thirdlib/mdd/ -rf
.PHONY:$(TARGET)
.PHONY: clean
.PHONY: depend
.PHONY: distclean
.PHONY: install
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;####################################
# 所有与第三方库相关的参数全放在这里
####################################
##########include dir#############
LIBS_INC :=
#LIBS_INC += -I/home/disk2/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include
#LIBS_INC += -I/home/disk2/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include/mkp
#LIBS_INC += -I/home/disk2/zp/work_space/thirdlib/mdd/inc
LIBS_INC += -I/home/share_disk/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include
LIBS_INC += -I/home/share_disk/sdk/Hi3531A_SDK_V1.0.3.0/mpp/include/mkp
LIBS_INC += -I/home/share_disk/workspace/thirdlib/mdd/inc
##########lib dir#############
LIBS_LIB :=
#LIBS_LIB += -L/home/disk2/sdk/Hi3531A_SDK_V1.0.3.0/mpp/lib
#LIBS_LIB += -L/home/disk2/zp/work_space/thirdlib/mdd
LIBS_LIB += -L/home/share_disk/sdk/Hi3531A_SDK_V1.0.3.0/mpp/lib
LIBS_LIB += -L/home/share_disk/workspace/thirdlib/mdd
##########load option#############
LDFLAGS :=
LDFLAGS += -lpthread -lm -lstdc++ -lmpi -ljpeg -lhdmi -ltde -lupvqe -lVoiceEngine -ldnvqe -ldl
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;####################################
# 所有与源码相关的参数全放在这里
####################################
SRCS_INC :=
SRCS_INC += -I.
SRCS_C :=
SRCS_C += $(wildcard ./*.c)
SRCS_CPP :=
SRCS_CPP += $(wildcard ./*.cpp)
############public source code############
#UTIL_TOOLS_DIR := /home/disk2/zp/work_space/code/util_tools/code
UTIL_TOOLS_DIR := /home/share_disk/workspace/code/util_tools/code
SRCS_INC += -I$(UTIL_TOOLS_DIR)/base64
SRCS_INC += -I$(UTIL_TOOLS_DIR)/cJSON
SRCS_INC += -I$(UTIL_TOOLS_DIR)/cross_platform
SRCS_INC += -I$(UTIL_TOOLS_DIR)/error
SRCS_INC += -I$(UTIL_TOOLS_DIR)/filefunc
SRCS_INC += -I$(UTIL_TOOLS_DIR)/inirw
SRCS_INC += -I$(UTIL_TOOLS_DIR)/list
SRCS_INC += -I$(UTIL_TOOLS_DIR)/log
SRCS_INC += -I$(UTIL_TOOLS_DIR)/strfunc
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/base64/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/cJSON/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/cross_platform/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/error/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/filefunc/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/inirw/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/list/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/log/*.c)
SRCS_C += $(wildcard $(UTIL_TOOLS_DIR)/strfunc/*.c)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/base64/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/cJSON/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/cross_platform/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/error/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/filefunc/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/inirw/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/list/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/log/*.cpp)
SRCS_CPP += $(wildcard $(UTIL_TOOLS_DIR)/strfunc/*.cpp)
OBJS_C :=
OBJS_C += $(SRCS_C:.c=.o)
OBJS_CPP :=
OBJS_CPP += $(SRCS_CPP:.cpp=.o)
DEF_C :=
DEF_C += $(SRCS_C:.c=.d)
DEF_CPP :=
DEF_CPP += $(SRCS_CPP:.cpp=.d)
SRCS_MACRO :=
SRCS_MACRO += -DPLATFORM=1
export SRCS_INC #source code directory
export SRCS_C # c source code pathname
export SRCS_CPP # cpp source code pathname
export OBJS_C # The c suffix corresponding to the path name of the .o
export OBJS_CPP # The cpp suffix corresponding to the path name of the .o
export DEF_C # The c suffix corresponding to the path name of the .d
export DEF_CPP # The cpp suffix corresponding to the path name of the .d
export SRCS_MACRO # Source related macros
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>linux_c__attribute__pragma_pack</title><link>/language/clang/linux_c__attribute__pragma_pack/</link><pubDate>Tue, 20 Oct 2020 17:02:49 CST</pubDate><author>rinetd</author><guid>/language/clang/linux_c__attribute__pragma_pack/</guid><description>
&lt;h2 id=&#34;区别-pragma作用于结构内的成员变量-attribute-aligned-n-作用于结构体分配地址的对齐方式-和-结构体的大小&#34;&gt;【区别】 pragma作用于结构内的成员变量；attribute ((aligned(n)))作用于结构体分配地址的对齐方式 和 结构体的大小。&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;typedef struct Student_t
{
int age;
char c;
}__attribute__((aligned (64))) Student;
//影响结构体自身的对齐和大小，每个结构体单独占用64字节 ，不影响其中结构体中元素的对齐
typedef struct Node_t
{
int a;
char c;
} Node __attribute__((aligned(64)));
//只影响Node的对齐, 不影响Node的大小
//对于Node定义的数组，只影响整个数组的起始地址，不影响其中每个数组元素的对齐，每个变量还是占用8个字节，按8字节对齐，总占用内存是64的倍数。
typedef struct Node_p{
int a;
char c;
}__attribute__((packed)) Node_p ;
//影响结构中元素的对齐，通知编译器不做对齐处理等价于 #pragma pack(1)
#pragma pack(push)
#pragma pack(1)
typedef struct
{
int age;
char c;
}user;
#pragma pack(pop)
#pragma pack(push,2)
typedef struct List_t
{
int a;
char c;
}List;
#pragma pack(pop)
//push 和 pop成对出现 ，等价于 #pragma pack(2) 修改结构体中元素对齐规则
typedef int __attribute__((aligned(8))) myint;
// 只影响对齐不影响大小，整个数组按照起始地址8整数倍，数组大小是8的倍数 ，数组元素每个大小还是4
int main()
{
myint MYA[5],mya;
printf(&amp;quot;MYA:%p %p %d mya:%p %d\n&amp;quot;,&amp;amp;MYA[0],&amp;amp;MYA[4],sizeof(MYA),&amp;amp;mya,sizeof(mya));
Student C[5],c;
printf(&amp;quot;C:%p %p %d c:%p %d\n&amp;quot;,&amp;amp;C,&amp;amp;C[4],sizeof(C),&amp;amp;c,sizeof(c)) ;
Node_p cc;
printf(&amp;quot;***%p %p %d***\n&amp;quot;,&amp;amp;(cc.c),&amp;amp;(cc.a),sizeof(Node_p)) ;
Node A[5], a;
printf(&amp;quot;A:%p %p %d a:%p %d\n&amp;quot;,&amp;amp;A,&amp;amp;A[4],sizeof(A),&amp;amp;a,sizeof(a));
List B[5], b;
printf(&amp;quot;B:%p %p %d a:%p %d\n&amp;quot;,&amp;amp;B,&amp;amp;B[4],sizeof(B),&amp;amp;b,sizeof(b)) ;
return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出结果&lt;/p&gt;
&lt;p&gt;x86下，GCC默认按4字节对齐，它会在sex后面跟name后面分别填充三个和两个字节使length和整个结构体对齐。于是我们sizeof(my_stu)会得到长度为20，而不是15.&lt;br /&gt;
　　&lt;br /&gt;
　　四、attribute选项&lt;br /&gt;
　　&lt;br /&gt;
　　struct stu{&lt;br /&gt;
　　 char sex;&lt;br /&gt;
　　 int length;&lt;br /&gt;
　　 char name[10];&lt;br /&gt;
　　}attribute ((aligned (1)));&lt;br /&gt;
　　&lt;br /&gt;
　　struct stu my_stu;　&lt;br /&gt;
　　则sizeof(my_stu)可以得到大小为15。&lt;br /&gt;
　　&lt;br /&gt;
　　上面的定义等同于&lt;br /&gt;
　　struct stu{&lt;br /&gt;
　　 char sex;&lt;br /&gt;
　　 int length;&lt;br /&gt;
　　 char name[10];&lt;br /&gt;
　　}attribute ((packed));&lt;br /&gt;
　　struct stu my_stu;&lt;br /&gt;
　　&lt;br /&gt;
　 attribute((packed))得变量或者结构体成员使用最小的对齐方式，即对变量是一字节对齐，对域（field）是位对齐.&lt;br /&gt;
　　&lt;br /&gt;
　　五、什么时候需要设置对齐&lt;br /&gt;
　　&lt;br /&gt;
　　 在设计不同CPU下的通信协议时，或者编写硬件驱动程序时寄存器的结构这两个地方都需要按一字节对齐。即使看起来本来就自然对齐的也要使其对齐，以免不同的编译器生成的代码不一样.&lt;/p&gt;
&lt;p&gt;一、快速理解&lt;/p&gt;
&lt;p&gt;什么是字节对齐？&lt;br /&gt;
在C语言中，结构是一种复合数据类型，其构成元素既可以是基本数据类型（如int、long、float等）的变量，也可以是一些复合数据类型（如数组、结构、联合等）的数据单元。在结构中，编译器为结构的每个成员按其自然边界（alignment）分配空间。各个成员按照它们被声明的顺序在内存中顺序存储，第一个成员的地址和整个结构的地址相同。&lt;/p&gt;
&lt;p&gt;为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”. 比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.&lt;/p&gt;
&lt;p&gt;字节对齐有什么作用？&lt;br /&gt;
字节对齐的作用不仅是便于cpu快速访问，同时合理的利用字节对齐可以有效地节省存储空间。&lt;/p&gt;
&lt;p&gt;对于32位机来说，4字节对齐能够使cpu访问速度提高，比如说一个long类型的变量，如果跨越了4字节边界存储，那么cpu要读取两次，这样效率就低了。但是在32位机中使用1字节或者2字节对齐，反而会使变量访问速度降低。所以这要考虑处理器类型，另外还得考虑编译器的类型。在vc中默认是4字节对齐的，GNU gcc 也是默认4字节对齐。&lt;/p&gt;
&lt;p&gt;更改C编译器的缺省字节对齐方式&lt;br /&gt;
在缺省情况下，C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地，可以通过下面的方法来改变缺省的对界条件：&lt;br /&gt;
· 使用伪指令#pragma pack (n)，C编译器将按照n个字节对齐。&lt;br /&gt;
· 使用伪指令#pragma pack ()，取消自定义字节对齐方式。&lt;/p&gt;
&lt;p&gt;另外，还有如下的一种方式：&lt;br /&gt;
· __attribute((aligned (n)))，让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n，则按照最大成员的长度来对齐。&lt;br /&gt;
· attribute ((packed))，取消结构在编译过程中的优化对齐，按照实际占用字节数进行对齐。&lt;/p&gt;
&lt;p&gt;举例说明&lt;br /&gt;
struct test&lt;br /&gt;
{&lt;br /&gt;
char x1;&lt;br /&gt;
short x2;&lt;br /&gt;
float x3;&lt;br /&gt;
char x4;&lt;br /&gt;
};&lt;br /&gt;
译器默认情况下会对这个struct作自然边界（有人说“自然对界”我觉得边界更顺口）对齐，结构的第一个成员x1，其偏移地址为0，占据了第1个字节。第二个成员x2为short类型，其起始地址必须2字节对界，因此，编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上，在它们前面不需要额外的填充字节。在test结构中，成员x3要求4字节对界，是该结构所有成员中要求的最大边界单元，因而test结构的自然对界条件为4字节，编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。&lt;/p&gt;</description></item><item><title>libev_io_socket</title><link>/language/clang/libev/libev_io_socket/</link><pubDate>Mon, 19 Oct 2020 17:50:02 CST</pubDate><author>rinetd</author><guid>/language/clang/libev/libev_io_socket/</guid><description>&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;/*
* @author :
* @date : 2014-09-04
* @desc : tiny socket server implemented by libev
* to use this, you should install libev at first.
*
* server: just run the program
* client: telnet localhost 8080
*
* @refer : 1). http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod
* 2). http://blog.csdn.net/lengzijian/article/details/8315133
*
*/
#include &amp;lt;ev.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#define PORT 8080
#define BUFFER_SIZE 1024
#define MAX_CONNECTIONS 10
struct ev_io *libevlist[MAX_CONNECTIONS] = {NULL};
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents);
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents);
/*
Server Client
socket socket
| |
v v
bind connect
| |
v v
listen write
| |
v v
accept read
| |
v v
read close
|
v
write
|
v
close
*/
int main() {
struct ev_loop *loop = ev_default_loop(0);
/* socket start */
int sd;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
struct ev_io *socket_watcher = (struct ev_io*)malloc(sizeof(struct ev_io));
struct ev_timer *timeout_watcher = (struct ev_timer*)malloc(sizeof(struct ev_timer));
// socket
sd = socket(PF_INET, SOCK_STREAM, 0);
if (sd &amp;lt; 0) {
printf(&amp;quot;socket error\n&amp;quot;);
return -1;
}
bzero(&amp;amp;addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
// bind
if (bind(sd, (struct sockaddr*) &amp;amp;addr, sizeof(addr)) != 0) {
printf(&amp;quot;bind error\n&amp;quot;);
return -1;
}
// listen
if (listen(sd, SOMAXCONN) &amp;lt; 0) {
printf(&amp;quot;listen error\n&amp;quot;);
return -1;
}
// set sd reuseful
int bReuseaddr = 1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const char*) &amp;amp;bReuseaddr, sizeof(bReuseaddr)) != 0) {
printf(&amp;quot;setsockopt error in reuseaddr[%d]\n&amp;quot;, sd);
return -1;
}
/* socket end */
ev_io_init(socket_watcher, on_accept, sd, EV_READ);
ev_io_start(loop, socket_watcher);
while(1) {
ev_run(loop, 0);
}
return 1;
}
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents) {
printf(&amp;quot;I am: %d\n&amp;quot;, getpid());
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sd;
// ev_io watcher for client
struct ev_io *client_watcher = (struct ev_io*) malloc(sizeof(struct ev_io));
if (client_watcher == NULL) {
printf(&amp;quot;malloc error in accept_cb\n&amp;quot;);
return;
}
if (EV_ERROR &amp;amp; revents) {
printf(&amp;quot;error event in accept\n&amp;quot;);
return;
}
// socket accept: get file description
client_sd = accept(watcher-&amp;gt;fd, (struct sockaddr*) &amp;amp;client_addr, &amp;amp;client_len);
if (client_sd &amp;lt; 0) {
printf(&amp;quot;accept error\n&amp;quot;);
return;
}
// too much connections
if (client_sd &amp;gt; MAX_CONNECTIONS) {
printf(&amp;quot;fd too large[%d]\n&amp;quot;, client_sd);
close(client_sd);
return;
}
if (libevlist[client_sd] != NULL) {
printf(&amp;quot;client_sd not NULL fd is [%d]\n&amp;quot;, client_sd);
return;
}
printf(&amp;quot;client connected\n&amp;quot;);
// listen new client
ev_io_init(client_watcher, on_read, client_sd, EV_READ);
ev_io_start(loop, client_watcher);
libevlist[client_sd] = client_watcher;
}
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents) {
char buffer[BUFFER_SIZE];
ssize_t read;
if (EV_ERROR &amp;amp; revents) {
printf(&amp;quot;error event in read\n&amp;quot;);
return;
}
// socket recv
read = recv(watcher-&amp;gt;fd, buffer, BUFFER_SIZE, 0); // read stream to buffer
if (read &amp;lt; 0) {
printf(&amp;quot;read error\n&amp;quot;);
return;
}
if (read == 0) {
printf(&amp;quot;client disconnected.\n&amp;quot;);
if (libevlist[watcher-&amp;gt;fd] == NULL) {
printf(&amp;quot;the fd already freed[%d]\n&amp;quot;, watcher-&amp;gt;fd);
}
else {
close(watcher-&amp;gt;fd);
ev_io_stop(loop, libevlist[watcher-&amp;gt;fd]);
free(libevlist[watcher-&amp;gt;fd]);
libevlist[watcher-&amp;gt;fd] = NULL;
}
return;
}
else {
printf(&amp;quot;receive message:%s\n&amp;quot;, buffer);
}
// socket send to client
send(watcher-&amp;gt;fd, buffer, read, 0);
bzero(buffer, read);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt; //inet_addr
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;ev.h&amp;gt;
#define PORT_NO 5000
#define BUFFER_SIZE 10240
#define MAX_BACKLOG 1024
int total_clients = 0;
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents);
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents);
int new_tcp_server(const char *host, short port);
int set_nonblock(int fd);
int set_reuse_socket(int sock);
int set_address (const char *host, short port, struct sockaddr_in *addr);
int main(int argc , char *argv[])
{
struct ev_loop *loop = ev_default_loop(0);
int port = PORT_NO;
if(argc==2){
port = atoi(argv[1]);
}else if(argc &amp;gt; 2){
fprintf(stderr, &amp;quot;usage: server_libev [port]\n&amp;quot;);
return 1;
}
int fd = new_tcp_server(&amp;quot;0.0.0.0&amp;quot;, port);
printf(&amp;quot;Starting tcp server on :%d\n&amp;quot;, port);
ev_io listen_watcher;
ev_io_init(&amp;amp;listen_watcher, on_accept, fd, EV_READ);
ev_io_start(loop, &amp;amp;listen_watcher);
ev_loop(loop, 0);
return 0;
}
int new_tcp_server(const char *host, short port) {
int fd;
struct sockaddr_in address;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
perror(&amp;quot;socket&amp;quot;);
return -1;
}
set_reuse_socket(fd);
set_nonblock(fd);
set_address(host, port, &amp;amp;address);
if (bind(fd, (struct sockaddr*)&amp;amp;address, sizeof(address)) &amp;lt; 0) {
perror(&amp;quot;bind&amp;quot;);
close(fd);
return -1;
}
if (listen(fd, MAX_BACKLOG) &amp;lt;0){
perror(&amp;quot;listen&amp;quot;);
close(fd);
return -1;
};
return fd;
}
int set_address (const char *host, short port, struct sockaddr_in *addr)
{
memset(addr, 0, sizeof(*addr));
addr-&amp;gt;sin_family = AF_INET;
addr-&amp;gt;sin_addr.s_addr = inet_addr(host);
addr-&amp;gt;sin_port = htons(port );
return 0;
}
int set_nonblock(int fd) {
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}
int set_reuse_socket(int fd) {
int ok = 1;
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &amp;amp;ok, sizeof(ok));
}
void on_accept(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
struct ev_io *client_watcher = (struct ev_io*)malloc(sizeof(struct ev_io));
if(EV_ERROR &amp;amp; revents) {
perror(&amp;quot;invalid event&amp;quot;);
return;
}
int client_fd = accept(watcher-&amp;gt;fd, (struct sockaddr*)&amp;amp;client_addr, &amp;amp;client_len);
if (client_fd &amp;lt; 0) {
perror(&amp;quot;accept&amp;quot;);
return;
}
set_nonblock(client_fd);
total_clients++;
ev_io_init(client_watcher, on_read, client_fd, EV_READ);
ev_io_start(loop, client_watcher);
}
void on_read(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
char buffer[BUFFER_SIZE];
ssize_t len;
if(EV_ERROR &amp;amp; revents) {
return;
}
len = recv(watcher-&amp;gt;fd, buffer, BUFFER_SIZE, 0);
if (len &amp;lt; 0) {
return;
} else if (len == 0) {
// disconnected
ev_io_stop(loop, watcher);
close(watcher-&amp;gt;fd);
free(watcher);
total_clients--;
return;
}
send(watcher-&amp;gt;fd, buffer, len, 0);
memset(buffer, 0, BUFFER_SIZE);
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>libev</title><link>/language/clang/libev/libev/</link><pubDate>Mon, 19 Oct 2020 16:01:15 CST</pubDate><author>rinetd</author><guid>/language/clang/libev/libev/</guid><description>
&lt;p&gt;&lt;a href=&#34;https://blog.csdn.net/weiwangchao_/article/details/52961041&#34; target=&#34;_blank&#34;&gt;libev教程一：libev简单入门_把握自己。-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;libev是个非常优秀的基于事件的循环库，很多开源软件，比如nodejs就是使用其实现基础功能。本系列将对该库进行源码分析。&lt;a href=&#34;http://blog.csdn.net/breaksoftware/article/details/76066676&#34; target=&#34;_blank&#34;&gt;（转载请指明出于breaksoftware的csdn博客）&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不知道是被墙了还是网站不再维护，它的官网（&lt;a href=&#34;http://libev.schmorp.de/）在国内已经没法访问了。&#34; target=&#34;_blank&#34;&gt;http://libev.schmorp.de/）在国内已经没法访问了。&lt;/a&gt;&lt;br /&gt;
但是我们仍然可以从github上下载其源码（&lt;a href=&#34;https://github.com/enki/libev）。&#34; target=&#34;_blank&#34;&gt;https://github.com/enki/libev）。&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;交叉编译&#34;&gt;交叉编译&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;./configure --host=arm-none-linux-gnueabi --prefix=&lt;/code&gt;pwd&lt;code&gt;/libev&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;./configure --host=arm-mac-linux-gnueabihf CC=arm-mac-linux-gnueabihf-gcc --prefix=&lt;/code&gt;pwd&lt;code&gt;/libev&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;使用样例&#34;&gt;使用样例&lt;/h2&gt;
&lt;p&gt;libev支持相对时间定时器、绝对时间定时器、文件状态监控和信号监控等功能。我们可以在它基础上，通过少量的代码实现稳健完善的功能。&lt;/p&gt;
&lt;p&gt;我们先看一段实现定时器功能的代码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;ev.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
ev_timer timeout_watcher;
static void
timeout_cb(EV_P_ ev_timer *w, int revents)
{
puts(&amp;quot;timeout&amp;quot;);
ev_break(EV_A_ EVBREAK_ONE);
}
int main(void)
{
struct ev_loop *loop = EV_DEFAULT;
ev_timer_init(&amp;amp;timeout_watcher, timeout_cb, 5.5, 0);
ev_timer_start(loop, &amp;amp;timeout_watcher);
ev_run(loop, 0);
return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/unistd.h&amp;gt;
#include &amp;lt;ev.h&amp;gt;
void io_action(struct ev_loop *main_loop,ev_io *io_w,int e)
{
int rst;
char buf[1024];
memset(buf,0,sizeof(buf));
puts(&amp;quot;In IO action&amp;quot;);
read(STDIN_FILENO,buf,sizeof(buf));
buf[1023]=&#39;\0&#39;;
printf(&amp;quot;String: %s\n&amp;quot;,buf);
ev_io_stop(main_loop,io_w);
}
void timer_action(struct ev_loop *main_loop,ev_timer *time_w,int e)
{
puts(&amp;quot;In Time action&amp;quot;);
ev_timer_stop(main_loop,time_w);
}
void signal_action(struct ev_loop *main_loop,ev_signal *signal_w,int e)
{
puts(&amp;quot;In Signal action&amp;quot;);
ev_signal_stop(main_loop,signal_w);
ev_break(main_loop,EVBREAK_ALL);
}
int main(int argc,char **argv)
{
ev_io io_w;
ev_timer timer_w;
ev_signal signal_w;
struct ev_loop *main_loop = ev_default_loop(0);
ev_init(&amp;amp;io_w,io_action);
ev_io_set(&amp;amp;io_w,STDIN_FILENO,EV_READ);
ev_init(&amp;amp;timer_w,timer_action);
ev_timer_set(&amp;amp;timer_w,2,0);
ev_init(&amp;amp;signal_w,signal_action);
ev_signal_set(&amp;amp;signal_w,SIGINT);
ev_io_start(main_loop,&amp;amp;io_w);
ev_timer_start(main_loop,&amp;amp;timer_w);
ev_signal_start(main_loop,&amp;amp;signal_w);
ev_run(main_loop,0);
return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码的结构非常简单。首先我们要定义一个名为timeout_cb的回调函数用于响应定时器。然后定义一个ev_timer结构（监视器），它通过ev_timer_init进行初始化。初始化的参数包含之前定义的响应函数指针和迭代器超时时间。ev_timer准备好后，通过ev_timer_start将其和一个ev_loop进行绑定。最后使用ev_run方法运行起来这个ev_loop指针，从而实现一个完整的定时器功能。&lt;/p&gt;
&lt;p&gt;可见使用libev库非常方便。其实我们之后见到的其他用法和上面步骤是类似的，即：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化ev_loop。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;定义监视器。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;定义回调函数。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;监视器和回调函数关联。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;监视器和ev_loop关联。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_run将ev_loop运行起来。&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;假如上面代码是个框架使用的雏形，那么如果让我们去设计这样的框架，该如何设计呢？&lt;/p&gt;
&lt;h2 id=&#34;模型设计&#34;&gt;模型设计&lt;/h2&gt;
&lt;p&gt;首先我们需要考虑到的是使用sleep还是使用事件模型去实现逻辑等待。&lt;/p&gt;
&lt;p&gt;如果使用sleep方法，那么我们就要在唤醒后去检测各个事件，比如要检测文件状态是否发生变化，比如定时器时间是否已经超时。于是有个问题，就是sleep多久怎么确定？我们不知道是5秒后还是1秒后文件状态发生变化，那么只能最小粒度sleep了。那么这就意味着线程在短暂挂起后，马上检测一系列可能尚未发生改变的事件。这种设计明显很消耗CPU，而且非常低效。&lt;/p&gt;
&lt;p&gt;如果使用事件模型去等待，就可以解决上述问题。但是像定时器，在系统中并没有事件与其对应。于是我们需要考虑下对于没有事件对应的功能怎么通过事件模型去封装。&lt;/p&gt;
&lt;p&gt;其次我们需要考虑使用单线程模型还是多线程模型。&lt;/p&gt;
&lt;p&gt;单线程模型是让主流程和事件响应函数在一个线程中执行。其伪代码是&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;If (event is ready) {
event_callback(); // in the main thead
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其特点是实现简单，但是事件响应函数的效率将严重影响主流程对事件的响应速度。比如A、B两个事件同时发生，理论上我们希望两个事件的响应函数被同时执行，或者在允许存在的系统调用时间差（比如创建线程的消耗）内执行。然而单线程模型则会让一个响应函数执行完后再去执行另一响应函数，于是就出现排队现象。所以单线程模型无法保证及时响应。&lt;/p&gt;
&lt;p&gt;多线程模型则完全避免了上述问题。它可在事件发生后启动一个线程去处理响应函数。当然相对来说多线程模型比较复杂，需要考虑多线程同步问题。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;If (event is ready) {
thread_excute(event_callback); // run in another thread
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么libev对上面两个问题是怎么选择的呢？对于sleep和事件模型，libev选择的是后者，所以它是“高性能”的。对于单线程和多线程，libev选择的是前者。至于原因我并不知道，可能是作者希望它足够简单，或者希望它能在不支持多线程的系统上使用。但是要说明一点，并不是说libev不支持多线程。因为一个单线程模型的执行体，是可以放在其他若干个线程中执行的，只要保证数据同步。&lt;/p&gt;
&lt;h2 id=&#34;单-多线程编译&#34;&gt;单/多线程编译&lt;/h2&gt;
&lt;p&gt;libev提供了各种编译选项以支持各种特性。比如在支持多线程的系统上，我们可以指定EV_MULTIPLICITY参数，以让libev编译出多线程版本。&lt;/p&gt;
&lt;p&gt;libev对于单线程版本的数据都是以全局静态变量形式提供。而对于多线程版本，则提供了一个结构体——ev_loop保存数据，这样不同线程持有各自的数据对象，从而做到数据隔离。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#if EV_MULTIPLICITY
struct ev_loop
{
ev_tstamp ev_rt_now;
#define ev_rt_now ((loop)-&amp;gt;ev_rt_now)
#define VAR(name,decl) decl;
#include &amp;quot;ev_vars.h&amp;quot;
#undef VAR
};
#include &amp;quot;ev_wrap.h&amp;quot;
static struct ev_loop default_loop_struct;
EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */
#else
EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */
#define VAR(name,decl) static decl;
#include &amp;quot;ev_vars.h&amp;quot;
#undef VAR
static int ev_default_loop_ptr;
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不管是哪个版本，它们都提供了ev_default_loop_ptr变量。多线程版本它将指向全局静态变量default_loop_struct，这样对于使用了多线程版本又不想维护ev_loop结构对象的用户来说，直接使用这个对象就行了，非常方便。&lt;/p&gt;
&lt;p&gt;然后再看下ev_vars.h的引入。其实现如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#define VARx(type,name)VAR(name, type name)
VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */
VARx(ev_tstamp, mn_now) /* monotonic clock &amp;quot;now&amp;quot; */
VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */
/* for reverse feeding of events */
VARx(W *, rfeeds)
VARx(int, rfeedmax)
VARx(int, rfeedcnt)
VAR (pendings, ANPENDING *pendings [NUMPRI])
VAR (pendingmax, int pendingmax [NUMPRI])
VAR (pendingcnt, int pendingcnt [NUMPRI])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在多线程版本中，它在ev_loop结构体中被引入的。这样在编译器展开文件时，它将会被定义到结构体内部。在单线程版本中，VAR宏被声明为定义一个静态全局变量的形式。这种利用宏和编译展开技术，在不同结构中定义相同类型数据的方式还是很有意思的。&lt;/p&gt;
&lt;p&gt;但是又会有个问题，如何去访问这些变量呢？在单线程中，它们是静态变量，所有位置可以直接通过名称访问。而多线程版本中，则需要通过一个ev_loop结构体去引导。相关的代码总不能通过EV_MULTIPLICITY宏来区分不同变量形式吧？如果那样，代码将变得非常难看。我们看下libev怎么巧妙解决这个问题的。&lt;/p&gt;
&lt;p&gt;上面代码块的多线程定义区间，引入了ev_wrap.h文件。其实现如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#ifndef EV_WRAP_H
#define EV_WRAP_H
#define acquire_cb ((loop)-&amp;gt;acquire_cb)
#define activecnt ((loop)-&amp;gt;activecnt)
#define anfdmax ((loop)-&amp;gt;anfdmax)
#define anfds ((loop)-&amp;gt;anfds)
#define async_pending ((loop)-&amp;gt;async_pending)
#define asynccnt ((loop)-&amp;gt;asynccnt)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样使用一个和变量相同名称的宏替代了通过ev_loop结构体对象访问的变量。且这个宏名称和单线程版本中静态变量名相同。这样就让不同版本的关键变量“同名”了。于是代码对这些变量的访问直接使用其原始名称即可——单线程中使用的是真实变量名，多线程中使用的是宏。&lt;br /&gt;
这样的设计，又引入一个问题。那就是所有使用这些变量的函数，在多线程版本中，需要提供一个名字为loop的ev_loop结构体对象；而在单线程版本中则不需要。为了固化这个名称，libev还为此专门定义了一系列宏。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#if EV_MULTIPLICITY
struct ev_loop;
# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */
# define EV_P_ EV_P, /* a loop as first of multiple parameters */
# define EV_A loop /* a loop as sole argument to a function call */
# define EV_A_ EV_A, /* a loop as first of multiple arguments */
# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */
# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */
# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */
# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */
#else
# define EV_P void
# define EV_P_
# define EV_A
# define EV_A_
# define EV_DEFAULT
# define EV_DEFAULT_
# define EV_DEFAULT_UC
# define EV_DEFAULT_UC_
# undef EV_EMBED_ENABLE
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后我们在代码中导出可见的EV_P和EV_A就是为了保证不同版本的实现在代码层面是相同的。&lt;/p&gt;
&lt;h2 id=&#34;libev&#34;&gt;libev&lt;/h2&gt;
&lt;h3 id=&#34;1-1-introduction&#34;&gt;1.1 Introduction&lt;/h3&gt;
&lt;p&gt;主页&lt;a href=&#34;http://software.schmorp.de/pkg/libev.html&#34; target=&#34;_blank&#34;&gt;http://software.schmorp.de/pkg/libev.html&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;文档&lt;a href=&#34;http://software.schmorp.de/pkg/libev.html&#34; target=&#34;_blank&#34;&gt;http://software.schmorp.de/pkg/libev.html&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;libev所实现的功能就是一个强大的reactor,可能notify事件主要包括下面这些：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ev_io // IO可读可写&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_stat // 文件属性变化&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_async // 激活线程&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_signal // 信号处理&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_timer // 定时器&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_periodic // 周期任务&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_child // 子进程状态变化&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_fork // 开辟进程&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_cleanup // event loop退出触发事件&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_idle // 每次event loop空闲触发事件&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_embed // TODO(zhangyan04):I have no idea.&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_prepare // 每次event loop之前事件&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;ev_check // 每次event loop之后事件&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;1-2-about-the-code&#34;&gt;1.2 About The Code&lt;/h3&gt;
&lt;p&gt;代码风格相当严谨而且排版也非常工整，并且从域名看出作者是德国人。但是内部使用了大量的宏造成阅读代码并不是非常方便。&lt;/p&gt;
&lt;p&gt;并且从代码角度分析，应该是一开始支持有一个默认的event_loop,但是随着多核产生实际应用中可能会使用到多个event_loop, 猜想作者应该是为了方便的话使用了很多宏进行替换。&lt;/p&gt;
&lt;p&gt;允许使用多个event_loop的宏是EV_MULTIPLICITY.&lt;/p&gt;
&lt;p&gt;比如下面这段代码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void noinline
ev_io_start (EV_P_ ev_io *w)
{
int fd = w-&amp;gt;fd;
if (expect_false (ev_is_active (w)))
return;
assert ((&amp;quot;libev: ev_io_start called with negative fd&amp;quot;, fd &amp;gt;= 0));
assert ((&amp;quot;libev: ev_io_start called with illegal event mask&amp;quot;, !(w-&amp;gt;events &amp;amp; ~(EV__IOFDSET | EV_READ | EV_WRITE))));
EV_FREQUENT_CHECK;
ev_start (EV_A_ (W)w, 1);
array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
wlist_add (&amp;amp;anfds[fd].head, (WL)w);
fd_change (EV_A_ fd, w-&amp;gt;events &amp;amp; EV__IOFDSET | EV_ANFD_REIFY);
w-&amp;gt;events &amp;amp;= ~EV__IOFDSET;
EV_FREQUENT_CHECK;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初次阅读这个代码会觉得非常难懂。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;宏&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;定义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EV_P&lt;/td&gt;
&lt;td&gt;event parameter&lt;/td&gt;
&lt;td&gt;struct ev_loop* loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EV&lt;em&gt;P&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;EV_P,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EV_A&lt;/td&gt;
&lt;td&gt;event argument&lt;/td&gt;
&lt;td&gt;loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EV&lt;em&gt;A&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;EV_A,&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;然后很多变量只要是ev_loop成员的话都被封装成为了宏。比如代码里面的anfds,实际上的宏定义是&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#define anfds ((loop)-&amp;gt;anfds)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;事实上一个ev_loop里面的字段是相当多的，不过也很正常本身就是一个强大的reactor.但是这造成一个直接后果，就是对于想要了解ev_loop的全貌比较困难，所以想要彻底地了解libev也比较麻烦，所以我们只能够从应用层面来尝试了解它。&lt;/p&gt;
&lt;h3 id=&#34;1-3-eventloop&#34;&gt;1.3 EventLoop&lt;/h3&gt;
&lt;p&gt;首先我们关注一下reactor本身。在libev下面reactor对象称为event_loop.&lt;/p&gt;
&lt;p&gt;event_loop允许动态创建和销毁，并且允许绑定自定义数据&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;struct ev_loop * ev_loop_new (unsigned int flags);
void ev_loop_destroy (EV_P);
void ev_set_userdata (EV_P_ void *data);
void *ev_userdata (EV_P);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们这里主要关注一下flags.这里面主要是选择使用什么backend来进行poll操作，可以选择的有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EVBACKEND_SELECT&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVBACKEND_POLL&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVBACKEND_EPOLL // 通常我们选择这个&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVBACKEND_KQUEUE&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVBACKEND_DEVPOLL&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVBACKEND_PORT&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但是还有三个比较重要选项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EVFLAG_NOINOTIFY // 不适用inofity调用来使用ev_stat.这样可以减少fd使用。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVFLAG_SIGNALFD // 使用signalfd来检测信号是否发生，同样这样可以减少fd使用。&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大部分时候我们使用EVFLAG_AUTO(0)一般就足够满足需求了，从代码角度来看如果支持epoll的话那么首先会选择epoll. 因为在watcher的回调函数里面是可以知道当前event_loop的，这样就可以获得自定义数据。然后我们看看这个event_loop如何运行和停止的&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void ev_run (EV_P_ int flags);
void ev_break (EV_P_ int how);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样我们这里比较关注flags和how这两个参数。flags有下面这几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0.通常这是我们想要的，每次轮询在poll都会等待一段时间然后处理pending事件。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVRUN_NOWAIT.运行一次，在poll时候不会等待。这样效果相当于只是处理pending事件。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVRUN_ONCE.运行一次，但是在poll时候会等待，然后处理pending事件。&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而how有下面这几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EVBREAK_ONE.只是退出一次ev_run这个调用。通常来说使用这个就可以了。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;EVBREAK_ALL.退出所有的ev_run调用。这种情况存在于ev_run在pengding处理时候会递归调用。&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在backend/epoll底层每次epoll_wait时候，libev提供了接口回调可以在epoll_wait前后调用&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void ev_set_loop_release_cb (loop, void (*release)(EV_P), void (*acquire)(EV_P))
static void
epoll_poll (EV_P_ ev_tstamp timeout)
{
/* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */
/* the default libev max wait time, however. */
EV_RELEASE_CB;
eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax,
epoll_epermcnt ? 0 : ev_timeout_to_ms (timeout));
EV_ACQUIRE_CB;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在event_loop里面我们还关心一件事情，就是每次event_loop轮询的时间长短。通常来说这个不会是太大问题，但是在高性能情况下面我们需要设置&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void ev_set_io_collect_interval (EV_P_ ev_tstamp interval);
void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在ev_run里面有使用这些参数的代码比较麻烦。但是大意是这样，如果我们这是了timeout_interval的话，那么我们每次检查timeout时间的话必须在timeout_interval，使用这段时间ev_sleep.但是这个又会影响到io_interval,所以内部做了一些换算，换算的结果作为epoll_wait超时时间。不过同样在大部分时候我们不需要关心它，默认时候是0.0,系统会使用最快的响应方式来处理。&lt;/p&gt;
&lt;h3 id=&#34;1-4-watcher&#34;&gt;1.4 Watcher&lt;/h3&gt;
&lt;p&gt;然后我们关心一下EventHandler.在libev下面watcher相当于EventHandler这么一个概念，通常里面会绑定fd回调函数以及我们需要关注的事件。然后一旦触发事件之后会触发我们使用的回调函数，回调函数参数通常有reactor,watcher以及触发的事件。这里不打算重复文档里面的watcher 相关的内容和对应的API,但是对于某些内容的话可能会提到并且附带一些注释。之前我们还是看看通用过程，这里使用TYPE区分不同类型watcher.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;typedef void (*)(struct ev_loop *loop, ev_TYPE *watcher, int revents) callback; // callback都是这种类型
ev_init (ev_TYPE *watcher, callback); // 初始化watcher
ev_TYPE_set (ev_TYPE *watcher, [args]); // 设置watcher
ev_TYPE_init (ev_TYPE *watcher, callback, [args]); // 通常使用这个函数最方便，初始化和设置都在这里
ev_TYPE_start (loop, ev_TYPE *watcher); // 注册watcher
ev_TYPE_stop (loop, ev_TYPE *watcher); // 注销watcher
ev_set_priority (ev_TYPE *watcher, int priority); // 设置优先级
ev_feed_event (loop, ev_TYPE *watcher, int revents); // 这个做跨线程通知非常有用，相当于触发了某个事件。
bool ev_is_active (ev_TYPE *watcher); // watcher是否active.
bool ev_is_pending (ev_TYPE *watcher); // watcher是否pending.
int ev_clear_pending (loop, ev_TYPE *watcher); // 清除watcher pending状态并且返回事件
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;wacther的状态有下面这么几种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;initialiased. 调用init函数初始化&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;active. 调用start进行注册&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;pending. 已经触发事件但是没有处理&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;inactive. 调用stop注销。这个状态等同于initialised这个状态。&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实关于每个watcher具体是怎么实现的没有太多意思，因为大部分现有代码都差不多。会在下一节说说内部数据结构是怎么安排的，了解内部数据结构以及过程之后很多问题就可以避免了，比如&amp;quot;The special problem of disappearing file descriptors&amp;quot;这类问题。&lt;/p&gt;
&lt;h3 id=&#34;1-5-how-it-works&#34;&gt;1.5 How it works&lt;/h3&gt;
&lt;h4 id=&#34;1-5-1-ev-run&#34;&gt;1.5.1 ev_run&lt;/h4&gt;
&lt;p&gt;最主要的还是看看ev_run这个部分代码。我们不打算仔细阅读只是看看梗概然后大体分析一下数据结构应该怎么样的&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void
ev_run (EV_P_ int flags)
{
assert ((&amp;quot;libev: ev_loop recursion during release detected&amp;quot;, loop_done != EVBREAK_RECURSE));
loop_done = EVBREAK_CANCEL;
EV_INVOKE_PENDING; /* in case we recurse, ensure ordering stays nice and clean */
do
{
if (expect_false (loop_done))
break;
/* update fd-related kernel structures */
fd_reify (EV_A);
/* calculate blocking time */
{
ev_tstamp waittime = 0.;
ev_tstamp sleeptime = 0.;
/* remember old timestamp for io_blocktime calculation */
ev_tstamp prev_mn_now = mn_now;
/* update time to cancel out callback processing overhead */
time_update (EV_A_ 1e100);
if (expect_true (!(flags &amp;amp; EVRUN_NOWAIT || idleall || !activecnt)))
{
waittime = MAX_BLOCKTIME;
if (timercnt)
{
ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now + backend_fudge;
if (waittime &amp;gt; to) waittime = to;
}
/* don&#39;t let timeouts decrease the waittime below timeout_blocktime */
if (expect_false (waittime &amp;lt; timeout_blocktime))
waittime = timeout_blocktime;
/* extra check because io_blocktime is commonly 0 */
if (expect_false (io_blocktime))
{
sleeptime = io_blocktime - (mn_now - prev_mn_now);
if (sleeptime &amp;gt; waittime - backend_fudge)
sleeptime = waittime - backend_fudge;
if (expect_true (sleeptime &amp;gt; 0.))
{
ev_sleep (sleeptime);
waittime -= sleeptime;
}
}
}
assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
backend_poll (EV_A_ waittime);
assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
/* update ev_rt_now, do magic */
time_update (EV_A_ waittime + sleeptime);
}
/* queue pending timers and reschedule them */
timers_reify (EV_A); /* relative timers called last */
EV_INVOKE_PENDING;
}
while (expect_true (
activecnt
&amp;amp;&amp;amp; !loop_done
&amp;amp;&amp;amp; !(flags &amp;amp; (EVRUN_ONCE | EVRUN_NOWAIT))
));
if (loop_done == EVBREAK_ONE)
loop_done = EVBREAK_CANCEL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以总结一下大致步骤，其实和大部分的event loop写出来差不多。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先触发那些已经pending的watchers.&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;判断是否loop_done&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;fd_reify.这个后面会单独说。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;计算出waittime并且进行必要的sleep.&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;backend_poll开始轮询,并且整理好pending事件&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;timers_reify.这个和fd_reify不同&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;调用EV_INVOKE_PENDING来触发pending的io事件&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;非常简单。接下来我们看看fd_reify,backend_poll,timers_reify以及EV_INVOKE_PENDING.&lt;/p&gt;
&lt;h4 id=&#34;1-5-2-fd-reify&#34;&gt;1.5.2 fd_reify&lt;/h4&gt;
&lt;p&gt;下面是fd_reify代码片段.可以看出，这个部分就是在修改fd关注的events。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;inline_size void
fd_reify (EV_P)
{
int i;
for (i = 0; i &amp;lt; fdchangecnt; ++i)
{
int fd = fdchanges [i];
ANFD *anfd = anfds + fd;
ev_io *w;
unsigned char o_events = anfd-&amp;gt;events;
unsigned char o_reify = anfd-&amp;gt;reify;
anfd-&amp;gt;reify = 0;
/*if (expect_true (o_reify &amp;amp; EV_ANFD_REIFY)) probably a deoptimisation */
{
anfd-&amp;gt;events = 0;
for (w = (ev_io *)anfd-&amp;gt;head; w; w = (ev_io *)((WL)w)-&amp;gt;next)
anfd-&amp;gt;events |= (unsigned char)w-&amp;gt;events;
if (o_events != anfd-&amp;gt;events)
o_reify = EV__IOFDSET; /* actually |= */
}
if (o_reify &amp;amp; EV__IOFDSET)
backend_modify (EV_A_ fd, o_events, anfd-&amp;gt;events);
}
fdchangecnt = 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而这个fdchanges这个是在哪里调用的呢。我们可以看到就是在ev_io_start这个部分。也就是说如果我们想要修改 fd关注事件的话，我们必须显示地ev_io_stop掉然后修正之后重新ev_io_start.底层调用fd_change的话底层维护数组fdchanges来保存发生events变动的fd.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void noinline
ev_io_start (EV_P_ ev_io *w)
{
int fd = w-&amp;gt;fd;
if (expect_false (ev_is_active (w)))
return;
assert ((&amp;quot;libev: ev_io_start called with negative fd&amp;quot;, fd &amp;gt;= 0));
assert ((&amp;quot;libev: ev_io_start called with illegal event mask&amp;quot;, !(w-&amp;gt;events &amp;amp; ~(EV__IOFDSET | EV_READ | EV_WRITE))));
EV_FREQUENT_CHECK;
ev_start (EV_A_ (W)w, 1);
array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
wlist_add (&amp;amp;anfds[fd].head, (WL)w);
fd_change (EV_A_ fd, w-&amp;gt;events &amp;amp; EV__IOFDSET | EV_ANFD_REIFY);
w-&amp;gt;events &amp;amp;= ~EV__IOFDSET;
EV_FREQUENT_CHECK;
}
inline_size void
fd_change (EV_P_ int fd, int flags)
{
unsigned char reify = anfds [fd].reify;
anfds [fd].reify |= flags;
if (expect_true (!reify))
{
++fdchangecnt;
array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2);
fdchanges [fdchangecnt - 1] = fd;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;1-5-3-backend-poll&#34;&gt;1.5.3 backend_poll&lt;/h4&gt;
&lt;p&gt;backend_poll底层支持很多poll实现，我们这里仅仅看ev_epoll.c就可以.代码在这里面我们不列举了，如果某个fd触发事件的话那么最终会调用fd_event(EV&lt;em&gt;A&lt;/em&gt;,fd,event)来进行通知。所以我们看看fd_event.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;inline_speed void
fd_event_nocheck (EV_P_ int fd, int revents)
{
ANFD *anfd = anfds + fd;
ev_io *w;
for (w = (ev_io *)anfd-&amp;gt;head; w; w = (ev_io *)((WL)w)-&amp;gt;next)
{
int ev = w-&amp;gt;events &amp;amp; revents;
if (ev)
ev_feed_event (EV_A_ (W)w, ev);
}
}
void noinline
ev_feed_event (EV_P_ void *w, int revents)
{
W w_ = (W)w;
int pri = ABSPRI (w_);
if (expect_false (w_-&amp;gt;pending))
pendings [pri][w_-&amp;gt;pending - 1].events |= revents;
else
{
w_-&amp;gt;pending = ++pendingcnt [pri];
array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_-&amp;gt;pending, EMPTY2);
// set the watcher and revents.
pendings [pri][w_-&amp;gt;pending - 1].w = w_;
pendings [pri][w_-&amp;gt;pending - 1].events = revents;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到底层是一个ANFD的数组，根据fd进行偏移。如果fd过大的话似乎会影响性能没有hpserver里面的demuxtable实现方式好。然后得到这个fd下面所有的watcher,然后在loop-&amp;gt;pendings里面记录所有这些触发的watcher.&lt;/p&gt;
&lt;h4 id=&#34;1-5-4-timers-reify&#34;&gt;1.5.4 timers_reify&lt;/h4&gt;
&lt;p&gt;其中HEAP0就是最小堆下标。如果repeat的话说明需要重复发生，那么就会重新调整时间戳，如果不是repeat的话，那么内部会调用ev_timer_stop这个方法将这个计时器移除。所有的定时任务都通过feed_reverse添加。feed_reverse 内部是维护一个动态数组来保存所有的定时器任务，然后在feed_reverse_done里面遍历这些任务来触发这些定时器任务。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;inline_size void
timers_reify (EV_P)
{
EV_FREQUENT_CHECK;
if (timercnt &amp;amp;&amp;amp; ANHE_at (timers [HEAP0]) &amp;lt; mn_now)
{
do
{
ev_timer *w = (ev_timer *)ANHE_w (timers [HEAP0]);
/*assert ((&amp;quot;libev: inactive timer on timer heap detected&amp;quot;, ev_is_active (w)));*/
/* first reschedule or stop timer */
if (w-&amp;gt;repeat)
{
ev_at (w) += w-&amp;gt;repeat;
if (ev_at (w) &amp;lt; mn_now)
ev_at (w) = mn_now;
assert ((&amp;quot;libev: negative ev_timer repeat value found while processing timers&amp;quot;, w-&amp;gt;repeat &amp;gt; 0.));
ANHE_at_cache (timers [HEAP0]);
downheap (timers, timercnt, HEAP0);
}
else
ev_timer_stop (EV_A_ w); /* nonrepeating: stop timer */
EV_FREQUENT_CHECK;
feed_reverse (EV_A_ (W)w);
}
while (timercnt &amp;amp;&amp;amp; ANHE_at (timers [HEAP0]) &amp;lt; mn_now);
feed_reverse_done (EV_A_ EV_TIMER);
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;1-5-5-ev-invoke-pending&#34;&gt;1.5.5 EV_INVOKE_PENDING&lt;/h4&gt;
&lt;p&gt;这个宏最终调用的函数就是下面这个，遍历所有的pendings事件并且逐一触发。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void noinline
```c++
ev_invoke_pending (EV_P)
{
int pri;
for (pri = NUMPRI; pri--; )
while (pendingcnt [pri])
{
ANPENDING *p = pendings [pri] + --pendingcnt [pri];
p-&amp;gt;w-&amp;gt;pending = 0;
EV_CB_INVOKE (p-&amp;gt;w, p-&amp;gt;events);
EV_FREQUENT_CHECK;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;1-6-example&#34;&gt;1.6 Example&lt;/h3&gt;
&lt;p&gt;尝试编写一个简单的带有超时的echo-server和echo-client就发现其实还有非常多的其他的工作量，比如buffer的管理状态机实现等。所以我没有写出一个完整的example,只是简单地写了假设echo-client连接上server的话就简单地打印链接信息并且关闭。&lt;/p&gt;
&lt;h4 id=&#34;1-6-1-common-h&#34;&gt;1.6.1 common.h&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;\#ifndef _COMMON_H_
\#define _COMMON_H_
\#include &amp;lt;unistd.h&amp;gt;
\#include &amp;lt;fcntl.h&amp;gt;
\#include &amp;lt;sys/types.h&amp;gt;
\#include &amp;lt;sys/socket.h&amp;gt;
\#include &amp;lt;arpa/inet.h&amp;gt;
\#include &amp;lt;strings.h&amp;gt;
\#include &amp;lt;cstdlib&amp;gt;
\#include &amp;lt;cstdio&amp;gt;
\#include &amp;lt;cstddef&amp;gt;
\#include &amp;lt;string&amp;gt;
namespace common{
\#define D(exp,fmt,...) do {
\
if(!(exp)){
\
fprintf(stderr,fmt,\##__VA_ARGS__); \
abort(); \
} \
}while(0)
static void setnonblock(int fd){
fcntl(fd,F_SETFL,fcntl(fd,F_GETFL) | O_NONBLOCK);
}
static void setreuseaddr(int fd){
int ok=1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&amp;amp;ok,sizeof(ok));
}
static void setaddress(const char* ip,int port,struct sockaddr_in* addr){
bzero(addr,sizeof(*addr));
addr-&amp;gt;sin_family=AF_INET;
inet_pton(AF_INET,ip,&amp;amp;(addr-&amp;gt;sin_addr));
addr-&amp;gt;sin_port=htons(port);
}
static std::string address_to_string(struct sockaddr_in* addr){
char ip[128];
inet_ntop(AF_INET,&amp;amp;(addr-&amp;gt;sin_addr),ip,sizeof(ip));
char port[32];
snprintf(port,sizeof(port),&amp;quot;%d&amp;quot;,ntohs(addr-&amp;gt;sin_port));
std::string r;
r=r+&amp;quot;(&amp;quot;+ip+&amp;quot;:&amp;quot;+port+&amp;quot;)&amp;quot;;
return r;
}
static int new_tcp_server(int port){
int fd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
D(fd&amp;gt;0,&amp;quot;socket failed(%m)\n&amp;quot;);
setnonblock(fd);
setreuseaddr(fd);
sockaddr_in addr;
setaddress(&amp;quot;0.0.0.0&amp;quot;,port,&amp;amp;addr);
bind(fd,(struct sockaddr*)&amp;amp;addr,sizeof(addr));
listen(fd,64); // backlog = 64
return fd;
}
static int new_tcp_client(const char* ip,int port){
int fd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setnonblock(fd);
sockaddr_in addr;
setaddress(ip,port,&amp;amp;addr);
connect(fd,(struct sockaddr*)(&amp;amp;addr),sizeof(addr));
return fd;
}
}; // namespace common
\#endif // _COMMON_H_
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;1-6-2-echo-client-cc&#34;&gt;1.6.2 echo-client.cc&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;\#include &amp;quot;ev.h&amp;quot;
\#include &amp;quot;common.h&amp;quot;
static void do_connected(struct ev_loop* reactor,ev_io* w,int events){
close(w-&amp;gt;fd);
ev_break(reactor,EVBREAK_ALL);
}
int main(){
struct ev_loop* reactor=ev_loop_new(EVFLAG_AUTO);
int fd=common::new_tcp_client(&amp;quot;127.0.0.1&amp;quot;,34567);
ev_io io;
ev_io_init(&amp;amp;io,&amp;amp;do_connected,fd,EV_WRITE);
ev_io_start(reactor,&amp;amp;io);
ev_run(reactor,0);
close(fd);
ev_loop_destroy(reactor);
return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;1-6-3-echo-server-cc&#34;&gt;1.6.3 echo-server.cc&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;\#include &amp;quot;ev.h&amp;quot;
\#include &amp;quot;common.h&amp;quot;
static void do_accept(struct ev_loop* reactor,ev_io* w,int events){
struct sockaddr_in addr;
socklen_t addr_size=sizeof(addr);
int conn=accept(w-&amp;gt;fd,(struct sockaddr*)&amp;amp;addr,&amp;amp;addr_size);
std::string r=common::address_to_string(&amp;amp;addr);
fprintf(stderr,&amp;quot;accept %s\n&amp;quot;,r.c_str());
close(conn);
}
int main(){
struct ev_loop* reactor=ev_loop_new(EVFLAG_AUTO);
int fd=common::new_tcp_server(34567);
ev_io w;
ev_io_init(&amp;amp;w,do_accept,fd,EV_READ);
ev_io_start(reactor,&amp;amp;w);
ev_run(reactor,0);
close(fd);
ev_loop_destroy(reactor);
}
http://wangjunle23.blog.163.com/blog/static/11783817120124308920321/
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>linux socket keepalive</title><link>/language/clang/socket/linux-socket-keepalive/</link><pubDate>Fri, 09 Oct 2020 15:03:47 CST</pubDate><author>rinetd</author><guid>/language/clang/socket/linux-socket-keepalive/</guid><description>&lt;p&gt;// int keepalive = 1; // 开启keepalive属性&lt;br /&gt;
// int keepidle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测&lt;br /&gt;
// int keepinterval = 5; // 探测时发包的时间间隔为5 秒&lt;br /&gt;
// int keepcount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.&lt;/p&gt;
&lt;p&gt;// setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void &lt;em&gt;)&amp;amp;keepalive , sizeof(keepalive ));&lt;br /&gt;
// setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void&lt;/em&gt;)&amp;amp;keepidle , sizeof(keepidle ));&lt;br /&gt;
// setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&amp;amp;keepinterval , sizeof(keepinterval ));&lt;br /&gt;
// setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&amp;amp;keepcount , sizeof(keepcount ));&lt;/p&gt;</description></item><item><title>linux clib gear lib</title><link>/language/clang/linux-clib-gear-lib/</link><pubDate>Wed, 07 Oct 2020 15:57:45 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-clib-gear-lib/</guid><description>
&lt;h2 id=&#34;macos-下的交叉编译&#34;&gt;Macos 下的交叉编译&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;修改 build的目录下的&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;修改Makefile&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;# ARCH ?= linux
CROSS_PREFIX ?=arm-none-linux-gnueabi-
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;###&lt;/p&gt;</description></item><item><title>hafun 4g hefangyuan socket</title><link>/hardware/hafun-4g-hefangyuan-socket/</link><pubDate>Wed, 30 Sep 2020 10:29:07 CST</pubDate><author>rinetd</author><guid>/hardware/hafun-4g-hefangyuan-socket/</guid><description>
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;typedef enum
{
SOCK_ERR_EAI = -9,
SOCK_INVAL_PARA = -8,
SOCK_ERROR = -7,
SOCK_PEER_NOT_REACHABLE = -6, // 连接失败，连接不可达
SOCK_PEER_NO_RESPONSE = -5,
SOCK_CNT_RESET_BY_PEER = -4, // disconnect 断开连接
SOCK_UNKNOWN_APP = -3,
SOCK_BUSY = -2,
SOCK_NO_MEMORY = -1,
SOCK_SUCCESS = 0, // connect 连接成功
SOCK_ERR_CONN_PENDING = 1,
} sock_result_enum;
static uint8 g_conn_id = 1;
int _on_connect(sock_result_enum result)
{
// 创建和断开连接的时候都会回调这个函数，根据不同的网络状态，result的值不同
// 一般在开始创建网络的时候，这时候网络还不通，result=-6 ，当创建网络成功后 result=0 ，当网络断开时 result=-4
// SOCK_PEER_NOT_REACHABLE = -6, // 连接失败，连接不可达
// SOCK_CNT_RESET_BY_PEER = -4, // disconnect 断开连接
// SOCK_SUCCESS = 0, // connect 连接成功
printf(&amp;quot;%s %d \n&amp;quot;,__func__,result);
return SOCK_SUCCESS;
}
int _on_recv(const uint8 *data, int len)
{
printf(&amp;quot;%s %s %d\n&amp;quot;,__func__,data,len);
SIO_WRITE_ATCORE(data,len,1);
return SOCK_SUCCESS;
}
int _on_send(sock_result_enum result, int len)
{
printf(&amp;quot;%s %d %d \n&amp;quot;,__func__,result,len);
return SOCK_SUCCESS;
}
int _on_close(sock_result_enum result)
{
printf(&amp;quot;%s %d\n&amp;quot;,__func__,result);
return SOCK_SUCCESS;
}
int _send_keepalive_probe(uint8 idx)
{
// idx 返回的是连接号
struct timespec sys_time = {0, 0};
clock_gettime(CLOCK_REALTIME, &amp;amp;sys_time);
// g_sys_second = sys_time.tv_sec;
uint8 * data = (uint8*)&amp;quot;ping\n&amp;quot;;
sock_cont_put_tail(idx,data,strlen((char *)data));
printf(&amp;quot;%s %d %d\n&amp;quot;,__func__,idx,sys_time.tv_sec);
return SOCK_SUCCESS;
}
// static int _cam_send_keepalive_probe(uint8 idx)
// {
// if (m_sock_cb_cam.conn_id != idx)
// return SOCK_ERROR;
// _cam_sock_send_resp(CS_CODE_SUCCESS, CAM_SOCK_CMD_KEEPALIVE_REQ, NULL, 0);
// return SOCK_SUCCESS;
// }
static sock_event_callback_struct m_socket_event_cb = {
_on_connect, _on_recv, _on_send, _on_close, _send_keepalive_probe};
void socket_callback_init()
{
m_socket_event_cb.conn_id = g_conn_id;
int id = sock_cont_init(
g_conn_id,
SOCK_TCP,
&amp;quot;47.104.136.74&amp;quot;,
4000, // 端口
1, // keepalive 是否保持长链接
1, // AT命令调用1,还是函数调用0, 当参数为1 时从AT指令中可以查看其状态。
&amp;amp;m_socket_event_cb);
char data[100];
sprintf(data,&amp;quot;tcp sock_cont_init:%d\n&amp;quot;, id);
SIO_WRITE_ATCORE(data,100,1);
}
int get_socket_id()
{
return g_conn_id;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;当不使用-keepalive-错误-soc-ignore-pack-for-invalid-connect&#34;&gt;当不使用 keepalive 错误【SOC: Ignore pack for invalid connect】&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;当不使用 keepalive时 可能因为网络的问题连接创建失败，这是要自己去创建连接
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;1-connect-fail&#34;&gt;1,CONNECT FAIL&lt;/h3&gt;
&lt;p&gt;如何配置 自动重连的时间？&lt;br /&gt;
配置重连间隔参数：&lt;br /&gt;
设置命令 AT+CIPRECONN=&lt;reboot_timeout&gt;,&lt;wait_time&gt;&lt;br /&gt;
&lt;reboot_timeout&gt; 0,1,2 断线重连间隔次数&lt;br /&gt;
&lt;wait_time&gt; [0],0-3600000单位：秒 等待间隔时间再发起重连&lt;/p&gt;
&lt;h3 id=&#34;休眠-多次释放休眠锁&#34;&gt;【休眠】 多次释放休眠锁&lt;/h3&gt;
&lt;p&gt;int m_sleep_handler 是个文件描述符， 所以多次执行 acquire release 都没关系，以最后一次为准&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; // 1. 获取休眠锁，防止系统进入休眠
sleep_acquire(m_sleep_handler);
// 3. 释放休眠锁
sleep_release(m_sleep_handler);
sleep_release(m_sleep_handler);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A: 多次释放休眠锁，不影响系统运行&lt;/p&gt;
&lt;p&gt;B: 不释放休眠锁，等待强制休眠&lt;/p&gt;</description></item><item><title>linux clock_settime</title><link>/language/clang/linux-clock_settime/</link><pubDate>Sun, 27 Sep 2020 17:40:07 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-clock_settime/</guid><description>
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
int main ()
{
// 获取时间戳
time_t seconds = time(NULL); //The function time(NULL) returns the time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
sprintf(timestamp, &amp;quot;%-llu&amp;quot;, (unsigned long long int)seconds);
time_t start_t, end_t;
double diff_t;
printf(&amp;quot;程序启动...\n&amp;quot;);
time(&amp;amp;start_t);
printf(&amp;quot;休眠 5 秒...\n&amp;quot;);
sleep(5);
time(&amp;amp;end_t);
diff_t = difftime(end_t, start_t);
printf(&amp;quot;执行时间 = %f\n&amp;quot;, diff_t);
printf(&amp;quot;程序退出...\n&amp;quot;);
return(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;方法一-long-clock-t&#34;&gt;方法一： long clock_t&lt;/h3&gt;
&lt;p&gt;#include&lt;time.h&gt;&lt;br /&gt;
&lt;code&gt;clock_t start = clock();&lt;/code&gt;&lt;br /&gt;
需要计时的代码段&lt;br /&gt;
&lt;code&gt;clock_t end = clock();&lt;/code&gt;&lt;br /&gt;
运行时间t ＝ end - start; (单位ms)&lt;/p&gt;
&lt;p&gt;本方法有一定缺陷，在32bit机器上，运行时间较长达到（超过1小时），有可能出现计时错误。&lt;br /&gt;
clock()文档说明如下：&lt;br /&gt;
Note that the time can wrap around. On a 32-bit system where CLOCKS_PER_SEC equals 1000000 this function will return the same value approximately every 72 minutes.&lt;/p&gt;
&lt;p&gt;因此，当出现计时为负数或者很小的数时，需要人为凭经验加上若干个72minutes。。。&lt;/p&gt;
&lt;h3 id=&#34;方法二-timeval-gettimeofday&#34;&gt;方法二： timeval gettimeofday&lt;/h3&gt;
&lt;p&gt;显然方法一有一定局限性，方法二解决了长时间计时的问题。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;time.h&amp;gt;
timeval start, end;
gettimeofday(&amp;amp;start, null);
...
gettimeofday(&amp;amp;end, null);
diffTime(start,end) * 0.001
struct timeval tv_begin;
struct timeval tv_end;
int passed_milliseconds;
gettimeofday (&amp;amp;tv_begin, NULL);
temp = quickSort (temp);
gettimeofday (&amp;amp;tv_end, NULL);
passed_milliseconds = (tv_end.tv_sec - tv_begin.tv_sec) * 1000 + (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
printf(&amp;quot;master_load_file %.3f s \n&amp;quot;, diffTime(start,end) * 0.001 );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行时间 t ＝ 1000*(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec)/1000; （单位ms）&lt;/p&gt;
&lt;h4 id=&#34;时间函数介绍&#34;&gt;时间函数介绍&lt;/h4&gt;
&lt;p&gt;Linux c/c++中提供了很多操作时间的库函数，这里简要介绍。&lt;/p&gt;
&lt;p&gt;常用的时间函数包括以下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;time()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原型：&lt;code&gt;#include &amp;lt;time.h&amp;gt;&lt;/code&gt; &lt;code&gt;time_t time(time_t *t);&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;返回从公元1970-1-1 0:0:0的UTC时间到现在所经过的秒数&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;如果t 并非空指针的话，此函数也会将返回值存到t指针所指的内存&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;成功则返回秒数，失败则返回((time_t)-1)值，错误原因存于errno中&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;用time()函数结合其他函数（如：localtime、gmtime、asctime、ctime）可以获得当前系统时间或是标准时间。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;如果需要更高的时间精确度，就需要struct timespec 和 struct timeval来处理：&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;localtime_r()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原型：&lt;code&gt;struct tm *localtime_r(const time_t * t);&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果由结构tm返回&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;成功则返回0，失败返回－1，错误代码存于errno&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;结构tm的定义如下，此函数返回的时间日期已经转换成当地时区&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;gettimeofday&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;原型：&lt;code&gt;int gettimeofday ( struct timeval * tv , struct timezone * tz )&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;把目前的时间由tv所指的结构返回，当地时区的信息则放到tz所指的结构中&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;timeval结构定义为：&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;struct timeval {
long tv_sec; /*秒*/
long tv_usec; /*微秒*/
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;timezone 结构定义为:&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;struct timezone {
int tz_minuteswest; /*和Greenwich 时间差了多少分钟*/
int tz_dsttime; /*日光节约时间的状态*/
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;clock_settime&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;系统调用可以设置系统时间秒数与纳秒数。&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;返回值。0成功，-1失败&lt;br /&gt;
```cpp&lt;br /&gt;
#include &lt;time.h&gt;&lt;br /&gt;
int clock_settime(clockid_t clk_id, const struct timespec *tp);&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;clockid_t clk_id 用于指定计时时钟的类型，有以下4种：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;struct timespec *tp 用来存储当前的时间，其结构如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct timespec
{
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;```&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ctime&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原型：&lt;code&gt;char *ctime(const time_t *timep);&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果以字符串形态返回&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;此函数已经由时区转换成当地时间，字符串格式为&lt;code&gt;Wed Jun 30 21 :49 :08 1993&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;asctime&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原型：&lt;code&gt;char * asctime(const struct tm * timeptr);&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;将参数timeptr所指的tm结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果以字符串形态返回&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;此函数已经由时区转换成当地时间，字符串格式为: &lt;code&gt;Wed Jun 30 21:49:08 1993\n&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;若再调用相关的时间日期函数，此字符串可能会被破坏。此函数与ctime不同处在于传入的参数是不同的结构&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gmtime&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原型：&lt;code&gt;struct tm* gmtime(const time_t*timep);&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果由结构tm返回&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;settimeofday&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原型：&lt;code&gt;int settimeofday ( const struct timeval *tv,const struct timezone *tz);&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;把目前时间设成由tv所指的结构信息，当地时区信息则设成tz所指的结构&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;注意，只有root权限才能使用此函数修改时间&lt;br /&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多详细信息请使用man手册。&lt;/p&gt;
&lt;p&gt;struct tm ----&amp;gt; asctime() ----&amp;gt; string&lt;br /&gt;
time_t ----&amp;gt; ctime() ----&amp;gt; string&lt;/p&gt;
&lt;p&gt;struct tm ---&amp;gt; strftime() ----&amp;gt; formatted string&lt;br /&gt;
time_t ----&amp;gt; gmtime() / localtime() ----&amp;gt; struct tm&lt;/p&gt;
&lt;h4 id=&#34;获取精确到毫秒的时间&#34;&gt;获取精确到毫秒的时间&lt;/h4&gt;
&lt;hr /&gt;
&lt;p&gt;可以结合&lt;code&gt;time, localtime, strftime&lt;/code&gt;得到本地时间，精确到秒。代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;static string CurrentLocalTime(void)
{
time_t t; //秒时间
tm *local; //本地时间
char buf[128] = {0};
t = time(NULL); //获取目前秒时间
local = localtime(&amp;amp;t); //转为本地时间，注意，该函数非线程安全，下面的例子会使用线程安全函数localtime_r
strftime(buf, 64, &amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, local); //根据需要自定义格式
return buf;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要想得到精确到毫秒的时间，就需要使用&lt;code&gt;gettimeofday&lt;/code&gt;了。代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;/**
* @name: GetLocalTimeWithMs
* @msg: 获取本地时间，精确到毫秒
* @param {type}
* @return: string字符串，格式为YYYYMMDDHHMMSSsss，如：20190710130510368
*/
static string GetLocalTimeWithMs(void)
{
string defaultTime = &amp;quot;19700101000000000&amp;quot;;
try
{
struct timeval curTime;
gettimeofday(&amp;amp;curTime, NULL);
int milli = curTime.tv_usec / 1000;
char buffer[80] = {0};
struct tm nowTime;
localtime_r(&amp;amp;curTime.tv_sec, &amp;amp;nowTime);//把得到的值存入临时分配的内存中，线程安全
strftime(buffer, sizeof(buffer), &amp;quot;%Y%m%d%H%M%S&amp;quot;, &amp;amp;nowTime);
char currentTime[84] = {0};
snprintf(currentTime, sizeof(currentTime), &amp;quot;%s%03d&amp;quot;, buffer, milli);
return currentTime;
}
catch(const std::exception&amp;amp; e)
{
return defaultTime;
}
catch (...)
{
return defaultTime;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到字符串表示的本地时间信息后，可根据需要对字符串进行操作，简单方便。&lt;/p&gt;
&lt;p&gt;本地时间的获取在各种程序中使用率较高，可以放在项目的util中，供所有代码使用。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;void test_clock_settime()
{
int rc;
struct timespec tp, request = { 1, 0 }, remain;
rc = clock_gettime(CLOCK_REALTIME, &amp;amp;tp);
assert(rc == 0);
printf(&amp;quot;[%10&amp;quot;PRId64&amp;quot;.%09d] clock_gettime (CLOCK_REALTIME)\n&amp;quot;, (__int64) tp.tv_sec, (int) tp.tv_nsec);
rc = clock_settime(CLOCK_MONOTONIC, &amp;amp;tp);
assert(rc == -1 &amp;amp;&amp;amp; (errno == EINVAL));
rc = clock_settime(CLOCK_PROCESS_CPUTIME_ID, &amp;amp;tp);
assert(rc == -1 &amp;amp;&amp;amp; (errno == EINVAL));
rc = clock_settime(CLOCK_THREAD_CPUTIME_ID, &amp;amp;tp);
assert(rc == -1 &amp;amp;&amp;amp; (errno == EINVAL));
rc = clock_settime(CLOCK_REALTIME, &amp;amp;tp);
assert(rc == 0 || (errno == EPERM));
rc = clock_gettime(CLOCK_REALTIME, &amp;amp;tp);
assert(rc == 0);
printf(&amp;quot;[%10&amp;quot;PRId64&amp;quot;.%09d] clock_gettime (CLOCK_REALTIME)\n&amp;quot;, (__int64) tp.tv_sec, (int) tp.tv_nsec);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;error.h&amp;gt;
int main()
{
struct timespec tpset, tsreset;
if (clock_gettime(CLOCK_REALTIME, &amp;amp;tsreset) != 0) {
perror(&amp;quot;clock_gettime() did not return success\n&amp;quot;);
}
tpset.tv_sec = 0;
tpset.tv_nsec = 0;
clock_settime(CLOCK_REALTIME, &amp;amp;tpset);
perror(&amp;quot;clock_settime&amp;quot;);
}
// 直接的结果会在调用clock_settime的时候提示非法参数，原因如下：
// clock_settime-&amp;gt;kc-&amp;gt;clock_set(which_clock, &amp;amp;new_tp)-&amp;gt;posix_clock_realtime_set-&amp;gt;do_sys_settimeofday-&amp;gt;do_sys_settimeofday64-&amp;gt;do_settimeofday64
int do_settimeofday64(const struct timespec64 *ts)
{
struct timekeeper *tk = &amp;amp;tk_core.timekeeper;
struct timespec64 ts_delta, xt;
unsigned long flags;
int ret = 0;
if (!timespec64_valid_strict(ts))
return -EINVAL;
raw_spin_lock_irqsave(&amp;amp;timekeeper_lock, flags);
write_seqcount_begin(&amp;amp;tk_core.seq);
timekeeping_forward_now(tk);
xt = tk_xtime(tk);
ts_delta.tv_sec = ts-&amp;gt;tv_sec - xt.tv_sec;
ts_delta.tv_nsec = ts-&amp;gt;tv_nsec - xt.tv_nsec;
// 这里就换返回failed，从这里看到调用clock_settime 是设置和当前时间的差值，像上面的case，如果tpset.tv_sec = 0 和 tpset.tv_nsec = 0 都是零的话，这个差值会非常大，例如：clock_gettime(CLOCK_REALTIME, {1498723813, 202715700})
//因此在调用timespec64_compare时候就提示failed
if (timespec64_compare(&amp;amp;tk-&amp;gt;wall_to_monotonic, &amp;amp;ts_delta) &amp;gt; 0) {
ret = -EINVAL;
goto out;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#define _XOPEN_SOURCE
#define _POSIX_C_SOURCE 199309L
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;errno.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/stat.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/rtc.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;sys/time.h&amp;gt;
/*
time_t : type long int ,用来存储从1970到现在过了多少秒；
更精确一点，可以使用 struct timeval
struct timeval{
long tv_sec; //秒
long tv_usec; //微秒
}
struct timeval有两个成员，一个是秒，一个是微秒, 所以最高精确度是微秒。
一般由函数int gettimeofday(struct timeval *tv, struct timezone *tz)获取系统的时间
struct timespec {
time_t tv_sec; // seconds
long tv_nsec; // and nanoseconds
};
struct timespec有两个成员，一个是秒，一个是纳秒, 所以最高精确度是纳秒。
eg:
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&amp;amp;ts);
CLOCK_REALTIME 统当前时间，从1970年1.1日算起
CLOCK_MONOTONIC 系统的启动时间，不能被设置
CLOCK_PROCESS_CPUTIME_ID 本进程运行时间
CLOCK_THREAD_CPUTIME_ID 本线程运行时间
*/
/*
int clock_gettime(clockid_t clk_id, struct timespec *tp);
Return 0 on success, or –1 on error
clock_gettime ——&amp;gt;struct timespec s ——&amp;gt; struct tm ——&amp;gt;format string
*/
void nowtime_ns()
{
struct timespec ts;
struct tm tm_time;
time_t t;
printf(&amp;quot;---------------------------struct timespec---------------------------------------\n&amp;quot;);
printf(&amp;quot;[time(NULL)] : %ld\n&amp;quot;, time(NULL));
if(clock_gettime(CLOCK_MONOTONIC,&amp;amp;ts)&amp;lt;0)
{
printf(&amp;quot;clock_gettime failed \n&amp;quot;);
perror(&amp;quot;clock_gettime&amp;quot;);
return;
}
printf(&amp;quot;clock_gettime : tv_sec=%ld, tv_nsec=%ld\n&amp;quot;, ts.tv_sec, ts.tv_nsec);
localtime_r(&amp;amp;(ts.tv_sec),&amp;amp;tm_time);
char date_time[50]={0};
strftime(date_time,sizeof(date_time),&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,&amp;amp;tm_time);
printf(&amp;quot;clock_gettime : date_time=%s, tv_nsec=%ld\n&amp;quot;, date_time, ts.tv_nsec);
}
/*
gettimeofday ——&amp;gt;struct timeval s ——&amp;gt;struct tm ——&amp;gt;format string
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);
gettimeofday() and settimeofday() return 0 for success, or -1 for failure (in which case errno is set appropriately).
*/
void nowtime_us()
{
struct timeval tv;
struct tm tm_time;
printf(&amp;quot;---------------------------struct timeval----------------------------------------\n&amp;quot;);
printf(&amp;quot;[time(NULL)] : %ld\n&amp;quot;, time(NULL));
if(gettimeofday(&amp;amp;tv,NULL)&amp;lt;0)
{
perror(&amp;quot;gettimeofday&amp;quot;);
return;
}
printf(&amp;quot;gettimeofday: tv_sec=%ld, tv_usec=%ld\n&amp;quot;, tv.tv_sec, tv.tv_usec);
localtime_r(&amp;amp;(tv.tv_sec),&amp;amp;tm_time);
char date_time[50]={0};
strftime(date_time,sizeof(date_time),&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,&amp;amp;tm_time);
printf(&amp;quot;clock_gettime : date_time=%s, tv_usec=%ld\n&amp;quot;, date_time, tv.tv_usec);
}
int main()
{
nowtime_ns();
printf(&amp;quot;\n&amp;quot;);
nowtime_us();
printf(&amp;quot;\n&amp;quot;);
return 0;
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>linux hlist_head</title><link>/language/clang/linux-hlist_head/</link><pubDate>Fri, 25 Sep 2020 22:11:14 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-hlist_head/</guid><description>&lt;pre&gt;&lt;code class=&#34;language-cmake&#34;&gt;CC = gcc
CFLAGXX := -Wall -g3
CFLAGXX += -fPIC
INCDIR := . -I ../inc
BINDIR := ../bin
LIBDIR := ../lib
LIB := libhlist.so
TARGET = hlist_demo
all: $(TARGET)
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(SOURCE)))
LIBOBJS = $(filter-out rbtreedemo.o, $(OBJS))
%.o: %.c
$(CC) $(CFLAGXX) -I $(INCDIR) -c $^ -o $@
%.o: %.cpp
$(CC) $(CFLAGXX) -I $(INCDIR) -c $^ -o $@
$(TARGET): $(OBJS)
@mkdir -p $(BINDIR)
$(CC) $^ -o $(shell pwd)/$(BINDIR)/$(TARGET)
# $(CC) -fPIC -shared $(LIBOBJS) -L $(LIBDIR) -lrbtree -o $(shell pwd)/$(LIBDIR)/$(LIB)
.PHONY:
clean:
@echo Removing binary and object files ...
-rm -f *.o $(TARGET)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;quot;list.h&amp;quot;
#define PAIR_TABLE_BITS 4
#define PAIR_TABLE_SIZE (1 &amp;lt;&amp;lt; PAIR_TABLE_BITS)
#define PAIR_TABLE_MASK (PAIR_TABLE_SIZE - 1)
static struct hlist_head *pair_map;
struct my_pair {
int key;
int value;
struct hlist_node hlist;
};
static int hash_func(struct my_pair *obj) {
int hashkey = obj-&amp;gt;key &amp;amp; PAIR_TABLE_MASK;
/* hashkey = 2; */
return hashkey;
}
void dump_entry(struct my_pair *obj) {
printf(&amp;quot;key: %-15d value: %-15d\n&amp;quot;, obj-&amp;gt;key, obj-&amp;gt;value);
}
void dump_hash_table(struct hlist_head *map) {
int i;
struct my_pair *pos;
for (i = 0; i &amp;lt; PAIR_TABLE_SIZE; ++i) {
hlist_for_each_entry(pos, &amp;amp;map[i], hlist) {
printf(&amp;quot;[bucket %2d]: &amp;quot;, i);
dump_entry(pos);
}
}
}
void flush_hash_table(struct hlist_head *map) {
int i;
struct my_pair *pos;
for (i = 0; i &amp;lt; PAIR_TABLE_SIZE; ++i) {
hlist_for_each_entry(pos, &amp;amp;map[i], hlist) {
hlist_del(pos);
/* free(pos); */
}
}
}
int main(int argc, char *argv[])
{
int i;
srand(time(NULL));
pair_map = (struct hlist_head *)malloc(sizeof(struct hlist_head) * PAIR_TABLE_SIZE);
/* init hash table */
for (i = 0; i &amp;lt; PAIR_TABLE_SIZE; ++i) {
INIT_HLIST_HEAD(&amp;amp;pair_map[i]);
}
struct my_pair *pair;
int hashkey = 0;
for (i = 0; i &amp;lt; 5; ++i) {
pair = (struct my_pair*) malloc(sizeof(struct my_pair));
pair-&amp;gt;key = rand();
pair-&amp;gt;value = pair-&amp;gt;key + 1;
/* calc hash key, i.e. the NO. of buckets */
hashkey = hash_func(pair);
/* add entry into hash map */
hlist_add_head(&amp;amp;pair-&amp;gt;hlist, &amp;amp;pair_map[hashkey]);
}
dump_hash_table(pair_map);
/* printf(&amp;quot;Del entry key = %d\n&amp;quot;, ); */
flush_hash_table(pair_map);
/* dump_entry(pair_map); */
return 0;
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>linux list_head</title><link>/language/clang/linux-list_head/</link><pubDate>Fri, 25 Sep 2020 20:23:58 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-list_head/</guid><description>
&lt;p&gt;&lt;a href=&#34;https://blog.csdn.net/wanshilun/article/details/79747710&#34; target=&#34;_blank&#34;&gt;https://blog.csdn.net/wanshilun/article/details/79747710&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;xxx_init() 用来对一个数据结果对象赋初始值&lt;br /&gt;
init_xxx 初始化一个对象&lt;/p&gt;
&lt;h2 id=&#34;一起分析内核最重要的链表list-head&#34;&gt;一起分析内核最重要的链表list_head&lt;/h2&gt;
&lt;h2 id=&#34;一-链表结构&#34;&gt;一、链表结构&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;struct list_head {
struct list_head *next, *prev;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;二-链表初始化函数&#34;&gt;二、链表初始化函数&lt;/h2&gt;
&lt;p&gt;list_head 链表的初始化只是把 *next, *prev连个指针指向链表头，形成双向循环链表；&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#define LIST_HEAD_INIT(name) { &amp;amp;(name), &amp;amp;(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list-&amp;gt;next, list);
list-&amp;gt;prev = list;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面是各种不同的初始化或者定义并初始化链表的函数，我们都可以使用，&lt;br /&gt;
例如：&lt;br /&gt;
1）我们自己定义一个链表头,然后调用函数初始化：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;struct list_head name = LIST_HEAD_INIT(name)；
// INIT_LIST_HEAD（name）;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2）或者使用下面宏定义并初始化一个双向循环链表头；&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;LIST_HEAD(name)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;三-增-删-遍历接口&#34;&gt;三、增、删、遍历接口&lt;/h2&gt;
&lt;p&gt;我们都知道链表是用来保存一下有意义的字段的，但是list_haed结构中并没有其他字段，所以内核使用该链表时，总是把它嵌套到一个宿主结构中来使用。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;struct my_list{
struct list_head mylist;
int val ;
}；
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;3-1-增加节点&#34;&gt;3.1 增加节点&lt;/h2&gt;
&lt;p&gt;内核中提供了添加一个节点的接口，如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;static inline void list_add(struct list_head *new, struct list_head *head)；//首插入
static inline void list_add_tail(struct list_head *new, struct list_head *head)；//尾插入
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;3-2-删除节点&#34;&gt;3.2 删除节点&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;static inline void list_del(struct list_head *entry)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;3-3-遍历链表&#34;&gt;3.3 遍历链表&lt;/h2&gt;
&lt;p&gt;内核是同过下面这个宏定义来完成对list_head链表进行遍历的，如下 :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#define list_for_each(pos, head) for (pos = (head)-&amp;gt;next; pos != (head); pos = pos-&amp;gt;next`) //从前往后
#define list_for_each_prev(pos, head) for (pos = (head)-&amp;gt;prev; pos != (head); pos = pos-&amp;gt;prev) //从后往前
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;3-4-定位宿主&#34;&gt;3.4 定位宿主&lt;/h2&gt;
&lt;p&gt;上面的所有操作都是基于list_head这个链表进行的，涉及的结构体也都是该结构体，但是我们知道，这个结构体是嵌入到别的宿主中使用的，所以我们应该定位到该结构体的宿主位置,内核中有定义宏去直接得到宿主的地址,如下：&lt;br /&gt;
&lt;strong&gt;作用：已知某结构体的成员member和指向该成员的指针ptr(也就是member的地址)，算出该结构体的起始地址。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#define container_of(ptr,type,member)
{const typeof(((type*)0)-&amp;gt;member)* _myptr=(ptr);
(type*)((char*)_myptr-offsetof(type,member));})
#define offsetof(TYPE, MEMBER) ((size_t) &amp;amp;((TYPE*)0)-&amp;gt;MEMBER)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们分析上面这个宏，发现有好些都不认识，这里一个一个分析：&lt;br /&gt;
( (TYPE *)0 ) 将零转型为TYPE类型指针;&lt;br /&gt;
((TYPE *)0)-&amp;gt;MEMBER 访问结构中的数据成员;&lt;br /&gt;
&amp;amp;( ( (TYPE _)0 )-&amp;gt;MEMBER )取出数据成员的地址;，因为是从0开始的，所以就相当于该成员在结构体中的偏移量。&lt;br /&gt;
(size&lt;em&gt;t)(&amp;amp;(((TYPE&lt;/em&gt;)0)-&amp;gt;MEMBER))结果转换类型；&lt;br /&gt;
typeof为C语言的关键字，用来由变量得到变量的类型，eg；typeof(a)得到变量a的类型。&lt;/p&gt;
&lt;p&gt;经过上面分析可以看出来，其实现就是通过mem成员的地址减去mem的偏移量，来得到首地址，即宿主的地址。&lt;/p&gt;
&lt;h2 id=&#34;3-5-宿主结构的遍历&#34;&gt;3.5 宿主结构的遍历&lt;/h2&gt;
&lt;p&gt;宿主结构的遍历，它们的实现都是利用list_entry宏。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#define list_entry((ptr)-&amp;gt;next, type, member) container_of(ptr,type,member)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)-&amp;gt;next, typeof(*pos), member); \
&amp;amp;pos-&amp;gt;member != (head); \
pos = list_entry(pos-&amp;gt;member.next, typeof(*pos), member))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;quot;list.h&amp;quot; /*由于我的机器上没有list.h,所以我拷贝了一个，如果你机器上有，应该是加#include &amp;lt;linux/list.h&amp;gt;*/
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#define MAX_USER_LEN 32
#define MAX_PAS_LEN 32
#define MAX_SERVER_LEN 1024
typedef struct server_detect_ftp
{
struct list_head list;
char server[MAX_SERVER_LEN];
int port;
char username[MAX_USER_LEN];
char password[MAX_PAS_LEN];
}server_detect_ftp_t;
int main(void)
{
struct list_head head;//头部
server_detect_ftp_t ftp_link;
server_detect_ftp_t ftp_link1;
server_detect_ftp_t *entry;
struct list_head *p;
INIT_LIST_HEAD(&amp;amp;head);//初始化头部
strcpy(ftp_link.server,&amp;quot;www.163.com&amp;quot;);
ftp_link.port=34;
strcpy(ftp_link.username,&amp;quot;good&amp;quot;);
strcpy(ftp_link.password,&amp;quot;good&amp;quot;);
strcpy(ftp_link1.server,&amp;quot;www.163.com&amp;quot;);
ftp_link1.port=34;
strcpy(ftp_link1.username,&amp;quot;good&amp;quot;);
strcpy(ftp_link1.password,&amp;quot;good&amp;quot;);
INIT_LIST_HEAD(&amp;amp;head);
list_add(&amp;amp;ftp_link.list,&amp;amp;head);
list_add(&amp;amp;ftp_link1.list,&amp;amp;head);//添加链表
list_del(&amp;amp;ftp_link1.list);//删除链表
list_for_each(p,&amp;amp;head)//遍历
{
entry=list_entry(p,struct server_detect_ftp,list);//读取某个值
printf(&amp;quot;%s\n&amp;quot;,entry-&amp;gt;username);
}
return 0;
}
#include &amp;quot;list.h&amp;quot;
#include &amp;quot;time.h&amp;quot;
struct rtc_alarm_dev
{
time_t expect;
int repeat;
volatile int interval;
void *(*func)(void *);
struct list_head list;
} rtc_alarm_dev_t;
struct list_head rtc_alarm_list;
// #define _DEBUG
int rtc_alarm_isActive(struct rtc_alarm_dev *timer);
int rtc_alarm_add(struct rtc_alarm_dev *timer)
{
struct list_head *plist;
struct rtc_alarm_dev *dev;
if (rtc_alarm_isActive(timer))
rtc_alarm_del(timer);
if (list_empty(&amp;amp;rtc_alarm_list))
{
list_add(&amp;amp;timer-&amp;gt;list, &amp;amp;rtc_alarm_list);
}
else
{
list_for_each(plist, &amp;amp;rtc_alarm_list)
{
dev = list_entry(plist, struct rtc_alarm_dev, list);
if (dev-&amp;gt;expect &amp;gt; timer-&amp;gt;expect)
{
list_add(&amp;amp;timer-&amp;gt;list, dev-&amp;gt;list.prev);
break;
}
}
if (plist == &amp;amp;rtc_alarm_list)
list_add(&amp;amp;timer-&amp;gt;list, &amp;amp;dev-&amp;gt;list);
}
#ifdef _DEBUG
list_for_each(plist, &amp;amp;rtc_alarm_list)
{
printf(&amp;quot;%s debug: dev-&amp;gt;expect = %d, dev = %08x list: %p %p\n&amp;quot;, __func__, dev-&amp;gt;expect, dev, plist, rtc_alarm_list);
dev = list_entry(plist, struct rtc_alarm_dev, list);
printf(&amp;quot;%s debug: dev-&amp;gt;expect = %d, dev = %08x list: %p\n&amp;quot;, __func__, dev-&amp;gt;expect, dev, plist);
}
// printf(&amp;quot;%s debug: dev-&amp;gt;expect = %d, dev = %08x list: %p\n&amp;quot;, __func__, dev-&amp;gt;expect, dev,plist);
#endif
// rtc_alarm_update();
return 0;
}
int rtc_alarm_isActive(struct rtc_alarm_dev *timer)
{
struct list_head *plist;
struct rtc_alarm_dev *dev;
int active = 0;
list_for_each(plist, &amp;amp;rtc_alarm_list)
{
dev = list_entry(plist, struct rtc_alarm_dev, list);
if (dev == timer)
{
active = 1;
break;
}
}
return active;
}
int rtc_alarm_del(struct rtc_alarm_dev *timer)
{
struct list_head *plist;
struct rtc_alarm_dev *dev;
list_for_each(plist, &amp;amp;rtc_alarm_list)
{
dev = list_entry(plist, struct rtc_alarm_dev, list);
if (dev == timer)
{
plist = plist-&amp;gt;prev;
list_del(&amp;amp;dev-&amp;gt;list);
break;
}
}
return 0;
}
int main()
{
INIT_LIST_HEAD(&amp;amp;rtc_alarm_list);
struct rtc_alarm_dev sample_dev_1;
memset(&amp;amp;sample_dev_1, 0, sizeof(struct rtc_alarm_dev));
sample_dev_1.func = NULL;
sample_dev_1.repeat = 1;
sample_dev_1.interval = 1 * 60; /* Sampling Cycle */
// now = rtc_get_time();
// tm = gmtime(&amp;amp;now);
// expect = now - tm-&amp;gt;tm_sec + (2 - tm-&amp;gt;tm_min % 2) * 60;
// expect = now + 10;
// tm = gmtime(&amp;amp;expect);
// logcat(&amp;quot;TGQingXie Expect: %s&amp;quot;, asctime(tm));
sample_dev_1.expect = 10;
rtc_alarm_add(&amp;amp;sample_dev_1);
struct rtc_alarm_dev sample_dev_2;
sample_dev_2.expect = 15;
rtc_alarm_add(&amp;amp;sample_dev_2);
struct list_head *plist;
struct rtc_alarm_dev *dev;
list_for_each(plist, &amp;amp;rtc_alarm_list)
{
dev = list_entry(plist, struct rtc_alarm_dev, list);
printf(&amp;quot;%s debug: dev-&amp;gt;expect = %d, dev = %08x list: %p %p\n&amp;quot;, __func__, dev-&amp;gt;expect, dev, plist, &amp;amp;rtc_alarm_list);
}
printf(&amp;quot;%s debug: dev-&amp;gt;expect = %d, dev = %08x list: %p %p\n&amp;quot;, __func__, dev-&amp;gt;expect, dev, plist, &amp;amp;rtc_alarm_list);
// main debug: dev-&amp;gt;expect = 10, dev = e439c2b0 list: 0x7ffee439c2c8 0x10b864040
// main debug: dev-&amp;gt;expect = 15, dev = e439c288 list: 0x7ffee439c2a0 0x10b864040
// main debug: dev-&amp;gt;expect = 15, dev = e439c288 list: 0x10b864040 0x10b864040
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>linux cross libuv</title><link>/language/clang/linux-cross-libuv/</link><pubDate>Mon, 21 Sep 2020 18:01:09 CST</pubDate><author>rinetd</author><guid>/language/clang/linux-cross-libuv/</guid><description>&lt;p&gt;交叉编译&lt;/p&gt;
&lt;p&gt;git clone &lt;a href=&#34;https://github.com/libuv/libuv&#34; target=&#34;_blank&#34;&gt;https://github.com/libuv/libuv&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;sh autogen.sh
$ ./configure --prefix=/tmp/libuv_prefix --host=arm-none-linux-gnueabi CC=arm-none-linux-gnueabi-gcc
$ make
$ make check
$ make install
2.交叉编译libuv
./autogen.sh
CC=/opt/embedded/tools/bin/arm-linux-gnueabihf-gcc
CFLAGS=-I/opt/embedded/platform/usr/include
LDFLAGS=-L/opt/embedded/platform/usr/lib
LIBS=-ludev
./configure --prefix=/opt/embedded/platform/usr/ --host=arm-linux
make install
&lt;/code&gt;&lt;/pre&gt;</description></item></channel></rss>