日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
深入了解Linux1Wire編程,開創(chuàng)智能家居新篇章(linux1wire編程)

智能家居是未來的發(fā)展趨勢,而深入了解linux 1wire編程則為智能家居帶來了更多的應(yīng)用和可塑性。本文將通過介紹Linux 1Wire編程的基本原理和實(shí)現(xiàn)方法,展示Linux 1Wire編程在智能家居領(lǐng)域中的廣泛應(yīng)用。

目前創(chuàng)新互聯(lián)已為近千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站運(yùn)營、企業(yè)網(wǎng)站設(shè)計、軹城網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

一、Linux 1Wire編程的基本原理

Linux 1Wire編程是一種在Linux平臺上操作1Wire總線的編程技術(shù)。1Wire總線是一種串行數(shù)據(jù)總線,常用于連接智能設(shè)備和傳感器,如溫度傳感器、濕度傳感器等。通過1Wire總線,這些設(shè)備可以向主機(jī)發(fā)送數(shù)據(jù)和接收指令。

在Linux系統(tǒng)中,1Wire總線的操作可以通過引腳控制器來實(shí)現(xiàn)。其中,位于/sys/bus/w1/devices/目錄下的文件夾代表了每個連接到1Wire總線上的設(shè)備。每個設(shè)備都有一個唯一的編號,通常由8個十六進(jìn)制數(shù)字組成。該編號代表了設(shè)備在1Wire總線上的地址。

通過讀寫/sys/bus/w1/devices/目錄下的文件,我們可以向連接到1Wire總線上的設(shè)備發(fā)送指令并讀取其返回值。當(dāng)我們向一個設(shè)備發(fā)送讀取數(shù)據(jù)的指令時,該設(shè)備會向1Wire總線上發(fā)送其存儲的數(shù)據(jù)。我們可以通過解析該數(shù)據(jù)來獲取我們需要的信息。

二、Linux 1Wire編程的實(shí)現(xiàn)方法

在Linux系統(tǒng)中,我們可以通過shell腳本或C語言來實(shí)現(xiàn)1Wire總線的操作。其中,C語言實(shí)現(xiàn)的1Wire驅(qū)動可以更加穩(wěn)定和高效。

以下是一個實(shí)現(xiàn)讀取溫度傳感器數(shù)據(jù)的C語言示例代碼:

“`c

#include

#include

#include

#include

#include

#define BUF_SIZE 256

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

{

int fd;

char buf[BUF_SIZE];

char path[BUF_SIZE];

float temp = 0;

snprintf(path, BUF_SIZE, “/sys/bus/w1/devices/%s/w1_slave”, argv[1]);

fd = open(path, O_RDON);

if (fd == -1) {

perror(“Fled to open device”);

exit(EXIT_FLURE);

}

if (read(fd, buf, BUF_SIZE) == -1) {

perror(“Fled to read device”);

exit(EXIT_FLURE);

}

close(fd);

if (strstr(buf, “YES”) != NULL) {

char *tmp = strstr(buf, “t=”);

if (tmp != NULL) {

temp = atoi(tmp + 2) / 1000.0;

printf(“Temperature: %.2f\n”, temp);

}

} else {

printf(“CRC check fled\n”);

}

return 0;

}

“`

該代碼通過讀取/sys/bus/w1/devices/目錄下的文件來獲取存儲在溫度傳感器內(nèi)的溫度數(shù)據(jù)。代碼將傳感器的地址作為參數(shù)傳入程序。然后,程序通過該地址構(gòu)建存儲溫度數(shù)據(jù)的文件路徑。之后程序從該文件中讀取存儲的數(shù)據(jù)并解析出溫度值。程序輸出溫度值并退出。

三、Linux 1Wire編程在智能家居領(lǐng)域的應(yīng)用

Linux 1Wire編程在智能家居領(lǐng)域的應(yīng)用非常廣泛。以下是幾個典型的應(yīng)用場景:

1. 溫度監(jiān)測

通過連接多個溫度傳感器到1Wire總線上,我們可以實(shí)現(xiàn)對整個家居環(huán)境的溫度監(jiān)測。通過1Wire編程,我們可以讀取所有傳感器的溫度數(shù)據(jù),并將其存儲到數(shù)據(jù)庫中,然后用于分析和控制。例如,我們可以通過該數(shù)據(jù)來自動調(diào)節(jié)家居的空調(diào)、加熱器、通風(fēng)系統(tǒng)等。

2. 水質(zhì)監(jiān)測

通過連接多個水質(zhì)傳感器到1Wire總線上,我們可以實(shí)現(xiàn)對家庭自來水、池塘和魚缸等水體的監(jiān)測。通過1Wire編程,我們可以讀取傳感器的ph值、溶解氧、溫度等參數(shù),并將其存儲到數(shù)據(jù)庫中,以便后續(xù)分析和控制。例如,我們可以通過該數(shù)據(jù)來控制魚缸內(nèi)的水泵、空氧機(jī)、澆水設(shè)備等。

3. 窗戶控制

通過連接多個窗戶控制器到1Wire總線上,我們可以實(shí)現(xiàn)對家庭窗戶的遠(yuǎn)程控制。例如,我們可以通過1Wire編程來控制窗戶的開關(guān)狀態(tài)、自動調(diào)節(jié)窗戶高度、遠(yuǎn)程監(jiān)測窗戶狀態(tài)等。這些控制器可以自由放置于窗戶上,并通過1Wire總線連接到智能家居控制器。

成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián),建站經(jīng)驗(yàn)豐富以策略為先導(dǎo)10多年以來專注數(shù)字化網(wǎng)站建設(shè),提供企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計,響應(yīng)式網(wǎng)站制作,設(shè)計師量身打造品牌風(fēng)格,熱線:028-86922220

如何看懂《Linux多線程服務(wù)端編程

一:進(jìn)程和線程

每個進(jìn)程有自己獨(dú)立的地址空間?!霸谕粋€進(jìn)程”還是“不在同一個進(jìn)程”是系統(tǒng)功能劃分的重要決策點(diǎn)。《Erlang程序設(shè)計》把進(jìn)程比喻為人:

每個人有自己的記憶(內(nèi)存),人與人通過談話(消息傳遞)來交流,談話既可以是面談姿并野(同一臺服務(wù)器),也可以在里談(不同的服務(wù)器,有網(wǎng)絡(luò)通信)。面談和談的區(qū)別在于,面談可以立即知道對方是否死了(crash,SIGCHLD),而談只能通過周期性的心跳來判斷對方是否還活著。

有了這些比喻,設(shè)計分布式系統(tǒng)時可以采取“角色扮演”,團(tuán)隊(duì)里的幾個人各自扮演一個進(jìn)程,人的角色由進(jìn)程的代碼決定(管登錄的、管消息分發(fā)的、管買賣的等等)。每個人有自己的記憶,但不知道別人的記憶,要想知道別人的看法,只能通過交談(暫不考慮共享內(nèi)存這種IPC)。然后就可以思考:跡喊

·容錯:萬一有人突然死了

·擴(kuò)容:新人中途加進(jìn)來

·負(fù)載均衡:把甲的活兒挪給乙做

·退休:甲要修復(fù)bug,先別派新任務(wù),等他做完手上的事情就把他重啟

等等各種場景,十分便利。

線程的特點(diǎn)是共享地址空間,從而可以高效地共享數(shù)據(jù)。一臺機(jī)器上的多個進(jìn)程能高效地共享代碼段(操作系統(tǒng)可以映射為同樣的物理內(nèi)存),但不能共享數(shù)據(jù)。如果多個進(jìn)程大量共享內(nèi)存,等于是把多進(jìn)程程序當(dāng)成多線程來寫,掩耳盜鈴。

“多線程”的價值,我認(rèn)為是為了更好地發(fā)揮多核處理器(multi-cores)的效能。在單核時代,多線程沒有多大價值(個人想法:如果要完成的任務(wù)是CPU密集型的,那多線程沒有優(yōu)勢,甚至因?yàn)榫€程切換的開銷,多線程反而更慢;如果要完成的任務(wù)既有CPU計算,又有磁盤或網(wǎng)絡(luò)IO,則使用多線程的好處是,當(dāng)某個線程因?yàn)镮O而阻塞時,OS可以調(diào)度其他線程執(zhí)行,雖然效率確實(shí)要比任務(wù)的順序執(zhí)行效率要高,然而,這種類型的任務(wù),可以通過單線程的”non-blocking IO+IO multiplexing”的模型(事件驅(qū)動)來提高效率,采用多線程的方式,帶來的可能僅僅是編程上的簡單而已)。Alan Cox說過:”A computer is a state machine.Threads are for people who can’t program state machines.”(計算機(jī)是一臺狀態(tài)機(jī)。線程是給那些不能編寫狀態(tài)機(jī)程序的人準(zhǔn)備的)如果只有一塊CPU、一個執(zhí)行單元,那么確實(shí)如Alan Cox所說,按狀態(tài)機(jī)的思路去寫程序是最蔽謹(jǐn)高效的。

二:單線程服務(wù)器的常用編程模型

據(jù)我了解,在高性能的網(wǎng)絡(luò)程序中,使用得最為廣泛的恐怕要數(shù)”non-blocking IO + IO multiplexing”這種模型,即Reactor模式。

在”non-blocking IO + IO multiplexing”這種模型中,程序的基本結(jié)構(gòu)是一個事件循環(huán)(event loop),以事件驅(qū)動(event-driven)和事件回調(diào)的方式實(shí)現(xiàn)業(yè)務(wù)邏輯:

view plain copy

//代碼僅為示意,沒有完整考慮各種情況

while(!done)

{

int timeout_ms = max(1000, getNextTimedCallback());

int retval = poll(fds, nfds, timeout_ms);

if (retval0){

處理IO事件,回調(diào)用戶的IO event handler

}

}

}

這里select(2)/poll(2)有伸縮性方面的不足(描述符過多時,效率較低),Linux下可替換為epoll(4),其他操作系統(tǒng)也有對應(yīng)的高性能替代品。

Reactor模型的優(yōu)點(diǎn)很明顯,編程不難,效率也不錯。不僅可以用于讀寫socket,連接的建立(connect(2)/accept(2)),甚至DNS解析都可以用非阻塞方式進(jìn)行,以提高并發(fā)度和吞吐量(throughput),對于IO密集的應(yīng)用是個不錯的選擇。lighttpd就是這樣,它內(nèi)部的fdevent結(jié)構(gòu)十分精妙,值得學(xué)習(xí)。

基于事件驅(qū)動的編程模型也有其本質(zhì)的缺點(diǎn),它要求事件回調(diào)函數(shù)必須是非阻塞的。對于涉及網(wǎng)絡(luò)IO的請求響應(yīng)式協(xié)議,它容易割裂業(yè)務(wù)邏輯,使其散布于多個回調(diào)函數(shù)之中,相對不容易理解和維護(hù)。

三:多線程服務(wù)器的常用編程模型

大概有這么幾種:

a:每個請求創(chuàng)建一個線程,使用阻塞式IO操作。在Java 1.4引人NIO之前,這是Java網(wǎng)絡(luò)編程的推薦做法??上炜s性不佳(請求太多時,操作系統(tǒng)創(chuàng)建不了這許多線程)。

b:使用線程池,同樣使用阻塞式IO操作。與第1種相比,這是提高性能的措施。

c:使用non-blocking IO + IO multiplexing。即Java NIO的方式。

d:Leader/Follower等高級模式。

在默認(rèn)情況下,我會使用第3種,即non-blocking IO + one loop per thread模式來編寫多線程C++網(wǎng)絡(luò)服務(wù)程序。

1:one loop per thread

此種模型下,程序里的每個IO線程有一個event loop,用于處理讀寫和定時事件(無論周期性的還是單次的)。代碼框架跟“單線程服務(wù)器的常用編程模型”一節(jié)中的一樣。

libev的作者說:

One loop per thread is usually a good model. Doing this is almost never wrong, some times a better-performance model exists, but it is always a good start.

這種方式的好處是:

a:線程數(shù)目基本固定,可以在程序啟動的時候設(shè)置,不會頻繁創(chuàng)建與銷毀。

b:可以很方便地在線程間調(diào)配負(fù)載。

c:IO事件發(fā)生的線程是固定的,同一個TCP連接不必考慮事件并發(fā)。

Event loop代表了線程的主循環(huán),需要讓哪個線程干活,就把timer或IO channel(如TCP連接)注冊到哪個線程的loop里即可:對實(shí)時性有要求的connection可以單獨(dú)用一個線程;數(shù)據(jù)量大的connection可以獨(dú)占一個線程,并把數(shù)據(jù)處理任務(wù)分?jǐn)偟搅韼讉€計算線程中(用線程池);其他次要的輔助性connections可以共享一個線程。

比如,在dbproxy中,一個線程用于專門處理客戶端發(fā)來的管理命令;一個線程用于處理客戶端發(fā)來的MySQL命令,而與后端數(shù)據(jù)庫通信執(zhí)行該命令時,是將該任務(wù)分配給所有事件線程處理的。

對于non-trivial(有一定規(guī)模)的服務(wù)端程序,一般會采用non-blocking IO + IO multiplexing,每個connection/acceptor都會注冊到某個event loop上,程序里有多個event loop,每個線程至多有一個event loop。

多線程程序?qū)vent loop提出了更高的要求,那就是“線程安全”。要允許一個線程往別的線程的loop里塞東西,這個loop必須得是線程安全的。

在dbproxy中,線程向其他線程分發(fā)任務(wù),是通過管道和隊(duì)列實(shí)現(xiàn)的。比如主線程accept到連接后,將表示該連接的結(jié)構(gòu)放入隊(duì)列,并向管道中寫入一個字節(jié)。計算線程在自己的event loop中注冊管道的讀事件,一旦有數(shù)據(jù)可讀,就嘗試從隊(duì)列中取任務(wù)。

2:線程池

不過,對于沒有IO而光有計算任務(wù)的線程,使用event loop有點(diǎn)浪費(fèi)??梢允褂靡环N補(bǔ)充方案,即用blocking queue實(shí)現(xiàn)的任務(wù)隊(duì)列:

view plain copy

typedef boost::functionFunctor;

BlockingQueue taskQueue; //線程安全的全局阻塞隊(duì)列

//計算線程

void workerThread()

{

while (running) //running變量是個全局標(biāo)志

{

Functor task = taskQueue.take(); //this blocks

task(); //在產(chǎn)品代碼中需要考慮異常處理

}

}

// 創(chuàng)建容量(并發(fā)數(shù))為N的線程池

int N = num_of_computing_threads;

for (int i = 0; i task = boost::bind(&Foo::calc,&foo);

taskQueue.post(task);

除了任務(wù)隊(duì)列,還可以用BlockingQueue實(shí)現(xiàn)數(shù)據(jù)的生產(chǎn)者消費(fèi)者隊(duì)列,即T是數(shù)據(jù)類型而非函數(shù)對象,queue的消費(fèi)者從中拿到數(shù)據(jù)進(jìn)行處理。其實(shí)本質(zhì)上是一樣的。

3:總結(jié)

總結(jié)而言,我推薦的C++多線程服務(wù)端編程模式為:one (event) loop per thread + thread pool:

event loop用作IO multiplexing,配合non-blockingIO和定時器;

thread pool用來做計算,具體可以是任務(wù)隊(duì)列或生產(chǎn)者消費(fèi)者隊(duì)列。

以這種方式寫服務(wù)器程序,需要一個優(yōu)質(zhì)的基于Reactor模式的網(wǎng)絡(luò)庫來支撐,muduo正是這樣的網(wǎng)絡(luò)庫。比如dbproxy使用的是libevent。

程序里具體用幾個loop、線程池的大小等參數(shù)需要根據(jù)應(yīng)用來設(shè)定,基本的原則是“阻抗匹配”(解釋見下),使得CPU和IO都能高效地運(yùn)作。所謂阻抗匹配原則:

如果池中線程在執(zhí)行任務(wù)時,密集計算所占的時間比重為 P (0 就能立刻列出用到某服務(wù)的客戶端地址(Foreign Address列),然后在客戶端的機(jī)器上用netstat或lsof命令找出是哪個進(jìn)程發(fā)起的連接。TCP短連接和UDP則不具備這一特性。二是通過接收和發(fā)送隊(duì)列的長度也較容易定位網(wǎng)絡(luò)或程序故障。在正常運(yùn)行的時候,netstat打印的Recv-Q和Send-Q都應(yīng)該接近0,或者在0附近擺動。如果Recv-Q保持不變或持續(xù)增加,則通常意味著服務(wù)進(jìn)程的處理速度變慢,可能發(fā)生了死鎖或阻塞。如果Send-Q保持不變或持續(xù)增加,有可能是對方服務(wù)器太忙、來不及處理,也有可能是網(wǎng)絡(luò)中間某個路由器或交換機(jī)故障造成丟包,甚至對方服務(wù)器掉線,這些因素都可能表現(xiàn)為數(shù)據(jù)發(fā)送不出去。通過持續(xù)監(jiān)控Recv-Q和Send-Q就能及早預(yù)警性能或可用性故障。以下是服務(wù)端線程阻塞造成Recv-Q和客戶端Send-Q激增的例子:

view plain copy

$netstat -tn

Proto Recv-Q Send-Q Local Address Foreign

tcp.0.0.10:.0.0.10:#服務(wù)端連接

tcp.0.0.10:.0.0.10:#客戶端連接

tcp.0.0.10:.0.0.4:

五:多線程服務(wù)器的適用場合

如果要在一臺多核機(jī)器上提供一種服務(wù)或執(zhí)行一個任務(wù),可用的模式有:

a:運(yùn)行一個單線程的進(jìn)程;

b:運(yùn)行一個多線程的進(jìn)程;

c:運(yùn)行多個單線程的進(jìn)程;

d:運(yùn)行多個多線程的進(jìn)程;

考慮這樣的場景:如果使用速率為50MB/s的數(shù)據(jù)壓縮庫,進(jìn)程創(chuàng)建銷毀的開銷是800微秒,線程創(chuàng)建銷毀的開銷是50微秒。如何執(zhí)行壓縮任務(wù)?

如果要偶爾壓縮1GB的文本文件,預(yù)計運(yùn)行時間是20s,那么起一個進(jìn)程去做是合理的,因?yàn)檫M(jìn)程啟動和銷毀的開銷遠(yuǎn)遠(yuǎn)小于實(shí)際任務(wù)的耗時。

如果要經(jīng)常壓縮500kB的文本數(shù)據(jù),預(yù)計運(yùn)行時間是10ms,那么每次都起進(jìn)程 似乎有點(diǎn)浪費(fèi)了,可以每次單獨(dú)起一個線程去做。

如果要頻繁壓縮10kB的文本數(shù)據(jù),預(yù)計運(yùn)行時間是200微秒,那么每次起線程似 乎也很浪費(fèi),不如直接在當(dāng)前線程搞定。也可以用一個線程池,每次把壓縮任務(wù)交給線程池,避免阻塞當(dāng)前線程(特別要避免阻塞IO線程)。

由此可見,多線程并不是萬靈丹(silver bullet)。

1:必須使用單線程的場合

據(jù)我所知,有兩種場合必須使用單線程:

a:程序可能會fork(2);

實(shí)際編程中,應(yīng)該保證只有單線程程序能進(jìn)行fork(2)。多線程程序不是不能調(diào)用fork(2),而是這么做會遇到很多麻煩:

fork一般不能在多線程程序中調(diào)用,因?yàn)長inux的fork只克隆當(dāng)前線程的thread of control,不可隆其他線程。fork之后,除了當(dāng)前線程之外,其他線程都消失了。

這就造成一種危險的局面。其他線程可能正好處于臨界區(qū)之內(nèi),持有了某個鎖,而它突然死亡,再也沒有機(jī)會去解鎖了。此時如果子進(jìn)程試圖再對同一個mutex加鎖,就會立即死鎖。因此,fork之后,子進(jìn)程就相當(dāng)于處于signal handler之中(因?yàn)椴恢勒{(diào)用fork時,父進(jìn)程中的線程此時正在調(diào)用什么函數(shù),這和信號發(fā)生時的場景一樣),你不能調(diào)用線程安全的函數(shù)(除非它是可重入的),而只能調(diào)用異步信號安全的函數(shù)。比如,fork之后,子進(jìn)程不能調(diào)用:

malloc,因?yàn)閙alloc在訪問全局狀態(tài)時幾乎肯定會加鎖;

任何可能分配或釋放內(nèi)存的函數(shù),比如snprintf;

任何Pthreads函數(shù);

printf系列函數(shù),因?yàn)槠渌€程可能恰好持有stdout/stderr的鎖;

除了man 7 signal中明確列出的信號安全函數(shù)之外的任何函數(shù)。

因此,多線程中調(diào)用fork,唯一安全的做法是fork之后,立即調(diào)用exec執(zhí)行另一個程序,徹底隔斷子進(jìn)程與父進(jìn)程的聯(lián)系。

在多線程環(huán)境中調(diào)用fork,產(chǎn)生子進(jìn)程后。子進(jìn)程內(nèi)部只存在一個線程,也就是父進(jìn)程中調(diào)用fork的線程的副本。

使用fork創(chuàng)建子進(jìn)程時,子進(jìn)程通過繼承整個地址空間的副本,也從父進(jìn)程那里繼承了所有互斥量、讀寫鎖和條件變量的狀態(tài)。如果父進(jìn)程中的某個線程占有鎖,則子進(jìn)程同樣占有這些鎖。問題是子進(jìn)程并不包含占有鎖的線程的副本,所以子進(jìn)程沒有辦法知道它占有了哪些鎖,并且需要釋放哪些鎖。

盡管Pthread提供了pthread_atfork函數(shù)試圖繞過這樣的問題,但是這回使得代碼變得混亂。因此《Programming With Posix Threads》一書的作者說:”Avoid using fork in threaded code except where the child process will immediately exec a new program.”。

b:限制程序的CPU占用率;

這個很容易理解,比如在一個8核的服務(wù)器上,一個單線程程序即便發(fā)生busy-wait,占滿1個core,其CPU使用率也只有12.5%,在這種最壞的情況下,系統(tǒng)還是有87.5%的計算資源可供其他服務(wù)進(jìn)程使用。

關(guān)于linux 1wire編程的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。

創(chuàng)新互聯(lián)是成都專業(yè)網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁設(shè)計、SEO優(yōu)化、手機(jī)網(wǎng)站、小程序開發(fā)、APP開發(fā)公司等,多年經(jīng)驗(yàn)沉淀,立志成為成都網(wǎng)站建設(shè)第一品牌!


網(wǎng)站名稱:深入了解Linux1Wire編程,開創(chuàng)智能家居新篇章(linux1wire編程)
網(wǎng)頁網(wǎng)址:http://www.dlmjj.cn/article/ccsghch.html