pos機跳轉商戶的原理

 新聞資訊3  |   2023-09-12 10:03  |  投稿人:pos機之家

網上有很多關于pos機跳轉商戶的原理,從服務器開發(fā)底層聊一聊協(xié)程的實現原理的知識,也有很多人為大家解答關于pos機跳轉商戶的原理的問題,今天pos機之家(www.dsth100338.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!

本文目錄一覽:

1、pos機跳轉商戶的原理

pos機跳轉商戶的原理

一、先介紹一組概念

進程

進程是系統(tǒng)進行資源分配和調度的基本單位進程是一個實體,每一個進程都有自己地址空間

線程

線程是程序執(zhí)行流的最小單元一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成線程是進程中的一個實體,是被系統(tǒng)獨立調度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資源

子例程

子例程是某個主程序的一部分代碼子例程又被稱為子程序、過程、方法、函數等。在主程序中可以調用子例程來執(zhí)行

協(xié)程

協(xié)程與子例程一樣,協(xié)程(coroutine)也是一種程序組件協(xié)程看上去也是子程序,但執(zhí)行過程中,在子程序內部可中斷,然后轉而執(zhí)行別的子程序,在適當的時候再返回來接著執(zhí)行注意,在一個子程序中中斷,去執(zhí)行其他子程序,不是函數調用,有點類似CPU的中斷二、同步與異步在介紹協(xié)程之前,必須先要詳細的介紹一下同步和異步,因為這是引出協(xié)程的重要原因

同步

以客戶端為例,客戶端向服務端發(fā)出請求之后,必須阻塞等待服務端給自己返回數據,屬于一問一答的模式下面是服務端同步的偽代碼,核心是handle()函數:服務端在接收到消息之后,必須阻塞等待處理完消息再去發(fā)送數據,這個過程必須是一次性完成的,并且是在一個線程中完成的

while (1) { int nready = epoll_wait(epfd, events, EVENT_SIZE, -1); for (i = 0;i < nready;i ++) { int sockfd = events[i].data.fd; if (sockfd == listenfd) { int connfd = accept(listenfd, xxx, xxxx); setnonblock(connfd); ev.events = EPOLLIN | EPOLLET; ev.data.fd = connfd; epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); } // 進行消息的處理 else { handle(sockfd); } }} // 可以看到是數據的收發(fā)在一個過程中int handle(int sockfd) { // 先接收 recv(sockfd, rbuffer, length, 0); // 消息處理 parser_proto(rbuffer, length); // 再發(fā)送 send(sockfd, sbuffer, length, 0);}

異步

異步與同步不同,在異步的情況下,客戶端向服務端發(fā)出請求之后,客戶端可以不等待服務端給自己返回數據,接著繼續(xù)執(zhí)行。當然接收回復這一過程在其他線程中執(zhí)行,當回復達到之后再通知客戶端去接收(一般由回調函數實現)下面是服務端同步的偽代碼,核心是handle()函數:可以看到服務端在處理數據時,把任務交給了其他的線程去執(zhí)行,而不必阻塞等待

while (1) { int nready = epoll_wait(epfd, events, EVENT_SIZE, -1); for (i = 0;i < nready;i ++) { int sockfd = events[i].data.fd; if (sockfd == listenfd) { int connfd = accept(listenfd, xxx, xxxx); setnonblock(connfd); ev.events = EPOLLIN | EPOLLET; ev.data.fd = connfd; epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); } // 進行消息的處理 else { handle(sockfd); } }} // 此函數是在線程池創(chuàng)建的線程中運行int thread_cb(int sockfd) { recv(sockfd, rbuffer, length, 0); parser_proto(rbuffer, length); send(sockfd, sbuffer, length, 0);} // 此函數是在主線程中運行的int handle(int sockfd) { // 把讀寫任務放到另外的線程中去執(zhí)行 push_thread(sockfd, thread_cb); //將 sockfd 放到其他線程中運行。}Linuxc/c++后臺服務器開發(fā)高階視頻學習資料后臺私信【架構】獲取

備注

"異步IO"和"IO異步"的區(qū)別:異步IO:核心點是"IO",例如POSXI提供的AIO接口IO異步:核心點是"操作",例如上面服務端代碼的異步處理,就是IO異步操作。我們平常所說的"多線程異步"指的就是這個"同步IO"和"IO同步"與上面也是相同的道理

同步與異步的區(qū)別

總結起來就是:同步:編程簡單,性能差異步:編程復雜,性能高同步編程簡單是因為其數據的處理是在同一個過程中進行處理的。但是異步的處理可能在不同的線程中進行處理,例如在異步的情況下客戶端發(fā)送2次請求,這2個請求可能在服務端的2個線程中進行處理,2個線程共用一個客戶端的fd,可能造成數據混亂,最常見的解決方法就是進行加鎖四、設計協(xié)程的核心通過上面的介紹,我們知道了同步與異步之間的差異,有沒有一種方式,有異步性能,同步的代碼邏輯。來方便編程人員對 IO 操作的 組件呢? 有,采用一種輕量級的協(xié)程來實現。在每次 send 或者 recv 之前進行 切換,再由調度器來處理 epoll_wait 的流程而協(xié)程的設計核心目標就是:為了擁有同步的簡單編程方式,同時又想要擁有異步的性能五、實現協(xié)程的核心:跳轉(協(xié)程切換)協(xié)程想要擁有同步的編程方式和異步的性能,因此我們不能對同步的代碼進行修改,而要想辦法對異步的代碼進行修改,使得其下面我們以https://blog.csdn.net/qq_41453285/article/details/106357786中的HTTP客戶端異步實現代碼為例下面且聽我細細道來

如何跳轉?往哪里跳轉?

在代碼中,客戶端調用async_http_commit()函數向服務端發(fā)送一個HTTP請求,為了實現異步的方式,我們在調用send()發(fā)送數據之后,把這個fd添加到epoll中進行管理(這是異步的方式),而不是在send()后面調用recv()(這是同步的方式)下面是async_http_thread_func()函數的代碼,這個函數在while(1)循環(huán)中一直調用epoll_wait()監(jiān)聽描述符是否有事件發(fā)生,例如上面的async_http_commit()函數調用send()給服務端發(fā)送數據之后,服務端給客戶端回送響應,那么epoll_wait()就會被觸發(fā),從而調用recv()接收數據那么如何用異步代碼實現同步的效果呢?那就需要程序進行跳轉在關于跳轉可以詳細見下面的解釋(圖片點開來看):(圖左側)async_http_commit()函數接收完數據之后為了實現與同步一樣的效果,我們跳轉到右側的async_http_thread_func()函數(圖右側)跳轉到async_http_thread_func()函數之后就可以調用epoll_wait()來檢測數據了(因為上面的async_http_commit()函數調用了send()發(fā)送數據了),如果epoll_wait()檢測到有數據來之后就可以在下面接收數據,當接收完數據之后再跳轉回到左側的async_http_commit()函數中

跳轉的方法有哪些呢?

程序跳轉的方法有3種:①setjmp()、longjmp()函數:不推薦使用,編碼復雜②ucontext③匯編實現這3種方法,其中最常用的就是匯編,并且Go等語言的協(xié)程也是用匯編實現的

yield、resume

yield:讓出當前的CPU,跳轉到指定的位置進行執(zhí)行,這個過程叫做yieldresume:上面yield讓出CPU之后跳轉到指定的位置執(zhí)行,當指定的位置執(zhí)行完成之后,回到當初的位置這個過程叫做resume例如,對于上面來說,就是六、如何通過匯編實現協(xié)程的切換呢?下載代碼https://github.com/wangbojing/NtyCo開始解析,后面要用到對于上面的跳轉來說,其實就是協(xié)程之間的切換,如何實現這種切換呢?在介紹協(xié)程切換之前,來說一下線程的切換,如下圖所示:CPU有很多的寄存器,這些寄存器保存了當先在處理器上運行線程的信息例如當前CPU運行的是A線程,那么寄存器保存的都是線程A的信息當此時需要把線程A切出CPU,來讓B線程在CPU上運行,那么就需要把當前寄存器的內容都保存在線程A的棧中,然后把B運行所需要的內容加載到寄存器中,從而使得B線程在CPU上運行起來協(xié)程如何切換?與線程是相同的道理,還是以上面的圖片為例:sync_http_commit()函數調用send()之后,在跳轉到pos位置之前把當前寄存器的內容保存到一個結構體中(例如命名為store結構體)跳轉到async_http_thread_func()函數指定的pos位置之后,把pos位置的內容信息加載到寄存器中開始執(zhí)行

如何實現這些內容的保存與切換

首先到代碼的Nty_coroutine.c文件中找到_switch()函數,這個函數是實現切換的核心:參數1:新的上下文參數2:當前的上下文_switch()函數就是把當前寄存器的內容保存在參數2中,然后加載參數1所指定的內容加載到寄存器中例如,如果是上面的sync_http_commit()函數,其在"jump-pos"的時候就調用_switch()進行切換,然后async_http_thread_func()函數執(zhí)行完需要切換回sync_http_commit()函數的時候,會在"jump->back"的地方調用這個函數,只是參數不同而已nty_cpu_ctx結構體就是一些寄存器的指針_switch()函數可以用下面的圖來表示那么如何實現這些寄存器值的保存與交換呢?以下面為例,自己看圖片吧,稍微有點復雜_switch()函數的參數1名為rdi、參數2名為rsi在左側,前一半部分:匯編指令把寄存器的內容保存到rsi中,留下次跳轉回來使用;后一半部分:把rdi的內容加載到寄存器中開始使用0、8、16那些是偏移,因為一個指針就是8字節(jié),所以rsp對應的是esp、rbp對應的是ebp......依次類推X86-64有16個64位寄存器,分別是:%rax:作為函數返回值使用%rsp:棧指針寄存器,指向棧頂%rdi,%rsi,%rdx,%rcx,%r8,%r9:用作函數參數,依次對應第1參數,第2參數......依次類推(例如在_switch()函數中,參數1叫做rdi、參數2叫做rsi......)%rbx,%rbp,%r12,%r13,,:用作數據存儲,遵循被調用者使用規(guī)則,簡單說就是隨便 用,調用子函數之前要備份它,以防他被修改 %r10,%r11 用作數據存儲,遵循調用者使用規(guī)則,簡單說就是使用之前要先保存原值

以上就是關于pos機跳轉商戶的原理,從服務器開發(fā)底層聊一聊協(xié)程的實現原理的知識,后面我們會繼續(xù)為大家整理關于pos機跳轉商戶的原理的知識,希望能夠幫助到大家!

轉發(fā)請帶上網址:http://www.dsth100338.com/newstwo/111398.html

你可能會喜歡:

版權聲明:本文內容由互聯(lián)網用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發(fā)現本站有涉嫌抄襲侵權/違法違規(guī)的內容, 請發(fā)送郵件至 babsan@163.com 舉報,一經查實,本站將立刻刪除。