1 Star 0 Fork 1

苏小逝 / s_task

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
BSL-1.0

s_task - 跨平台的C语言协程多任务库

目录

特性

  • 全c和汇编实现,紧凑小巧又不失实用,并且不需要c++。
  • 协程切换代码来自boost汇编,性能极好,稳定可靠,移植性好,几乎全平台支持。
  • 和libuv(稍作修改)无缝融合,完美支持跨平台网络编程。
  • 支持 await , async 关键词,含义和用法都其他语言的await/async相同 -- 没有调用 await 函数的地方,协程肯定不会被切换出去,可确保共享数据不会被其他协程改变。 具备传染性,能调用 await 的函数,一定在一个 async 函数里。这个async 函数需要用 await 调用。
  • 支持协程间的event变量、mutex锁、chan数据通道等,方便不同协程间同步数据和状态。这个方式比其他协程resume函数更好用和可控。
  • 除支持windows, linux, macos这些常规环境外,更能为stm32等嵌入式小芯片环境提供多任务支持(注:小芯片环境下不支持libuv)。
  • 在嵌入式小芯片下使用,s_task是个恰到好处的RTOS -- 没有动态内存分配,增加程序大小不到 1.5k, 不增加程序空间负担,支持任务和中断间通讯。

协程 vs 多线程

协程和多线程编程模式对比,协程的优势极其明显 --

  • 协程不会陷入死锁的窘境。

  • 协程需要的代码量极小。

    一般协程比多线程更少的代码量就能实现,这在资源捉襟见肘的嵌入式单片机中尤其重要。有时跑个多线程 RTOS 库,应用自己都没空间了。s_task协程只增加了不到 1.5K 的代码量,这对单片机极其友好。

  • 协程主动让出CPU,没有也不需要 “抢占式多任务” 。

    您没看错,人们已经开始反思,“抢占式多任务” 根本不是啥优势,而是多线程最大的缺点,更是bug之源。主动让出CPU的协程,减少bug的同时,更能带来更好的CPU利用率,更多的并发任务数。这也是近年来,不管C++, C#, nodejs, java, php各式语言,都开始引入协程的原因。

  • 协程比任何的所谓 “实时操作系统RTOS” 更实时。

  • 协程有 await 标注任务可能切换。

    通过__await__标注,程序员明确的知道,在某个函数在运行的时候,CPU可能会被切换到其他任务。如此多任务间共享变量变得无比安全,这点是线程不能比的。

s_task协程库,更是打造了全平台兼容的协程支持环境,从高端linux服务器、windows/apple桌面,到安卓ndk,到stm32、arduino等各式嵌入式无操作系统环境都能支持,有些芯片的运行内存甚至可以低至1K大小。(参考兼容性列表

所有这些平台,全部共享一套同样接口的多任务API。使用s_task,您将用最小的使用成本,获得最大的收益。

现在,放弃进入历史垃圾堆的多线程编程,开始您的 s_task 协程之旅!

示例

示例 1 - 创建简单任务

#include <stdio.h>
#include "s_task.h"

//定义协程任务需要的栈空间
void* g_stack_main[64 * 1024];
void* g_stack0[64 * 1024];
void* g_stack1[64 * 1024];

void sub_task(__async__, void* arg) {
    int i;
    int n = (int)(size_t)arg;
    for (i = 0; i < 5; ++i) {
        printf("task %d, delay seconds = %d, i = %d\n", n, n, i);
        s_task_msleep(__await__, n * 1000);  //等待一点时间
    }
}

void main_task(__async__, void* arg) {
    int i;

    //创建两个子任务
    s_task_create(g_stack0, sizeof(g_stack0), sub_task, (void*)1);
    s_task_create(g_stack1, sizeof(g_stack1), sub_task, (void*)2);

    for (i = 0; i < 4; ++i) {
        printf("task_main arg = %p, i = %d\n", arg, i);
        s_task_yield(__await__); //主动让出cpu
    }

    //等待子任务结束
    s_task_join(__await__, g_stack0);
    s_task_join(__await__, g_stack1);
}

int main(int argc, char* argv) {

    s_task_init_system();

    //创建一个任务
    s_task_create(g_stack_main, sizeof(g_stack_main), main_task, (void*)(size_t)argc);
    s_task_join(__await__, g_stack_main);
    printf("all task is over\n");
    return 0;
}

示例 2 - (无需回调函数的)异步HTTP客户端程序

void main_task(__async__, void *arg) {
    uv_loop_t* loop = (uv_loop_t*)arg;

    const char *HOST = "baidu.com";
    const unsigned short PORT = 80;

    //<1> 异步域名解析
    struct addrinfo* addr = s_uv_getaddrinfo(__await__,
        loop,
        HOST,
        NULL,
        NULL);
    if (addr == NULL) {
        fprintf(stderr, "can not resolve host %s\n", HOST);
        goto out0;
    }

    if (addr->ai_addr->sa_family == AF_INET) {
        struct sockaddr_in* sin = (struct sockaddr_in*)(addr->ai_addr);
        sin->sin_port = htons(PORT);
    }
    else if (addr->ai_addr->sa_family == AF_INET6) {
        struct sockaddr_in6* sin = (struct sockaddr_in6*)(addr->ai_addr);
        sin->sin6_port = htons(PORT);
    }

    //<2> 异步连接服务端
    uv_tcp_t tcp_client;
    int ret = uv_tcp_init(loop, &tcp_client);
    if (ret != 0)
        goto out1;
    ret = s_uv_tcp_connect(__await__, &tcp_client, addr->ai_addr);
    if (ret != 0)
        goto out2;

    //<3> 异步发送请求
    const char *request = "GET / HTTP/1.0\r\n\r\n";
    uv_stream_t* tcp_stream = (uv_stream_t*)&tcp_client;
    s_uv_write(__await__, tcp_stream, request, strlen(request));

    //<4> 异步读HTTP返回数据
    ssize_t nread;
    char buf[1024];
    while (true) {
        ret = s_uv_read(__await__, tcp_stream, buf, sizeof(buf), &nread);
        if (ret != 0) break;

        // 输出从HTTP服务器读到的数据
        fwrite(buf, 1, nread, stdout);
    }

    //<5> 关闭连接
out2:;
    s_uv_close(__await__, (uv_handle_t*)&tcp_client);
out1:;
    uv_freeaddrinfo(addr);
out0:;
}

示例 3 - ardinuo下同时跑多个任务控制led闪烁

#include <stdio.h>
#include "src/s_task/s_task.h"

//这个程序运行了三个任务
// 1) 任务 main_task - 
//    等10秒并设置退出标志 g_exit,
//    在所有其他任务退出后,将LED设为常量。
// 2) 任务 sub_task_fast_blinking -
//    使 LED 快速闪烁
// 3) 任务 sub_task_set_low -
//    使上个任务中快速闪烁的LED,没快速闪烁3秒种,熄灭1秒种。

void setup() {
    // 初始化LED
    pinMode(LED_BUILTIN, OUTPUT);
}


//定义协程任务需要的栈空间
char g_stack0[384];
char g_stack1[384];
volatile bool g_is_low = false;
volatile bool g_exit = false;

void sub_task_fast_blinking(__async__, void* arg) {
    while(!g_exit) {
        if(!g_is_low)
            digitalWrite(LED_BUILTIN, HIGH); // 点亮LED

        s_task_msleep(__await__, 50);        // 等50毫秒
        digitalWrite(LED_BUILTIN, LOW);      // 熄灭LED
        s_task_msleep(__await__, 50);        // 等50毫秒
    }    
}

void sub_task_set_low(__async__, void* arg) {
    while(!g_exit) {
        g_is_low = true;                     // 关闭LED快闪
        digitalWrite(LED_BUILTIN, LOW);      // 熄灭LED
        s_task_sleep(__await__, 1);          // 等1秒
        g_is_low = false;                    // 打开LED快闪
        s_task_sleep(__await__, 3);          // 等3秒
    }    
}

void main_task(__async__, void* arg) {
    // 创建两个任务
    s_task_create(g_stack0, sizeof(g_stack0), sub_task_fast_blinking, NULL);
    s_task_create(g_stack1, sizeof(g_stack1), sub_task_set_low, NULL);

    // 等10秒
    s_task_sleep(__await__, 10);
    g_exit = true;

    // 等待两个任务结束
    s_task_join(__await__, g_stack0);
    s_task_join(__await__, g_stack1);
}

void loop() {
    
    s_task_init_system();
    main_task(__await__, NULL);

    // 使LED常亮
    digitalWrite(LED_BUILTIN, HIGH);
    while(1);
}

兼容性

"s_task" 可以作为一个单独的库使用,也可以配合libuv实现跨平台网络编程(编译时加上宏定义USE_LIBUV)。

平台 coroutine协程 libuv支持
Windows :heavy_check_mark: :heavy_check_mark:
Linux :heavy_check_mark: :heavy_check_mark:
MacOS :heavy_check_mark: :heavy_check_mark:
FreeBSD (12.1, x64) :heavy_check_mark: :heavy_check_mark:
Android :heavy_check_mark: :heavy_check_mark:
MingW (https://www.msys2.org/) :heavy_check_mark: :heavy_check_mark:
ARMv6-M (M051) :heavy_check_mark: :x:
ARMv7-M (STM32F103, STM32F302) :heavy_check_mark: :x:
STM8 (STM8S103, STM8L051F3) :heavy_check_mark: :x:
Arduino UNO (AVR MEGA328P) :heavy_check_mark: :x:
Arduino DUE (ATSAM3X8E) :heavy_check_mark: :x:

linux在以下硬件环境测试通过

  • i686 (ubuntu-16.04)
  • x86_64 (centos-8.1)
  • arm (树莓派32位)
  • aarch64 (① 树莓派64位, ② ubuntu 14.04 / centos7.6 运行于华为鲲鹏920)
  • mipsel (openwrt ucLinux 3.10.14 for MT7628)
  • mips64 (fedora for loongson 3A-4000 龙芯)
  • riscv64 (jslinux)

编译

Linux / FreeBSD / MacOS / MingW(MSYS2)

git clone https://github.com/xhawk18/s_task.git
cd s_task/build/
cmake .
make

若您采用交叉编译器,请在上述运行 "cmake ." 指令时,加上参数 CMAKE_C_COMPILER 指定您所使用的交叉编译器,例如 --

cmake . -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc

Windows 或其他平台

平台 项目 工具链
Windows build\windows\s_task.sln visual studio 2019
Android build\android\cross_build_arm*.sh android ndk 20, API level 21 (在termux测试)
STM8S103 build\stm8s103\Project.eww IAR workbench for STM8
STM8L051F3 build\stm8l05x\Project.eww IAR workbench for STM8
STM32F103 build\stm32f103\arcc\Project.uvproj Keil uVision5
STM32F103 build\stm32f103\gcc\Project.uvproj arm-none-eabi-gcc
STM32F302 build\stm32f302\Project.uvporj Keil uVision5
M051 build\m051\Project.uvporj Keil uVision5
ATmega328P build\atmega328p\atmega328p.atsln Atmel Studio 7.0
Arduino UNO
Arduino DUE
build\arduino\arduino.ino Arduino IDE

如何在您的项目中使用s_task?

在 linux/unix 等环境里,可以先用cmake编译,编译完成后,将产生可以直接用于您的项目的链接库文件,您可以通过以下简单3步使用s_task --

  • 将 libs_task.a 加入到您的项目
  • #include "s_task.h"
  • 编译时加上宏定义 USE_LIBUV

在 arduino 上使用,可以复制目录 "include" 和 "src" 下的所有*.h, *.c文件到您的项目的 src/s_task目录下。这里有个实际的目录结果可供参考:"build/arduino/"。

在 windows 或其他平台,请用 build 目录下的项目作为项目模板和参考。

API

Task (任务)

/* 
 * Return values -- 
 * For all functions marked by __async__ and hava an int return value, will
 *     return 0 on waiting successfully,
 *     return -1 on waiting cancalled by s_task_cancel_wait() called by other task.
 */

/* Function type for task entrance */
typedef void(*s_task_fn_t)(__async__, void *arg);

/* Create a new task */
void s_task_create(void *stack, size_t stack_size, s_task_fn_t entry, void *arg);

/* Wait a task to exit */
int s_task_join(__async__, void *stack);

/* Sleep in milliseconds */
int s_task_msleep(__async__, uint32_t msec);

/* Sleep in seconds */
int s_task_sleep(__async__, uint32_t sec);

/* Yield current task */
void s_task_yield(__async__);

/* Cancel task waiting and make it running */
void s_task_cancel_wait(void* stack);

Chan (数据通道)

/*
 * macro: Declare the chan variable
 *    name: name of the chan
 *    TYPE: type of element in the chan
 *    count: max count of element buffer in the chan
 */
s_chan_declare(name,TYPE,count);

/*
 * macro: Initialize the chan (parameters same as what's in s_declare_chan).
 * To make a chan, we need to use "s_chan_declare" and then call "s_chan_init".
 */
s_chan_init(name,TYPE,count);

/* Put element into chan */
void s_chan_put(__async__, s_chan_t *chan, const void *in_object);

/* Put number of elements into chan */
void s_chan_put_n(__async__, s_chan_t *chan, const void *in_object, uint16_t number);

/* Get element from chan */
void s_chan_get(__async__, s_chan_t *chan, void *out_object);

/* Get number of elements from chan */
void s_chan_get_n(__async__, s_chan_t *chan, void *out_object, uint16_t number);

Mutex (互斥量)

/* Initialize a mutex */
void s_mutex_init(s_mutex_t *mutex);

/* Lock the mutex */
int s_mutex_lock(__async__, s_mutex_t *mutex);

/* Unlock the mutex */
void s_mutex_unlock(s_mutex_t *mutex);

Event (事件)

/* Initialize a wait event */
void s_event_init(s_event_t *event);

/* Wait event */
int s_event_wait(__async__, s_event_t *event);

/* Set event */
void s_event_set(s_event_t *event);

/* Wait event with timeout */
int s_event_wait_msec(__async__, s_event_t *event, uint32_t msec);

/* Wait event with timeout */
int s_event_wait_sec(__async__, s_event_t *event, uint32_t sec);

嵌入式平台

嵌入式平台特殊API

Chan for interrupt (中断和任务的数据通道,仅嵌入式平台支持,STM8/STM32/M051/Arduino)

任务里调用的 chan api

/* Task puts element into chan and waits interrupt to read the chan */
void s_chan_put__to_irq(__async__, s_chan_t *chan, const void *in_object);

/* Task puts number of elements into chan and waits interrupt to read the chan */
void s_chan_put_n__to_irq(__async__, s_chan_t *chan, const void *in_object, uint16_t number);

/* Task waits interrupt to write the chan and then gets element from chan */
void s_chan_get__from_irq(__async__, s_chan_t *chan, void *out_object);

/* Task waits interrupt to write the chan and then gets number of elements from chan */
void s_chan_get_n__from_irq(__async__, s_chan_t *chan, void *out_object, uint16_t number);

中断里调用 chan api

/*
 * Interrupt writes element into the chan,
 * return number of element was written into chan
 */
uint16_t s_chan_put__in_irq(s_chan_t *chan, const void *in_object);

/*
 * Interrupt writes number of elements into the chan,
 * return number of element was written into chan
 */
uint16_t s_chan_put_n__in_irq(s_chan_t *chan, const void *in_object, uint16_t number);

/*
 * Interrupt reads element from chan,
 * return number of element was read from chan
 */
uint16_t s_chan_get__in_irq(s_chan_t *chan, void *out_object);

/*
 * Interrupt reads number of elements from chan,
 * return number of element was read from chan
 */
uint16_t s_chan_get_n__in_irq(s_chan_t *chan, void *out_object, uint16_t number);

Event for interrupt (中断里的事件,仅嵌入式平台支持,STM8/STM32/M051/Arduino)

任务里调用的 event api

/* 
 * Wait event from irq, disable irq before call this function!
 *   S_IRQ_DISABLE()
 *   ...
 *   s_event_wait__from_irq(...)
 *   ...
 *   S_IRQ_ENABLE()
 */
int s_event_wait__from_irq(__async__, s_event_t *event);

/* 
 * Wait event from irq, disable irq before call this function!
 *   S_IRQ_DISABLE()
 *   ...
 *   s_event_wait_msec__from_irq(...)
 *   ...
 *   S_IRQ_ENABLE()
 */
int s_event_wait_msec__from_irq(__async__, s_event_t *event, uint32_t msec);

/* 
 * Wait event from irq, disable irq before call this function!
 *   S_IRQ_DISABLE()
 *   ...
 *   s_event_wait_sec__from_irq(...)
 *   ...
 *   S_IRQ_ENABLE()
 */
int s_event_wait_sec__from_irq(__async__, s_event_t *event, uint32_t sec);

中断里调用的 event api

/* Set event in interrupt */
void s_event_set__in_irq(s_event_t *event);
低功耗运行模式

如果my_on_idle函数为空,当没有任务运行时,程序将进入忙等待模式,这样通常表现为CPU占据了100%的时间。 为避免此问题,可实现适当的 my_on_idle 函数,以便程序可以低功耗运行。

目前在Windows/Linux/MacOS/Android等平台上,已经实现低功耗运行模式。

在无操作系统的嵌入式环境下,可能并未做低功耗支持(请检查对应平台的my_on_idle函数)。 如果您希望自己优化芯片运行功耗,可在 my_on_idle 函数加入使芯片睡眠一段时间的代码,睡眠时间最长为 max_idle_ms 毫秒。

void my_on_idle(uint64_t max_idle_ms) {
    /* 增加使CPU睡眠代码,最长不超过  max_idle_ms 毫秒 */
}

希望移植到新的平台?

移植文档参考此处

联系方式

使用中有任何问题或建议,欢迎QQ加群 567780316 交流。

s_task交流群

其他协程库对比

MIT License Copyright (c) 2020 xhawk18 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================================================ This software use code with 3rd License ================================================================================ PostgreSQL Database Management System (formerly known as Postgres, then as Postgres95) Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group Portions Copyright (c) 1994, The Regents of the University of California Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ================================================================================ Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================================================ libuv is licensed for use as follows: ==== Copyright (c) 2015-present libuv project contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== This license applies to parts of libuv originating from the https://github.com/joyent/libuv repository: ==== Copyright Joyent, Inc. and other Node contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== This license applies to all parts of libuv that are not externally maintained libraries. The externally maintained libraries used by libuv are: - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. - inet_pton and inet_ntop implementations, contained in src/inet.c, are copyright the Internet Systems Consortium, Inc., and licensed under the ISC license. - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three clause BSD license. - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. Three clause BSD license. - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement n° 289016). Three clause BSD license. ================================================================================ The AVR code from https://github.com/LeeReindeer/ROS with MIT License

简介

暂无描述 展开 收起
BSL-1.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/kimbeaur/s_task.git
git@gitee.com:kimbeaur/s_task.git
kimbeaur
s_task
s_task
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891