linux下使用hiredis异步API实现sub/pub消息订阅和发布的功能

2023-05-16

本文转载自链接:
http://blog.csdn.net/chenzba/article/details/51224715

最近使用redis的c接口——hiredis,使客户端与redis服务器通信,实现消息订阅和发布(PUB/SUB)的功能,我把遇到的一些问题和解决方法列出来供大家学习。
       废话不多说,先贴代码。
redis_publisher.h
/*************************************************************************
    > File Name: redis_publisher.h
    > Author: chenzengba
    > Mail: chenzengba@gmail.com 
    > Created Time: Sat 23 Apr 2016 10:15:09 PM CST
    > Description: 封装hiredis,实现消息发布给redis功能
 ************************************************************************/

#ifndef REDIS_PUBLISHER_H
#define REDIS_PUBLISHER_H

#include <stdlib.h>
#include <hiredis/async.h>
#include <hiredis/adapters/libevent.h>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <boost/tr1/functional.hpp>

class CRedisPublisher
{
public:    
    CRedisPublisher();
    ~CRedisPublisher();

    bool init();
    bool uninit();
    bool connect();
    bool disconnect();
    
    bool publish(const std::string &channel_name, 
        const std::string &message);

private:
     // 下面三个回调函数供redis服务调用
    // 连接回调
    static void connect_callback(const redisAsyncContext *redis_context,
        int status);
	
	// 断开连接的回调
    static void disconnect_callback(const redisAsyncContext *redis_context,
        int status);

	// 执行命令回调
    static void command_callback(redisAsyncContext *redis_context,
        void *reply, void *privdata);

    // 事件分发线程函数
    static void *event_thread(void *data);
    void *event_proc();

private:
     // libevent事件对象
    event_base *_event_base;
	// 事件线程ID
    pthread_t _event_thread;
	// 事件线程的信号量
    sem_t _event_sem;
	// hiredis异步对象
    redisAsyncContext *_redis_context;
};

#endif
redis_publisher.cpp
/*************************************************************************
    > File Name: redis_publisher.cpp
    > Author: chenzengba
    > Mail: chenzengba@gmail.com 
    > Created Time: Sat 23 Apr 2016 10:15:09 PM CST
    > Description: 
 ************************************************************************/
 
#include <stddef.h>
#include <assert.h>
#include <string.h>
#include "redis_publisher.h"

CRedisPublisher::CRedisPublisher():_event_base(0), _event_thread(0),
_redis_context(0)
{
}

CRedisPublisher::~CRedisPublisher()
{
}

bool CRedisPublisher::init()
{
    // initialize the event
    _event_base = event_base_new();    // 创建libevent对象
    if (NULL == _event_base)
    {
        printf(": Create redis event failed.\n");
        return false;
    }

    memset(&_event_sem, 0, sizeof(_event_sem));
    int ret = sem_init(&_event_sem, 0, 0);
    if (ret != 0)
    {
        printf(": Init sem failed.\n");
        return false;
    }

    return true;
}

bool CRedisPublisher::uninit()
{
    _event_base = NULL;
    
    sem_destroy(&_event_sem);   
    return true;
}

bool CRedisPublisher::connect()
{
    // connect redis
    _redis_context = redisAsyncConnect("127.0.0.1", 6379);    // 异步连接到redis服务器上,使用默认端口
    if (NULL == _redis_context)
    {
        printf(": Connect redis failed.\n");
        return false;
    }

    if (_redis_context->err)
    {
        printf(": Connect redis error: %d, %s\n", 
            _redis_context->err, _redis_context->errstr);    // 输出错误信息
        return false;
    }

    // attach the event
    redisLibeventAttach(_redis_context, _event_base);    // 将事件绑定到redis context上,使设置给redis的回调跟事件关联
    
    // 创建事件处理线程
    int ret = pthread_create(&_event_thread, 0, &CRedisPublisher::event_thread, this);
    if (ret != 0)
    {
        printf(": create event thread failed.\n");
        disconnect();
        return false;
    }

	// 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态
    redisAsyncSetConnectCallback(_redis_context, 
        &CRedisPublisher::connect_callback);

	// 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连
    redisAsyncSetDisconnectCallback(_redis_context,
        &CRedisPublisher::disconnect_callback);

	// 启动事件线程
    sem_post(&_event_sem);
    return true;
}

bool CRedisPublisher::disconnect()
{
    if (_redis_context)
    {
        redisAsyncDisconnect(_redis_context);
        redisAsyncFree(_redis_context);
        _redis_context = NULL;
    }

    return true;
}

bool CRedisPublisher::publish(const std::string &channel_name,
    const std::string &message)
{
    int ret = redisAsyncCommand(_redis_context, 
        &CRedisPublisher::command_callback, this, "PUBLISH %s %s", 
        channel_name.c_str(), message.c_str());
    if (REDIS_ERR == ret)
    {
        printf("Publish command failed: %d\n", ret);
        return false;
    }

    return true;
}

void CRedisPublisher::connect_callback(const redisAsyncContext *redis_context,
    int status)
{
    if (status != REDIS_OK)
    {
        printf(": Error: %s\n", redis_context->errstr);
    }
    else
    {
        printf(": Redis connected!\n");
    }
}

void CRedisPublisher::disconnect_callback(
    const redisAsyncContext *redis_context, int status)
{
    if (status != REDIS_OK)
    {
		// 这里异常退出,可以尝试重连
        printf(": Error: %s\n", redis_context->errstr);
    }
}

// 消息接收回调函数
void CRedisPublisher::command_callback(redisAsyncContext *redis_context,
    void *reply, void *privdata)
{
    printf("command callback.\n");
	// 这里不执行任何操作
}

void *CRedisPublisher::event_thread(void *data)
{
    if (NULL == data)
    {
        printf(": Error!\n");
        assert(false);
        return NULL;
    }

    CRedisPublisher *self_this = reinterpret_cast<CRedisPublisher *>(data);
    return self_this->event_proc();
}

void *CRedisPublisher::event_proc()
{
    sem_wait(&_event_sem);
	
	// 开启事件分发,event_base_dispatch会阻塞
    event_base_dispatch(_event_base);

    return NULL;
}
redis_subscriber.h
/*************************************************************************
    > File Name: redis_subscriber.h
    > Author: chenzengba
    > Mail: chenzengba@gmail.com 
    > Created Time: Sat 23 Apr 2016 10:15:09 PM CST
    > Description: 封装hiredis,实现消息订阅redis功能
 ************************************************************************/

#ifndef REDIS_SUBSCRIBER_H
#define REDIS_SUBSCRIBER_H

#include <stdlib.h>
#include <hiredis/async.h>
#include <hiredis/adapters/libevent.h>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <boost/tr1/functional.hpp>

class CRedisSubscriber
{
public:
    typedef std::tr1::function<void (const char *, const char *, int)>         NotifyMessageFn;	// 回调函数对象类型,当接收到消息后调用回调把消息发送出去
		
    CRedisSubscriber();
    ~CRedisSubscriber();
    
	bool init(const NotifyMessageFn &fn);	// 传入回调对象
    bool uninit();
    bool connect();
    bool disconnect();
    
    // 可以多次调用,订阅多个频道
    bool subscribe(const std::string &channel_name);
	
private:
    // 下面三个回调函数供redis服务调用
    // 连接回调
    static void connect_callback(const redisAsyncContext *redis_context,
        int status);
	
	// 断开连接的回调
    static void disconnect_callback(const redisAsyncContext *redis_context,
        int status);

	// 执行命令回调
    static void command_callback(redisAsyncContext *redis_context,
        void *reply, void *privdata);

    // 事件分发线程函数
    static void *event_thread(void *data);
    void *event_proc();
	
private:
    // libevent事件对象
    event_base *_event_base;
	// 事件线程ID
    pthread_t _event_thread;
	// 事件线程的信号量
    sem_t _event_sem;
	// hiredis异步对象
    redisAsyncContext *_redis_context;
	
	// 通知外层的回调函数对象
    NotifyMessageFn _notify_message_fn;
};

#endif
redis_subscriber.cpp:
/*************************************************************************
    > File Name: redis_subscriber.cpp
    > Author: chenzengba
    > Mail: chenzengba@gmail.com 
    > Created Time: Sat 23 Apr 2016 10:15:09 PM CST
    > Description: 
 ************************************************************************/
 
#include <stddef.h>
#include <assert.h>
#include <string.h>
#include "redis_subscriber.h"

CRedisSubscriber::CRedisSubscriber():_event_base(0), _event_thread(0),
_redis_context(0)
{
}

CRedisSubscriber::~CRedisSubscriber()
{
}

bool CRedisSubscriber::init(const NotifyMessageFn &fn)
{
    // initialize the event
    _notify_message_fn = fn;
    _event_base = event_base_new();    // 创建libevent对象
    if (NULL == _event_base)
    {
        printf(": Create redis event failed.\n");
        return false;
    }

    memset(&_event_sem, 0, sizeof(_event_sem));
    int ret = sem_init(&_event_sem, 0, 0);
    if (ret != 0)
    {
        printf(": Init sem failed.\n");
        return false;
    }

    return true;
}

bool CRedisSubscriber::uninit()
{
    _event_base = NULL;
    
    sem_destroy(&_event_sem);   
    return true;
}

bool CRedisSubscriber::connect()
{
    // connect redis
    _redis_context = redisAsyncConnect("127.0.0.1", 6379);    // 异步连接到redis服务器上,使用默认端口
    if (NULL == _redis_context)
    {
        printf(": Connect redis failed.\n");
        return false;
    }

    if (_redis_context->err)
    {
        printf(": Connect redis error: %d, %s\n", 
            _redis_context->err, _redis_context->errstr);    // 输出错误信息
        return false;
    }

    // attach the event
    redisLibeventAttach(_redis_context, _event_base);    // 将事件绑定到redis context上,使设置给redis的回调跟事件关联
    
    // 创建事件处理线程
    int ret = pthread_create(&_event_thread, 0, &CRedisSubscriber::event_thread, this);
    if (ret != 0)
    {
        printf(": create event thread failed.\n");
        disconnect();
        return false;
    }

	// 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态
    redisAsyncSetConnectCallback(_redis_context, 
        &CRedisSubscriber::connect_callback);

	// 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连
    redisAsyncSetDisconnectCallback(_redis_context,
        &CRedisSubscriber::disconnect_callback);

	// 启动事件线程
    sem_post(&_event_sem);
    return true;
}

bool CRedisSubscriber::disconnect()
{
    if (_redis_context)
    {
        redisAsyncDisconnect(_redis_context);
        redisAsyncFree(_redis_context);
        _redis_context = NULL;
    }

    return true;
}

bool CRedisSubscriber::subscribe(const std::string &channel_name)
{
    int ret = redisAsyncCommand(_redis_context, 
        &CRedisSubscriber::command_callback, this, "SUBSCRIBE %s", 
        channel_name.c_str());
    if (REDIS_ERR == ret)
    {
        printf("Subscribe command failed: %d\n", ret);
        return false;
    }
    
    printf(": Subscribe success: %s\n", channel_name.c_str());
    return true;
}

void CRedisSubscriber::connect_callback(const redisAsyncContext *redis_context,
    int status)
{
    if (status != REDIS_OK)
    {
        printf(": Error: %s\n", redis_context->errstr);
    }
    else
    {
        printf(": Redis connected!");
    }
}

void CRedisSubscriber::disconnect_callback(
    const redisAsyncContext *redis_context, int status)
{
    if (status != REDIS_OK)
    {
		// 这里异常退出,可以尝试重连
        printf(": Error: %s\n", redis_context->errstr);
    }
}

// 消息接收回调函数
void CRedisSubscriber::command_callback(redisAsyncContext *redis_context,
    void *reply, void *privdata)
{
    if (NULL == reply || NULL == privdata) {
        return ;
    }

	// 静态函数中,要使用类的成员变量,把当前的this指针传进来,用this指针间接访问
    CRedisSubscriber *self_this = reinterpret_cast<CRedisSubscriber *>(privdata);
    redisReply *redis_reply = reinterpret_cast<redisReply *>(reply);
	
	// 订阅接收到的消息是一个带三元素的数组
    if (redis_reply->type == REDIS_REPLY_ARRAY &&
    redis_reply->elements == 3)
    {
        printf(": Recieve message:%s:%d:%s:%d:%s:%d\n",
        redis_reply->element[0]->str, redis_reply->element[0]->len,
        redis_reply->element[1]->str, redis_reply->element[1]->len,
        redis_reply->element[2]->str, redis_reply->element[2]->len);
		
		// 调用函数对象把消息通知给外层
        self_this->_notify_message_fn(redis_reply->element[1]->str,
            redis_reply->element[2]->str, redis_reply->element[2]->len);
    }
}

void *CRedisSubscriber::event_thread(void *data)
{
    if (NULL == data)
    {
        printf(": Error!\n");
        assert(false);
        return NULL;
    }

    CRedisSubscriber *self_this = reinterpret_cast<CRedisSubscriber *>(data);
    return self_this->event_proc();
}

void *CRedisSubscriber::event_proc()
{
    sem_wait(&_event_sem);
	
	// 开启事件分发,event_base_dispatch会阻塞
    event_base_dispatch(_event_base);

    return NULL;
}

问题1:hiredis官网没有异步接口的实现例子。

        hiredis提供了几个异步通信的API,一开始根据API名字的理解,我们实现了跟redis服务器建立连接、订阅和发布的功能,可在实际使用的时候,程序并没有像我们预想的那样,除了能够建立连接外,任何事情都没发生。
        网上查了很多资料,原来hiredis的异步实现是通过事件来分发redis发送过来的消息的,hiredis可以使用libae、libev、libuv和libevent中的任何一个实现事件的分发,网上的资料提示使用libae、libev和libuv可能发生其他问题,这里为了方便就选用libevent。 hireds官网并没有对libevent做任何介绍,也没用说明使用异步机制需要引入事件的接口,所以一开始走了很多弯路。
        关于libevent的使用这里就不再赘述,详情可以见libevent官网。
libevent官网:http://libevent.org/
libevent api文档:https://www.monkey.org/~provos/libevent/doxygen-2.0.1/include_2event2_2event_8h.html#6e9827de8c3014417b11b48f2fe688ae
CRedisPublisher和CRedisSubscriber的初始化过程:
初始化事件处理,并获得事件处理的实例:
_event_base = event_base_new();

在获得redisAsyncContext *之后,调用
redisLibeventAttach(_redis_context, _event_base);
这样就将事件处理和redis关联起来,最后在另一个线程调用
event_base_dispatch(_event_base);
启动事件的分发,这是一个阻塞函数,因此,创建了一个新的线程处理事件分发,值得注意的是,这里用信号灯_event_sem控制线程的启动,意在程序调用
    redisAsyncSetConnectCallback(_redis_context, 
        &CRedisSubscriber::connect_callback);
    redisAsyncSetDisconnectCallback(_redis_context,
        &CRedisSubscriber::disconnect_callback);
之后,能够完全捕捉到这两个回调。

问题2 奇特的‘ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context’错误

        有些人会觉得这两个类设计有点冗余,我们发现CRedisPublisher和CRedisSubscriber很多逻辑是一样的,为什么不把他们整合到一起成一个类,既能够发布消息也能够订阅消息。其实一开始我就是这么干的,在使用的时候发现, 用同个redisAsynContex *对象进行消息订阅和发布,与redis服务连接会自动断开,disconnect_callback回调会被调用,并且返回奇怪的错误:ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context,因此,不能使用同个redisAsyncContext *对象实现发布和订阅。这里为了减少设计的复杂性,就将两个类的逻辑分开了。
        当然,你也可以将相同的逻辑抽象到一个基类里,并实现publish和subscribe接口。

问题3 相关依赖的库

        编译之前,需要安装hiredis、libevent和boost库,我是用的是Ubuntu x64系统。
hiredis官网:https://github.com/redis/hiredis
下载源码解压,进入解压目录,执行make && make install命令。
libevent官网:http://libevent.org/下载最新的稳定版
解压后进入解压目录,执行命令
./configure -prefix=/usr
sudo make && make install
boost库:直接执行安装:sudo apt-get install libboost-dev
如果你不是用std::tr1::function的函数对象来给外层通知消息,就不需要boost库。你可以用接口的形式实现回调,把接口传给CRedisSubscribe类,让它在接收到消息后调用接口回调,通知外层。

问题4 如何使用

        最后贴出例子代码。
publisher.cpp,实现发布消息:
/*************************************************************************
    > File Name: publisher.cpp
    > Author: chenzengba
    > Mail: chenzengba@gmail.com 
    > Created Time: Sat 23 Apr 2016 12:13:24 PM CST
 ************************************************************************/

#include "redis_publisher.h"

int main(int argc, char *argv[])
{
    CRedisPublisher publisher;

    bool ret = publisher.init();
    if (!ret) 
    {
        printf("Init failed.\n");
        return 0;
    }

    ret = publisher.connect();
    if (!ret)
    {
        printf("connect failed.");
        return 0;
    }

    while (true)
    {
        publisher.publish("test-channel", "Test message");
        sleep(1);
    }

    publisher.disconnect();
    publisher.uninit();
    return 0;
}

subscriber.cpp实现订阅消息:
/*************************************************************************
    > File Name: subscriber.cpp
    > Author: chenzengba
    > Mail: chenzengba@gmail.com 
    > Created Time: Sat 23 Apr 2016 12:26:42 PM CST
 ************************************************************************/

#include "redis_subscriber.h"

void recieve_message(const char *channel_name,
    const char *message, int len)
{
    printf("Recieve message:\n    channel name: %s\n    message: %s\n",
        channel_name, message);
}

int main(int argc, char *argv[])
{
    CRedisSubscriber subscriber;
    CRedisSubscriber::NotifyMessageFn fn = 
        bind(recieve_message, std::tr1::placeholders::_1,
        std::tr1::placeholders::_2, std::tr1::placeholders::_3);

    bool ret = subscriber.init(fn);
    if (!ret)
    {
        printf("Init failed.\n");
        return 0;
    }

    ret = subscriber.connect();
    if (!ret)
    {
        printf("Connect failed.\n");
        return 0;
    }

    subscriber.subscribe("test-channel");

    while (true)
    {
        sleep(1);
    }

    subscriber.disconnect();
    subscriber.uninit();

    return 0;
}

关于编译的问题:在g++中编译, 注意要加上-lhiredis -levent参数,下面是一个简单的Makefile:
EXE=server_main client_main
CC=g++
FLAG=-lhiredis -levent
OBJ=redis_publisher.o publisher.o redis_subscriber.o subscriber.o

all:$(EXE)

$(EXE):$(OBJ)
	$(CC) -o publisher redis_publisher.o publisher.o $(FLAG)
	$(CC) -o subscriber redis_subscriber.o subscriber.o $(FLAG)

redis_publisher.o:redis_publisher.h
redis_subscriber.o:redis_subscriber.h

publisher.o:publisher.cpp
	$(CC) -c publisher.cpp

subscriber.o:subscriber.cpp
	$(CC) -c subscriber.cpp
clean:
	rm publisher subscriber *.o


致谢:
redis异步API使用libevent:http://www.tuicool.com/articles/N73uuu

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux下使用hiredis异步API实现sub/pub消息订阅和发布的功能 的相关文章

  • 无需超级用户即可在 Linux 中打开 RAW 套接字

    我必须编写一个在 Linux 上运行的 ping 函数 语言是 C 所以 C 也可以 在网上搜索并查看源代码ping命令 事实证明我应该创建一个原始套接字 icmp sock socket AF INET SOCK RAW IPPROTO
  • 在 Win7 登录屏幕上运行应用程序[重复]

    这个问题在这里已经有答案了 我想通过服务在 Windows 7 的登录屏幕上运行应用程序 我对此进行了长期研究并尝试了不同的方法 但不幸的是到目前为止还没有完全成功 我设法在当前登录用户的锁定屏幕上运行该应用程序 起初我认为这就是我基本上试
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • 配置tomat的server.xml文件并自动生成mod_jk.conf

    我在用apache 2 2 15 and tomcat6 6 0 24 on CentOS 6 4并希望使用 tomcat 服务器的功能 通过添加以下内容自动生成 mod jk conf 文件
  • 如何在 Linux 中使用 C 语言使用共享内存

    我的一个项目有点问题 我一直在试图找到一个有据可查的使用共享内存的例子fork 但没有成功 基本上情况是 当用户启动程序时 我需要在共享内存中存储两个值 当前路径这是一个char and a 文件名这也是char 根据命令参数 启动一个新进
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • 绕过 dev/urandom|random 进行测试

    我想编写一个功能测试用例 用已知的随机数值来测试程序 我已经在单元测试期间用模拟对其进行了测试 但我也希望用于功能测试 当然不是全部 最简单的方法是什么 dev urandom仅覆盖一个进程 有没有办法做类似的事情chroot对于单个文件并
  • linux下如何从文本文件中获取值

    我有一些文本格式的文件 xxx conf 我在这个文件中有一些文本 disablelog 1 当我使用 grep r disablelog oscam conf 输出是 disablelog 1 但我只需要值1 请问你有什么想法吗 一种方法
  • Linux/POSIX:为什么 fork() 不分叉*所有*线程

    众所周知 POSIX下创建新进程的默认方式是使用fork 在 Linux 下 这在内部映射到clone 我想知道的是 众所周知 当一个人打电话时fork 子进程是用单个线程创建的 调用的线程fork cf https linux die n
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • 检查已安装的软件包,如果没有找到则安装

    我需要检查已安装的软件包 如果未安装则安装它们 RHEL CentOS Fedora 示例 rpm qa grep glibc static glibc static 2 12 1 80 el6 3 5 i686 如何在 BASH 中进行检
  • 如何wget目录中最新的文件

    我想编写一个 bash 脚本来下载并安装最新的每日构建程序 RStudio 是否有可能使wget仅下载目录中最新的文件http www rstudio org download daily desktop http www rstudio
  • 如何在文件中搜索多行模式?

    我需要找到包含特定字符串模式的所有文件 我想到的第一个解决方案是使用find管道与xargs grep find iname py xargs grep e YOUR PATTERN 但是 如果我需要查找跨越多行的模式 我就会陷入困境 因为
  • 来源和出口有什么区别?

    我正在编写一个 shell 脚本 以读取具有 key value 对的文件并将这些变量设置为环境变量 但我有疑问 如果我这样做source file txt是否会将该文件中定义的变量设置为环境变量 或者我应该逐行读取文件并使用导出命令设置它
  • 跨 CPU 内核的 rdtsc 精度

    我从一个线程发送网络数据包 并在运行于不同 CPU 核心上的第二个线程上接收回复 我的流程测量每个数据包发送和接收之间的时间 类似于 ping 我使用 rdtsc 来获得高分辨率 低开销的计时 这是我的实现所需要的 所有测量结果看起来都很可
  • 如何在Linux中自动启动需要X的应用程序

    我试图在系统进入运行级别 5 时自动启动 X 应用程序 这样做的正确方法是什么 我写了一个脚本并将其放在 etc init d 中 我已运行适当的 chkconfig 命令来设置 etc rcX d 目录中的符号链接 一切工作正常 除了当我
  • 从另一个 python 脚本获取返回信息

    我在 Linux 上 我有一个 python 脚本 我想从另一个 python 脚本调用它 我不想将其作为模块导入 为了一层安全性 现在为了学术练习 因为我想弄清楚这一点 我实际上想让一个脚本使用 os system 或另一个类似的函数 并

随机推荐

  • C #Winform窗体自适应

    说明 xff1a Winform让窗体内的控件随着窗体的大小而改变 xff0c 而且能让指定的控件始终处于居中位置 封装成类 xff0c 直接拷贝过去即可 一 类源码如下 xff1a using System using System Co
  • Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)解决方案

    docker service Docker Application Container Engine Loaded loaded usr lib systemd system docker service enabled vendor pr
  • emWin - Movie篇

    STemWin版本 xff1a 544 xff08 ST购买了emWin的license xff0c 可以在ST芯片中使用emWin工具包 xff0c 就叫STemWin xff09 emWin开发工具包可以转换JPG BMP GIF等各种
  • 大规模集群故障处理

    我相信每一个集群管理员 xff0c 在长期管理多个不同体量及应用场景的集群后 xff0c 都会多少产生情绪 其实这在我看来 xff0c 是一个很微妙的事 xff0c 即大家也已经开始人性化的看待每一个集群了 既然是人性化的管理集群 xff0
  • ADRC调试经验(已调通)

    自抗扰控制的组成 有关自抗扰的相关内容 xff0c 韩老师在他的一系列论文中已经描绘的非常清晰了 xff0c 具体资料可以点击这里下载 其中对于TD和ESO这两个部分其实是比较好调节的 xff0c 很容易就能够获得很好的效果 比较难调节的参
  • 浏览器的同源策略

    https developer mozilla org zh CN docs Web Security Same origin policy 这篇翻译不完整 请帮忙从英语翻译这篇文章 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个
  • centos简单解决报错-bash 未找到命令

    centos报错 bash 未找到命令 在使用纯净镜像的时候 经常找不到一些额外的命令 想用但是不知道怎么安装 拿telnet 和netstat 举例 telnet yum provides telnet 这里只需要 yum y span
  • [问题已处理]-docker build出来的镜像没有更新成功

    导语 xff1a 记录一下docker build镜像的坑 如果修改代码文件的话 xff0c docker build 有时候会不替换文件 xff0c 而会使用cache xff0c 导致代码文件没有更新 第一次构建镜像 产生了cache
  • [问题已处理]在docker中使用nohup

    导语 xff1a docker运行容器是否能使用nohup 以下是测试在不同的情况下使用nohup 先启动一个容器 仅看进程的pid号参考 docker run it rm ubuntu 16 04 bash sleep 5 amp amp
  • k8s-集群搭建的三种方式和区别,kubeadm、minikube,二进制包

    k8s 集群搭建的三种方式 xff0c 目前主流的搭建k8s集群的方式有kubeadm minikube xff0c 二进制包 kubeadm 是一个工具 xff0c 用于快速搭建kubernetes集群 xff0c 目前应该是比较方便和推
  • 精确算法、启发式算法、元启发式算法及增长方式浅析

    组合优化问题是通过用数学方法的研究去寻找离散事件的最优编排 分组 次序或筛选等 xff0c 其变量是离散分布的 对于结构化的组合优化问题 xff0c 其解空间的规模能够得到控制 xff0c 对于这样的问题 xff0c 使用精确算法就可以求得
  • 重构一个快不可维护的项目

    历史原因 xff0c 接手了一个一直堆业务逻辑 xff0c 没有重构过的项目 xff0c 简单看了一下代码就感觉麻头皮 xff0c 满目都是一个方法里面大段的代码 xff0c 阅读起来极度困难 可以合并的类没有合并 xff0c 导致一个请求
  • 芯片端子的多路复用

    嵌入式软件的开发 xff0c 经常要和芯片打交道 xff0c 和个人电脑的通用平台的CPU使用X86或X64架构不同 xff0c 嵌入式电子产品使用的主控芯片是各种各样的 xff0c 从8051单片机 xff0c 到ARM Cortex M
  • 树莓派学习笔记——获取树莓派CPU温度

    0 前言 本文通过文件操作读取树莓派CPU温度 xff0c 在linux系统中任何设备的操作都被抽象成为文件读写 xff0c 通过读取 sys class thermal thermal zone0 temp文件中的内容便获得树莓派CPU的
  • DHT11温湿度传感器

    1 封装信息 2 DHT11通讯总介 微处理器与DHT 11之间的通讯和同步 xff0c 采用单总线数据格式 xff0c 一次通讯时间4ms左右 xff0c 数据分小数部分和整数部分 一次完整的数据传输为40bit xff0c 高位先出 数
  • does not support raise

    This plugin does not support propagateSizeHints This plugin does not support raise arm平台界面无法显示 xff0c 有如上日志 该系统上安装的是5 11
  • docker jvm 内存限制

    docker 容器提供了相关的内存限制 具体使用方式如 xff1a m 512m 完整例子 docker run rm m 512m e JAVA OPTS 61 Xmx512m tomcat 8 通过 m 进行限制 但是在实际应用重 xf
  • DES加密算法—实现(C语言)

    http www iteye com topic 478024 DES xff08 Data Encrypt Standard数据库加密标准 xff09 是迄今为止使用最广泛的加密体制 初学信息安全的新生 xff0c 一般都会被老师要求实现
  • 国家运输ITS通信协议(NTCIP)简介

    国家智能交通系统工程技术研究中心 张北海 中交国通智能交通系统技术有限公司 肖媛媛 1 NTCIP的发展历程 NTCIP National Transportation Communications for ITS Protocol 是美国
  • linux下使用hiredis异步API实现sub/pub消息订阅和发布的功能

    本文转载自链接 xff1a http blog csdn net chenzba article details 51224715 xfeff xfeff 最近使用redis的c接口 hiredis xff0c 使客户端与redis服务器通