精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何用eBPF實(shí)現(xiàn)一個(gè)學(xué)習(xí)型網(wǎng)橋

Linux閱碼場 ? 來源:未知 ? 2019-11-28 16:56 ? 次閱讀

eBPF技術(shù)風(fēng)靡當(dāng)下,eBPF字節(jié)碼正以星火燎原之勢被HOOK在Linux內(nèi)核中越來越多的位置,在這些HOOK點(diǎn)上,我們可以像編寫普通應(yīng)用程序一樣編寫內(nèi)核的HOOK程序,與以往為了實(shí)現(xiàn)一個(gè)功能動輒patch一整套邏輯框架代碼(比如Netfilter)相比,eBPF的工作方式非常靈活。

我們先來看一下目前eBPF的一些重要HOOK點(diǎn):


將來這個(gè)is_XXX序列肯定會不斷增加,布滿整個(gè)內(nèi)核(有點(diǎn)密集恐懼癥癥狀了...)。

本文將描述如何用eBPF實(shí)現(xiàn)一個(gè)學(xué)習(xí)型網(wǎng)橋的快速轉(zhuǎn)發(fā),并將其部署在XDP。

在開始之前,為了讓所有人都能看懂本文,我們先來回顧一些前置知識,如果暫時(shí)還不懂這些前置知識,沒關(guān)系,先把程序run起來是一個(gè)很好的起點(diǎn),如果到時(shí)候你覺得沒意思,再放棄也不遲。

前置知識

什么是BPF和eBPF

簡單來講,BPF是一套完整的 計(jì)算機(jī)體系結(jié)構(gòu) 。和x86,ARM這些類似,BPF包含自己的指令集和運(yùn)行時(shí)邏輯,同理,就像在x86平臺編程,最終要落實(shí)到x86匯編指令一樣,BPF字節(jié)碼也可以看成是匯編指令的序列。我們通過tcpdump的-d/-dd參數(shù)可見一斑:

[root@localhost ~]# tcpdump -i any tcp and host 1.1.1.1 -d

(000) ldh [14]

(001) jeq #0x86dd jt 10 jf 2

(002) jeq #0x800 jt 3 jf 10

(003) ldb [25]

(004) jeq #0x6 jt 5 jf 10

(005) ld [28]

(006) jeq #0x1010101 jt 9 jf 7

(007) ld [32]

(008) jeq #0x1010101 jt 9 jf 10

(009) ret #262144

(010) ret #0

[root@localhost ~]#

BPF的歷史非常古老,早在1992年就被構(gòu)建出來了,其背后的思想是, “與其把數(shù)據(jù)包復(fù)制到用戶空間執(zhí)行用戶態(tài)程序過濾,不如把過濾程序灌進(jìn)內(nèi)核去。”

遺憾的是,BPF后來并沒有大行其道,只是被應(yīng)用于非常有限的并不起眼的比如抓包層面。因此,由于它的語法并不復(fù)雜,人們直接手寫B(tài)PF匯編指令碼經(jīng)簡單封裝即可生成最終的字節(jié)碼。

當(dāng)人們認(rèn)識到BPF非常強(qiáng)壯的功能并準(zhǔn)備將其大用時(shí),指令系統(tǒng)以及操作系統(tǒng)內(nèi)核均已經(jīng)持續(xù)進(jìn)化了好多年,這意味著簡單的BPF不能再滿足需要,它需要 “被復(fù)雜化”

于是就出現(xiàn)了eBPF,即extended BPF。總體而言,eBPF相比BPF有了以下改進(jìn):1. 更復(fù)雜的指令系統(tǒng)。2. 更多可調(diào)用的函數(shù)。3. ...詳情可參見下面的鏈接:https://lwn.net/Articles/740157/

就像匯編語言進(jìn)化到C語言一樣,直接手寫eBPF字節(jié)碼顯得即笨拙又低效,于是人們開始使用C語言直接編寫eBPF程序,然后用編譯器將其編譯成eBPF字節(jié)碼。遺憾的是,目前eBPF體系結(jié)構(gòu)還不被gcc支持,不過很快就會支持了。我們不得不使用 特定的編譯器 來編譯eBPF的C代碼,比如clang。

什么是XDP

XDP,即eXpress Data Path,它其實(shí)是位于網(wǎng)卡驅(qū)動程序里的一個(gè)快速處理數(shù)據(jù)包的HOOK點(diǎn),為什么快?基于以下兩點(diǎn):

數(shù)據(jù)包處理位置非常底層,避開了很多內(nèi)核skb處理開銷。

可以將很多處理邏輯Offload到網(wǎng)卡硬件

顯而易見,在XDP這個(gè)HOOK點(diǎn)灌進(jìn)來一點(diǎn)eBPF字節(jié)碼,將是一件令人愉快的事情。

學(xué)習(xí)型網(wǎng)橋

Linux的Bridge模塊就是一個(gè)學(xué)習(xí)型網(wǎng)橋,其實(shí)就是一個(gè)現(xiàn)代交換式以太網(wǎng)交換機(jī),它可以從端口學(xué)習(xí)到MAC地址,在內(nèi)部生成MAC/端口映射表,以優(yōu)化轉(zhuǎn)發(fā)效率。

本文我們將用eBPF實(shí)現(xiàn)的網(wǎng)橋就是一個(gè)學(xué)習(xí)型網(wǎng)橋,并且它的數(shù)據(jù)路徑和控制路徑相分離,用eBPF字節(jié)碼實(shí)現(xiàn)的正是其數(shù)據(jù)路徑,它將被灌入XDP,而控制路徑則由一個(gè)用戶態(tài)程序?qū)崿F(xiàn)。

如何編譯eBPF程序

理論的學(xué)習(xí)自在平時(shí),當(dāng)打開電腦的時(shí)候,最快的速度run起來一些東西令人愉悅。我們不想花大量的時(shí)間在環(huán)境的搭建上。對于eBPF程序,內(nèi)核源碼樹的samples/bpf目錄將是一個(gè)非常好的起點(diǎn)。

以我自己的環(huán)境為例,我使用的是Ubuntu 19.10發(fā)行版,5.3.0-19-generic內(nèi)核,安裝源碼后,編譯之,最后編譯samples/bpf即可:

root@zhaoya-VirtualBox:/usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf# make

make -C ../../ /usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf/ BPF_SAMPLES_PATH=/usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf

make[1]: Entering directory '/usr/src/linux-source-5.3.0/linux-source-5.3.0'

CALL scripts/checksyscalls.sh

CALL scripts/atomic/check-atomics.sh

DESCEND objtool

...

samples/bpf目錄下的代碼都是比較典型的范例,我們照貓畫虎就能實(shí)現(xiàn)我們想要的功能。

大體上,每一個(gè)范例均由兩個(gè)部分組成:

XXX_kern.c文件:eBPF字節(jié)碼本身。

XXX_user.c文件:用戶態(tài)控制程序,控制eBPF字節(jié)碼的注入,更新。

即然我們要實(shí)現(xiàn)一個(gè)網(wǎng)橋,那么文件名我們可以確定為:

xdpbridgekern.c

xdpbridgeuser.c

同時(shí)我們修改Makefile文件,加入這兩個(gè)文件即可:

root@zhaoya-VirtualBox: samples/bpf# cat Makefile

...

hostprogs-y += xdp2

hostprogs-y += xdp_bridge

hostprogs-y += xdp_router_ipv4

...

xdp_bridge-objs := xdp_bridge_user.o

xdp_router_ipv4-objs := xdp_router_ipv4_user.o

...

always += xdp2_kern.o

always += xdp_bridge_kern.o

always += xdp_router_ipv4_kern.o

網(wǎng)橋XDP快速轉(zhuǎn)發(fā)的實(shí)現(xiàn)

對上述前置知識有了充分的理解之后,代碼就非常簡單了,我們剩下的工作就是填充xdpbridgekern.c和xdpbridgeuser.c兩個(gè)C文件,然后make它們。

我們先來看xdpbridgekern.c文件:

// xdp_bridge_kern.c

#include

#include

#include "bpf_helpers.h"

// mac_port_map保存該交換機(jī)的MAC/端口映射

struct bpf_map_def SEC("maps") mac_port_map = {

.type = BPF_MAP_TYPE_HASH,

.key_size = sizeof(long),

.value_size = sizeof(int),

.max_entries = 100,

};

// 以下函數(shù)是網(wǎng)橋轉(zhuǎn)發(fā)路徑的eBPF主函數(shù)實(shí)現(xiàn)

SEC("xdp_br")

int xdp_bridge_prog(struct xdp_md *ctx)

{

void *data_end = (void *)(long)ctx->data_end;

void *data = (void *)(long)ctx->data;

long dst_mac = 0;

int in_index = ctx->ingress_ifindex, *out_index;

// data即數(shù)據(jù)包開始位置

struct ethhdr *eth = (struct ethhdr *)data;

char info_fmt[] = "Destination Address: %lx Redirect to:[%d] From:[%d] ";

// 畸形包必須丟棄,否則無法通過內(nèi)核的eBPF字節(jié)碼合法性檢查

if (data + sizeof(struct ethhdr) > data_end) {

return XDP_DROP;

}

// 獲取目標(biāo)MAC地址

__builtin_memcpy(&dst_mac, eth->h_dest, 6);

// 在MAC/端口映射表里查找對應(yīng)該MAC的端口

out_index = bpf_map_lookup_elem(&mac_port_map, &dst_mac);

if (out_index == NULL) {

// 如若找不到,則上傳到慢速路徑,必要時(shí)由控制路徑更新MAC/端口表項(xiàng)。

return XDP_PASS;

}

// 非Hairpin下生效

if (in_index == *out_index) { // Hairpin ?

return XDP_DROP;

}

// 簡單打印些調(diào)試信息

bpf_trace_printk(info_fmt, sizeof(info_fmt), dst_mac, *out_index, in_index);

// 轉(zhuǎn)發(fā)到出端口

return bpf_redirect(*out_index, 0);

}

char _license[] SEC("license") = "GPL";

這里有必要說一下內(nèi)核對eBPF程序的合法性檢查,這個(gè)檢查一點(diǎn)都不多余,它確保你的eBPF代碼是安全的。這樣才不會造成內(nèi)核數(shù)據(jù)結(jié)構(gòu)被破壞掉,否則,如果任意eBPF程序都能注入內(nèi)核,那結(jié)局顯然是細(xì)思極恐的。

現(xiàn)在繼續(xù)我們的用戶態(tài)C代碼:

// xdp_bridge_user.c

#include

#include

#include

#include

#include

#include

#include

#include "bpf_util.h"

int flags = XDP_FLAGS_UPDATE_IF_NOEXIST;

static int mac_port_map_fd;

static int *ifindex_list;

// 退出時(shí)卸載掉XDP的eBPF字節(jié)碼

static void int_exit(int sig)

{

int i = 0;

for (i = 0; i < 2; i++) {

bpf_set_link_xdp_fd(ifindex_list[i], -1, 0);

}

exit(0);

}

int main(int argc, char *argv[])

{

int sock, i;

char buf[1024];

char filename[64];

static struct sockaddr_nl g_addr;

struct bpf_object *obj;

struct bpf_prog_load_attr prog_load_attr = {

// prog_type指明eBPF字節(jié)碼注入的位置,我們網(wǎng)橋的例子中當(dāng)然是XDP

.prog_type = BPF_PROG_TYPE_XDP,

};

int prog_fd;

snprintf(filename, sizeof(filename), "xdp_bridge_kern.o");

prog_load_attr.file = filename;

// 載入eBPF字節(jié)碼

if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) {

return 1;

}

mac_port_map_fd = bpf_object__find_map_fd_by_name(obj, "mac_port_map");

ifindex_list = (int *)calloc(2, sizeof(int *));

// 我們的例子中僅僅支持兩個(gè)端口的網(wǎng)橋,事實(shí)上可以多個(gè)。

ifindex_list[0] = if_nametoindex(argv[1]);

ifindex_list[1] = if_nametoindex(argv[2]);

for (i = 0; i < 2/*total */; i++) {

// 將eBPF字節(jié)碼注入到感興趣網(wǎng)卡的XDP

if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) {

printf("link set xdp fd failed ");

return 1;

}

}

signal(SIGINT, int_exit);

bzero(&g_addr, sizeof(g_addr));

g_addr.nl_family = AF_NETLINK;

g_addr.nl_groups = RTM_NEWNEIGH;

if ((sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {

int_exit(0);

return -1;

}

if (bind(sock, (struct sockaddr *) &g_addr, sizeof(g_addr)) < 0) {

int_exit(0);

return 1;

}

// 持續(xù)監(jiān)聽socket,捕獲Linux網(wǎng)橋上傳的notify信息,從而更新,刪除eBPF的map里特定的MAC/端口表項(xiàng)

while (1) {

int len;

struct nlmsghdr *nh;

struct ndmsg *ifimsg ;

int ifindex = 0;

unsigned char *cmac;

unsigned long lkey = 0;

len = recv(sock, buf, sizeof(buf), 0);

if (len <= 0) continue;

for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {

ifimsg = NLMSG_DATA(nh) ;

if (ifimsg->ndm_family != AF_BRIDGE) {

continue;

}

// 獲取notify信息中的端口

ifindex = ifimsg->ndm_ifindex;

for (i = 0; i < 2; i++) {

if (ifindex == ifindex_list[i]) break;

}

if (i == 2) continue;

// 獲取notify信息中的MAC地址

cmac = (unsigned char *)ifimsg + sizeof(struct ndmsg) + 4;

memcpy(&lkey, cmac, 6);

if (nh->nlmsg_type == RTM_DELNEIGH) {

bpf_map_delete_elem(mac_port_map_fd, (const void *)&lkey);

printf("Delete XDP bpf map-[HW Address:Port] item Key:[%lx] Value:[%d] ", lkey, ifindex);

} else if (nh->nlmsg_type == RTM_NEWNEIGH) {

bpf_map_update_elem(mac_port_map_fd, (const void *)&lkey, (const void *)&ifindex, 0);

printf("Update XDP bpf map-[HW Address:Port] item Key:[%lx] Value:[%d] ", lkey, ifindex);

}

}

}

}

用戶態(tài)程序同樣很容易理解。

數(shù)據(jù)面和控制面分離,這是網(wǎng)絡(luò)設(shè)備的標(biāo)準(zhǔn)路數(shù),幾十年前就這樣了,如今我們也能簡單實(shí)現(xiàn)一個(gè)了,很有趣不是嗎?

run起來

執(zhí)行make之后,我們可以得到可執(zhí)行文件xdpbridge以及eBPF字節(jié)碼文件xdpbridge_kern.o,在當(dāng)前目錄下直接執(zhí)行即可:

root@zhaoya-VirtualBox:samples/bpf# ./xdp_bridge enp0s9 enp0s10

在另一個(gè)終端查看eBPF字節(jié)碼里的map,即MAC/端口映射表:

root@zhaoya-VirtualBox:/home/zhaoya# bpftool p |tail -n 4

166: xdp name xdp_bridge_prog tag 956a68e9ac54a0b3 gpl

loaded_at 2019-11-08T01:14:46+0800 uid 0

xlated 576B jited 340B memlock 4096B map_ids 105

btf_id 114

root@zhaoya-VirtualBox:/home/zhaoya# bpftool map dump id 105

Found 0 elements

root@zhaoya-VirtualBox:/home/zhaoya#

OK,一切順利。現(xiàn)在讓我們正式用它搭建一個(gè)網(wǎng)橋吧。

暫時(shí)X掉xdp_bridge程序的運(yùn)行,讓我們一步一步來。

首先構(gòu)建下面的拓?fù)洌?/p>

中間的Linux Bridge主機(jī)(后面簡稱主機(jī)B)的enp0s9,enp0s10網(wǎng)卡將是我們注入eBPF字節(jié)碼的位置。

現(xiàn)在讓我們在主機(jī)B上創(chuàng)建一個(gè)標(biāo)準(zhǔn)的Linux網(wǎng)橋:

brctl addbr br0;

brctl addif br0 enp0s9;

brctl addif br0 enp0s10;

ifconfig br0 up;

在主機(jī)H1和主機(jī)H2的enp0s9上配置同網(wǎng)段的地址:

H1-enp0s9:40.40.40.201/24

H2-enp0s9:40.40.40.100/24

互相ping確認(rèn)是通的,并且主機(jī)B的enp0s9/enp0s10可以抓到雙向包,這說明主機(jī)B的Linux標(biāo)準(zhǔn)網(wǎng)橋工作是OK的。

接下來,停掉這一切,把br0也刪除掉。重新運(yùn)行xdpbridge程序,確認(rèn)OK后創(chuàng)建Linux標(biāo)準(zhǔn)網(wǎng)橋,從H1來ping H2,很暢通,同時(shí)我們會發(fā)現(xiàn)主機(jī)B的xdpbridge程序的輸出:

root@zhaoya-VirtualBox:/usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf# ./xdp_bridge enp0s9 enp0s10

Update XDP bpf map-[HW Address:Port] item Key:[683dbb270008] Value:[4]

Update XDP bpf map-[HW Address:Port] item Key:[683dbb270008] Value:[4]

Update XDP bpf map-[HW Address:Port] item Key:[e7f09f270008] Value:[5]

Update XDP bpf map-[HW Address:Port] item Key:[e7f09f270008] Value:[5]

Update XDP bpf map-[HW Address:Port] item Key:[e6f09f270008] Value:[4]

很顯然,eBPF的map學(xué)習(xí)到了新的MAC地址,我們可以用bpftool確認(rèn):

root@zhaoya-VirtualBox:~# bpftool p |tail -n 4

170: xdp name xdp_bridge_prog tag 956a68e9ac54a0b3 gpl

loaded_at 2019-11-08T01:26:19+0800 uid 0

xlated 576B jited 340B memlock 4096B map_ids 107

btf_id 117

root@zhaoya-VirtualBox:~# bpftool map dump id 107

key: 08 00 27 9f f0 e7 00 00 value: 05 00 00 00

key: 08 00 27 9f f0 e6 00 00 value: 04 00 00 00

key: 08 00 27 bb 3d 68 00 00 value: 04 00 00 00

Found 3 elements

此時(shí),主機(jī)B的enp0s9和enp0s10就抓不到任何H1和H2之間單播包了。廣播包仍然會被上傳到慢速路徑被標(biāo)準(zhǔn)Linux網(wǎng)橋處理。

我們看trace日志:

root@zhaoya-VirtualBox:~# cat /sys/kernel/debug/tracing/trace_pipe

-0 [003] ..s. 44274.198178: 0: Destination Address: e6f09f270008 Redirect to:[4] From:[5]

...

雖然主機(jī)B的網(wǎng)卡上沒有抓到包,但如何確保數(shù)據(jù)包真的就是從XDP的eBPF字節(jié)碼轉(zhuǎn)發(fā)走的而不是直接飛過去的呢?

很好的問題,這作為下一個(gè)練習(xí)不是更好嗎?嗯,你應(yīng)該試試加一個(gè)統(tǒng)計(jì)功能,而這個(gè)并不復(fù)雜。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11232

    瀏覽量

    208950
  • 網(wǎng)橋
    +關(guān)注

    關(guān)注

    0

    文章

    129

    瀏覽量

    16955
  • BPF
    BPF
    +關(guān)注

    關(guān)注

    0

    文章

    24

    瀏覽量

    3979

原文標(biāo)題:實(shí)現(xiàn)一個(gè)基于XDP_eBPF的學(xué)習(xí)型網(wǎng)橋

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    基于LPC1114的學(xué)習(xí)型紅外遙控器程序設(shè)計(jì)

    本設(shè)計(jì)就是采用LPC1114+OLED+紅外接收頭構(gòu)成個(gè)學(xué)習(xí)型紅外遙控器,
    發(fā)表于 12-14 16:44 ?6013次閱讀

    分享下學(xué)習(xí)型紅外遙控資料

    分享下學(xué)習(xí)型紅外遙控資料
    發(fā)表于 07-30 19:47

    學(xué)習(xí)型遙控器

    學(xué)習(xí)型遙控器
    發(fā)表于 08-16 16:59

    智能學(xué)習(xí)型紅外遙控器設(shè)計(jì)

    智能學(xué)習(xí)型紅外遙控器設(shè)計(jì)
    發(fā)表于 08-16 19:26

    用89C52 做一個(gè)學(xué)習(xí)型遙控器

    現(xiàn)在有單片機(jī)89C52 紅外線發(fā)射二級管 三級管 按鈕 電阻 電容 0038紅外接收 晶振 想做一個(gè) 學(xué)習(xí)型遙控器 遙控電視 和空調(diào) 帶串口的更好 需要給我電路圖和元器件型號 還有單片機(jī)文件 不要粘貼復(fù)制的那種 網(wǎng)上找了很多 都不行 需要懂行的朋友幫助 非常感謝你們 急
    發(fā)表于 02-13 09:04

    學(xué)習(xí)型紅外遙控器設(shè)計(jì)

    有沒有前輩知道怎么寫基于MSP430F149的學(xué)習(xí)型紅外遙控器設(shè)計(jì)程序的?麻煩聯(lián)系我。O(∩_∩)O謝謝984300719
    發(fā)表于 05-01 15:22

    如何增加學(xué)習(xí)型遙控器的學(xué)習(xí)距離?

    如圖,學(xué)習(xí)型遙控器,那部分是學(xué)習(xí)天線,如何增大信號?
    發(fā)表于 08-16 09:48

    請問單片機(jī)解碼433MHZ EV1527學(xué)習(xí)型編碼ic 學(xué)習(xí)功能如何用程序實(shí)現(xiàn)

    目前我在做一個(gè)51單片機(jī)解碼433Mhz模塊的EV1527學(xué)習(xí)型編碼IC,不知道如何學(xué)習(xí)對碼?
    發(fā)表于 09-09 15:04

    基于AT89C52的學(xué)習(xí)型遙控器的設(shè)計(jì)

    本文介紹了種基于 AT89C52 的學(xué)習(xí)型遙控器,并對其工作原理及軟、硬件的設(shè)計(jì)和實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的闡述。關(guān)鍵詞: AT89C52; 學(xué)習(xí)型遙控器; 紅外遙控編碼Abstract:
    發(fā)表于 08-14 08:58 ?208次下載

    基于NiosⅡ的紅外學(xué)習(xí)型遙控器設(shè)計(jì)

      本文設(shè)計(jì)了種基于NiosⅡ的紅外學(xué)習(xí)型遙控器,把載波頻率測量、紅外信號解調(diào)、脈寬測量、調(diào)制發(fā)送IP核集中到FPG
    發(fā)表于 12-15 10:39 ?2594次閱讀

    自主學(xué)習(xí)型網(wǎng)絡(luò)課件的研究與開發(fā)

    隨著網(wǎng)絡(luò)信息的高速發(fā)展,傳統(tǒng)網(wǎng)絡(luò)課件以被動知識展示為主要手段已經(jīng)不能滿足學(xué)習(xí)者自主學(xué)習(xí)的需求。自主學(xué)習(xí)型網(wǎng)絡(luò)課件立足學(xué)習(xí)者自身知識儲備,可以實(shí)現(xiàn)
    發(fā)表于 10-24 15:19 ?0次下載
    自主<b class='flag-5'>學(xué)習(xí)型</b>網(wǎng)絡(luò)課件的研究與開發(fā)

    學(xué)習(xí)型紅外線遙控設(shè)計(jì)與制作解析

    本文主要介紹了學(xué)習(xí)型紅外線遙控設(shè)計(jì)與制作解析.
    發(fā)表于 06-26 08:00 ?92次下載

    關(guān)于R8C/Lx學(xué)習(xí)型遙控器設(shè)計(jì)的介紹

    R8C/Lx學(xué)習(xí)型遙控器參考設(shè)計(jì)
    的頭像 發(fā)表于 07-23 01:04 ?3209次閱讀

    紅外學(xué)習(xí)型遙控器方案說明

    紅外學(xué)習(xí)型遙控器可通過學(xué)習(xí)操作學(xué)習(xí)其它遙控器上的部分按鍵,實(shí)現(xiàn)遙控器可遙控兩種設(shè)備,方便用戶操作。
    的頭像 發(fā)表于 10-18 11:31 ?4359次閱讀

    學(xué)習(xí)型遙控器的設(shè)計(jì)與實(shí)現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《學(xué)習(xí)型遙控器的設(shè)計(jì)與實(shí)現(xiàn).doc》資料免費(fèi)下載
    發(fā)表于 10-24 09:33 ?1次下載
    <b class='flag-5'>學(xué)習(xí)型</b>遙控器的設(shè)計(jì)與<b class='flag-5'>實(shí)現(xiàn)</b>