linux進程間通信快速入門【二】:共享內存編程(mmap、XSI、POSIX)

文章目錄

  • mmap內存共享映射
  • XSI共享內存
  • POSIX共享內存
  • 參考

使用文件或管道進行進程間通信會有很多局限性,比如效率問題以及數據處理使用文件描述符而不如內存地址訪問方便,于是多個進程以共享內存的方式進行通信就成了很自然要實現的IPC方案。
LInux給我們提供了三種共享內存的解決方案:

mmap內存共享映射。
XSI共享內存。
POSIX共享內存。

mmap內存共享映射

mmap可以將一個文件映射到內存中,在程序里就可以直接使用內存地址對文件內容進行訪問,這可以讓程序對文件訪問更方便。
API:

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);

Linux產生子進程的系統調用是fork,根據fork的語義以及其實現,我們知道新產生的進程在內存地址空間上跟父進程是完全一致的。所以Linux的mmap實現了一種可以在父子進程之間共享內存地址的方式,其使用方法是:
step1:父進程將flags參數設置MAP_SHARED方式通過mmap申請一段內存。內存可以映射某個具體文件,也可以不映射具體文件(fd置為-1,flag設置為MAP_ANONYMOUS)。
step2:父進程調用fork產生子進程。之后在父子進程內都可以訪問到mmap所返回的地址,就可以共享內存了。
示例:并發100個進程寫共享內存

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>#define COUNT 100int do_child(int *count)
{int interval;/* critical section */interval = *count;interval++;usleep(1);*count = interval;/* critical section */exit(0);
}int main()
{pid_t pid;int count;int *shm_p;// 開辟一個int大小的共享內存 可讀可寫 Share changes Don't use a file.shm_p = (int *)mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, -1, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}// 對該共享內存內容清零*shm_p = 0;// fork子進程,在子進程進行取數、++、置數操作for (count=0;count<COUNT;count++) {pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {do_child(shm_p);}}// 等待所有子進程生命周期結束for (count=0;count<COUNT;count++) {wait(NULL);}// 打印內容printf("shm_p: %d\n", *shm_p);// 回收共享內存munmap(shm_p, sizeof(int));exit(0);
}

這個例子中,我們在子進程中為了延長臨界區(critical section)處理的時間,使用了一個中間變量進行數值交換,并且還使用了usleep加強了一下racing的效果。結果如下:

[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# g++ ./racing_mmap.cpp -o racing_mmap
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./racing_mmap 
shm_p: 37
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./racing_mmap 
shm_p: 44
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./racing_mmap 
shm_p: 41

這段共享內存的使用是有競爭條件存在的,從文件鎖的例子我們知道,進程間通信絕不僅僅是通信這么簡單,還需要處理類似這樣的臨界區代碼。在這里,我們也可以使用文件鎖進行處理,但是共享內存使用文件鎖未免顯得太不協調了。除了不方便以及效率低下以外,文件鎖還不能夠進行更高級的進程控制。所以,我們在此需要引入更高級的進程同步控制原語來實現相關功能,這就是信號量(semaphore)的作用。這里信號量不是重點,將在后面的系列文章中進行探討。

應該注意,mmap方式的共享內存只能在通過fork產生的父子進程間通信,因為除此之外的其它進程無法得到共享內存段的地址
接下來再看看mmap開辟的內存位于哪里吧:

/** @Author: your name* @Date: 2022-03-17 19:00:57* @LastEditTime: 2022-03-17 19:00:58* @LastEditors: Please set LastEditors* @Description: 打開koroFileHeader查看配置 進行設置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE* @FilePath: /SocketTest/LocalSocketDemo/mmap.cpp*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>#define COUNT 100
#define MEMSIZE 1024*1024*1023*1int main()
{pid_t pid;int count;void *shm_p;shm_p = mmap(NULL, MEMSIZE, PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, -1, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}bzero(shm_p, MEMSIZE);sleep(3000);munmap(shm_p, MEMSIZE);exit(0);
}

結果如下:

[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# g++ ./mmap.cpp -o mmap
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# free -gtotal        used        free      shared  buff/cache   available
Mem:             15           8           3           0           2           6
Swap:             0           0           0
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# ./mmap &
[1] 23994
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# free -gtotal        used        free      shared  buff/cache   available
Mem:             15           8           2           1           3           5
Swap:             0           0           0

我們開辟了一個G內存,Centos的環境中mmap的共享內存會記錄到buff/cache中。

XSI共享內存

為了滿足多個無關進程共享內存的需求,Linux提供了更具通用性的共享內存手段,XSI共享內存就是這樣一種實現。
XSI是X/Open組織對UNIX定義的一套接口標準(X/Open System Interface)。由于UNIX系統的歷史悠久,在不同時間點的不同廠商和標準化組織定義過一些列標準,而目前比較通用的標準實際上是POSIX。我們還會經常遇到的標準還包括SUS(Single UNIX Specification)標準,它們大概的關系是,SUS是POSIX標準的超集,定義了部分額外附加的接口,這些接口擴展了基本的POSIX規范。相應的系統接口全集被稱為XSI標準,除此之外XSI還定義了實現必須支持的POSIX的哪些可選部分才能認為是遵循XSI的。它們包括文件同步,存儲映射文件,存儲保護及線程接口。只有遵循XSI標準的實現才能稱為UNIX操作系統。
XSI共享內存在Linux底層的實現實際上跟mmap沒有什么本質不同,只是在使用方法上有所區別。其使用的相關方法為:

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);int shmctl(int shmid, int cmd, struct shmid_ds *buf);#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);int shmdt(const void *shmaddr);

在一個操作系統內,如何讓兩個不相關(沒有父子關系)的進程可以共享一個內存段?系統中是否有現成的解決方案呢?
當然有,就是文件。我們知道,文件的設計就可以讓無關的進程可以進行數據交換。文件采用路徑和文件名作為系統全局的一個標識符,但是每個進程打開這個文件之后,在進程內部都有一個“文件描述符”去指向文件。此時進程通過fork打開的子進程可以繼承父進程的文件描述符,但是無關進程依然可以通過系統全局的文件名用open系統調用再次打開同一個文件,以便進行進程間通信。
實際上對于XSI的共享內存,其key的作用就類似文件的文件名,shmget返回的int類型的shmid就類似文件描述符,注意只是“類似”,而并非是同樣的實現。這意味著,我們在進程中不能用select、poll、epoll這樣的方法去控制一個XSI共享內存,因為它并不是“文件描述符”。對于一個XSI的共享內存,其key是系統全局唯一的,這就方便其他進程使用同樣的key,打開同樣一段共享內存,以便進行進程間通信。而使用fork產生的子進程,則可以直接通過shmid訪問到相關共享內存段。這就是key的本質:系統中對XSI共享內存的全局唯一表示符。
那么key是如何產生的呢?

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

一個key是通過ftok函數,使用一個pathname和一個proj_jd產生的。就是說,在一個可能會使用共享內存的項目組中,大家可以約定一個文件名和一個項目的proj_id,來在同一個系統中確定一段共享內存的key。ftok并不會去創建文件,所以必須指定一個存在并且進程可以訪問的pathname路徑。這里還要指出的一點是,ftok實際上并不是根據文件的文件路徑和文件名(pathname)產生key的,在實現上,它使用的是指定文件的inode編號和文件所在設備的設備編號。所以,不要以為你是用了不同的文件名就一定會得到不同的key,因為不同的文件名是可以指向相同inode編號的文件的(硬連接)。也不要認為你是用了相同的文件名就一定可以得到相同的key,在一個系統上,同一個文件名會被刪除重建的幾率是很大的,這種行為很有可能導致文件的inode變化。所以一個ftok的執行會隱含stat系統調用也就不難理解了。
key作為全局唯一標識不僅僅體現在XSI的共享內存中,XSI標準的其他進程間通信機制(信號量數組和消息隊列)也使用這一命名方式。
示例:多進程并發寫,會有競爭

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>#define COUNT 100
#define PATHNAME "/etc/passwd"int do_child(int proj_id)
{int interval;int *shm_p, shm_id;key_t shm_key;/* 使用ftok產生shmkey */if ((shm_key = ftok(PATHNAME, proj_id)) == -1) {perror("ftok()");exit(1);}/* 在子進程中使用shmget取到已經在父進程中創建好的共享內存id,注意shmget的第三個參數的使用。 */shm_id = shmget(shm_key, sizeof(int), 0);if (shm_id < 0) {perror("shmget()");exit(1);}/* 使用shmat將相關共享內存段映射到本進程的內存地址。 */shm_p = (int *)shmat(shm_id, NULL, 0);if ((void *)shm_p == (void *)-1) {perror("shmat()");exit(1);}/* critical section */interval = *shm_p;interval++;usleep(1);*shm_p = interval;/* critical section *//* 使用shmdt解除本進程內對共享內存的地址映射,本操作不會刪除共享內存。 */if (shmdt(shm_p) < 0) {perror("shmdt()");exit(1);}exit(0);
}int main()
{pid_t pid;int count;int *shm_p;int shm_id, proj_id;key_t shm_key;proj_id = 1234;/* 使用約定好的文件路徑和proj_id產生shm_key。 */if ((shm_key = ftok(PATHNAME, proj_id)) == -1) {perror("ftok()");exit(1);}/* 使用shm_key創建一個共享內存,如果系統中已經存在此共享內存則報錯退出,創建出來的共享內存權限為0600。 */shm_id = shmget(shm_key, sizeof(int), IPC_CREAT|IPC_EXCL|0600);if (shm_id < 0) {perror("shmget()");exit(1);}/* 將創建好的共享內存映射進父進程的地址以便訪問。 */shm_p = (int *)shmat(shm_id, NULL, 0);if ((void *)shm_p == (void *)-1) {perror("shmat()");exit(1);}/* 共享內存賦值為0。 */*shm_p = 0;/*  打開100個子進程并發讀寫共享內存。 */for (count=0;count<COUNT;count++) {pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {do_child(proj_id);}}/* 等待所有子進程執行完畢。 */for (count=0;count<COUNT;count++) {wait(NULL);}/* 顯示當前共享內存的值。 */printf("shm_p: %d\n", *shm_p);/* 解除共享內存地質映射。 */if (shmdt(shm_p) < 0) {perror("shmdt()");exit(1);}/* 刪除共享內存。 */if (shmctl(shm_id, IPC_RMID, NULL) < 0) {perror("shmctl()");exit(1);}exit(0);
}

XSI共享內存跟mmap在實現上并沒有本質區別。而之所以引入key和shmid的概念,也主要是為了在非父子關系的進程之間可以共享內存。根據上面的例子可以看到,使用shmget可以根據key創建共享內存,并返回一個shmid。它的第二個參數size用來指定共享內存段的長度,第三個參數指定創建的標志,可以支持的標志為:IPC_CREAT、IPC_EXCL。從Linux 2.6之后,還引入了支持大頁的共享內存,標志為:SHM_HUGETLB、SHM_HUGE_2MB等參數。shmget除了可以創建一個新的共享內存以外,還可以訪問一個已經存在的共享內存,此時可以將shmflg置為0,不加任何標識打開

當獲得shmid之后,就可以使用shmat來進行地址映射。shmat之后,通過訪問返回的當前進程的虛擬地址就可以訪問到共享內存段了。當然,在使用之后要記得使用shmdt解除映射,否則對于長期運行的程序可能造成虛擬內存地址泄漏,導致沒有可用地址可用。shmdt并不能刪除共享內存段,而只是解除共享內存和進程虛擬地址的映射,只要shmid對應的共享內存還存在,就仍然可以繼續使用shmat映射使用。想要刪除一個共享內存需要使用shmctlIPC_RMID指令處理。也可以在命令行中使用ipcrm刪除指定的共享內存id或key。
注意點:

共享內存由于其特性,與進程中的其他內存段在使用習慣上有些不同。一般進程對棧空間分配可以自動回收,而堆空間通過malloc申請,free回收。這些內存在回收之后就可以認為是不存在了。但是共享內存不同,用shmdt之后,實際上其占用的內存還在,并仍然可以使用shmat映射使用。如果不是用shmctl或ipcrm命令刪除的話,那么它將一直保留直到系統被關閉。當然,文件如果不刪除,下次重啟依舊還在,因為它放在硬盤上,而共享內存下次重啟就沒了,因為它畢竟還是內存。

跟mmap的共享內存一樣,XSI的共享內存在free現實中也會占用shared和buff/cache的消耗。實際上,在內核底層實現上,兩種內存共享都是使用的tmpfs方式實現的,所以它們實際上的內存使用都是一致的。

POSIX共享內存

XSI共享內存是歷史比較悠久,也比較經典的共享內存手段。它幾乎代表了共享內存的默認定義,當我們說有共享內存的時候,一般意味著使用了XSI的共享內存。但是這種共享內存也存在一切缺點,最受病垢的地方莫過于他提供的key+projid的命名方式不夠UNIX,沒有遵循一切皆文件的設計理念。
如果共享內存可以用文件描述符的方式提供給程序訪問,毫無疑問可以在Linux上跟select、poll、epoll這樣的IO異步事件驅動機制配合使用,做到一些更高級的功能。于是,遵循一切皆文件理念的POSIX標準的進程間通信機制應運而生。

POSIX共享內存實際上毫無新意,它本質上就是mmap對文件的共享方式映射,只不過映射的是tmpfs文件系統上的文件。

什么是tmpfs?Linux提供一種“臨時”文件系統叫做tmpfs,它可以將內存的一部分空間拿來當做文件系統使用,使內存空間可以當做目錄文件來用。Linux提供的POSIX共享內存,實際上就是在/dev/shm下創建一個文件,并將其mmap之后映射其內存地址即可。我們通過它給定的一套參數就能猜到它的主要函數shm_open無非就是open系統調用的一個封裝。大家可以通過man shm_overview來查看相關操作的方法。
POSIX共享內存的使用相關方法如下:

#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */int shm_open(const char *name, int oflag, mode_t mode);int shm_unlink(const char *name);

使用shm_open可以創建或者訪問一個已經創建的共享內存。上面說過,實際上POSIX共享內存就是在/dev/shm目錄中的的一個tmpfs格式的文件,所以shm_open無非就是open系統調用的封裝,所以起函數使用的參數幾乎一樣。其返回的也是一個標準的我呢間描述符。

shm_unlink也一樣是unlink調用的封裝,用來刪除文件名和文件的映射關系。在這就能看出POSIX共享內存和XSI的區別了,一個是使用文件名作為全局標識,另一個是使用key。

映射共享內存地址使用mmap,解除映射使用munmap。使用ftruncate設置共享內存大小,實際上就是對tmpfs的文件進行指定長度的截斷。使用fchmod、fchown、fstat等系統調用修改和查看相關共享內存的屬性。close調用關閉共享內存的描述符。實際上,這都是標準的文件操作。
下面看一下具體示例:
示例:多進程讀寫,有競爭

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <wait.h>
#include <sys/mman.h>#define COUNT 100
#define SHMPATH "shm"int do_child(char * shmpath)
{int interval, shmfd, ret;int *shm_p;/* 使用shm_open訪問一個已經創建的POSIX共享內存 */shmfd = shm_open(shmpath, O_RDWR, 0600);if (shmfd < 0) {perror("shm_open()");exit(1);}/* 使用mmap將對應的tmpfs文件映射到本進程內存 */shm_p = (int *)mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, shmfd, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}/* critical section */interval = *shm_p;interval++;usleep(1);*shm_p = interval;/* critical section */munmap(shm_p, sizeof(int));close(shmfd);exit(0);
}int main()
{pid_t pid;int count, shmfd, ret;int *shm_p;/* 創建一個POSIX共享內存 */shmfd = shm_open(SHMPATH, O_RDWR|O_CREAT|O_TRUNC, 0600);if (shmfd < 0) {perror("shm_open()");exit(1);}/* 使用ftruncate設置共享內存段大小 */ret = ftruncate(shmfd, sizeof(int));if (ret < 0) {perror("ftruncate()");exit(1);}/* 使用mmap將對應的tmpfs文件映射到本進程內存 */shm_p = (int *)mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, shmfd, 0);if (MAP_FAILED == shm_p) {perror("mmap()");exit(1);}*shm_p = 0;for (count=0;count<COUNT;count++) {pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {do_child(SHMPATH);}}for (count=0;count<COUNT;count++) {wait(NULL);}printf("shm_p: %d\n", *shm_p);munmap(shm_p, sizeof(int));close(shmfd);//sleep(3000);shm_unlink(SHMPATH);exit(0);
}
[root@VM-90-225-centos /home/hanhan/SocketTest/LocalSocketDemo]# g++ ./racing_posix_shm.cpp -o racing_posix_shm
./racing_posix_shm.cpp: In function ‘int main()’:
./racing_posix_shm.cpp:80:20: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]do_child(SHMPATH);^
/tmp/ccduro4X.o: In function `do_child(char*)':
racing_posix_shm.cpp:(.text+0x1e): undefined reference to `shm_open'
/tmp/ccduro4X.o: In function `main':
racing_posix_shm.cpp:(.text+0xe0): undefined reference to `shm_open'
racing_posix_shm.cpp:(.text+0x215): undefined reference to `shm_unlink'
collect2: error: ld returned 1 exit status

編譯執行這個程序需要指定一個額外rt的庫,可以使用如下命令進行編譯:

g++ ./racing_posix_shm.cpp -lrt -o racing_posix_shm

編譯好可以看到,正好是在編譯好之后,dev/shm文件路徑被創建

root@VM-90-225-centos /dev]
...
drwxrwxrwt  2 root root          40 Mar 18 15:17 shm
...

解釋:

shm_open的SHMPATH參數是一個路徑,這個路徑默認放在系統的/dev/shm目錄下。這是shm_open已經封裝好的,保證了文件一定會使用tmpfs。
shm_open實際上就是open系統調用的封裝。我們當然完全可以使用open的方式模擬這個方法。
使用ftruncate方法來設置“共享內存”的大小。其實就是更改文件的長度。
要以共享方式做mmap映射,并且指定文件描述符為shmfd。
shm_unlink實際上就是unlink系統調用的封裝。如果不做unlink操作,那么文件會一直存在于/dev/shm目錄下,以供其它進程使用。
關閉共享內存描述符直接使用close。

其本質上就是個tmpfs文件。那么從這個角度說,mmap匿名共享內存、XSI共享內存和POSIX共享內存在內核實現本質上其實都是tmpfs。如果我們去查看POSIX共享內存的free空間占用的話,結果將跟mmap和XSI共享內存一樣占用shared和buff/cache.

參考

https://zorrozou.github.io/docs/books/linuxjin-cheng-jian-tong-4fe1-gong-xiang-nei-cun.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376621.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376621.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376621.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ROBOTS.TXT屏蔽筆記、代碼、示例大全

自己網站的ROBOTS.TXT屏蔽的記錄&#xff0c;以及一些代碼和示例&#xff1a; 屏蔽后臺目錄&#xff0c;為了安全&#xff0c;做雙層管理后臺目錄/a/xxxx/&#xff0c;蜘蛛屏蔽/a/&#xff0c;既不透露后臺路徑&#xff0c;也屏蔽蜘蛛爬后臺目錄 緩存&#xff0c;阻止蜘蛛爬靜態…

五大主流瀏覽器 HTML5 和 CSS3 兼容性比較

轉眼又已過去了一年&#xff0c;在這一年里&#xff0c;Firefox 和 Chrome 在拼升級&#xff0c;版本號不斷飆升&#xff1b;IE10 隨著 Windows 8 在去年10月底正式發布&#xff0c;在 JavaScript 性能和對 HTML5 和 CSS3 的支持方面讓人眼前一亮。這篇文章給大家帶來《五大主流…

Ubuntu下將Sublime Text設置為默認編輯器

轉自將Sublime Text 2設置為默認編輯器 修改defaults.list 編輯/etc/gnome/default.list文件&#xff0c;將其中的所有gedit.desktop替換為sublime_text.desktop。 sublime_text.desktop在/opt/sublime_text目錄下&#xff0c;使用ls -al *sublime*命令查看具體文件名。 轉載于…

python獲取最近N天工作日列表、節假日列表

# 獲取最近兩周工作日列表、節假日列表 import datetime import chinese_calendar import time import pandas as pd# 將時間戳轉換成格式化日期 def timestamp_to_str(timestampNone, format%Y-%m-%d %H:%M:%S):if timestamp:time_tuple time.localtime(timestamp) # 把時間…

保存頁面的瀏覽記錄

我的設計思想是將用戶的瀏覽記錄保存到cookie里面&#xff0c;然后根據情況處理。cookie里面的數據格式是json格式&#xff0c;方便根據自己的需要添加或者修改屬性。引用了3個js文件,下載地址如下。 https://github.com/carhartl/jquery-cookie/blob/master/jquery.cookie.js …

開竅小老虎,一步一個腳印之 初識匯編(一)

最近一直浸淫在計算機編程中無法自拔。哲學 認識論中講過。人類的求知的過程是由兩次飛躍。第一是從感性認識到理性認識&#xff1b;第二是從理性認識到實踐。這段話對有些人是適用的。我就是其中的一名。在知乎上求助問題“學計算機要懂匯編嗎&#xff1f;”&#xff0c;地下有…

python腳本 請求數量達到上限,http請求重試

由于在內網發送http請求同一個token會限制次數&#xff0c;所以很容易達到網關流量上限。 業務中使用了多線程并發&#xff0c;一個線程發起一次http請求&#xff0c;得到正確結果后返回。這里采用的策略是&#xff0c;如果解析出來達到流量上限&#xff0c;那么該線程休眠一段…

shell 字符串操作

string"abcABC123ABCabc" 字符串長度: echo ${#string} #15 echo expr length $string #15 索引 用法&#xff1a;expr index $string $substring expr index $string "ABC" #4 提取子串 用法&#xff1a;${string:position} echo ${string:3} #A…

Linux 之目錄 -鳥哥的Linux私房菜

因為利用 Linux 來開發產品或 distributions 的社群/公司與個人實在太多了, 如果每個人都用自己的想 法來配置檔案放置的目錄,那么將可能造成很多管理上的困擾。 你能想象,你進入一個企業之后,所 接觸到的 Linux 目錄配置方法竟然跟你以前學的完全不同嗎? 很難想象吧~所以,后來…

python腳本:向表中插入新數據,刪除表中最舊的數據

一張表存儲歷史數據&#xff0c;最多存儲HISTORY_TABLE_MAX_ROWS條數據&#xff0c;當表中數據未達到HISTORY_TABLE_MAX_ROWS&#xff0c;直接插入&#xff1b;如果達到的話需要保證插入新數據的時候將最舊的數據刪除 這里使用先update最新數據&#xff0c;然后再重新update全表…

精通 VC++ 實效編程280例 - 02 菜單和光標

菜單和關閉時重要的 Windows 資源之一。SDK 中&#xff0c;用 HCURSOR 和 HMENU 分別表示菜單和光標的句柄。MFC 中&#xff0c;CMenu 類封裝了菜單的功能。 23 動態添加和刪除菜單項 添加菜單項可以調用 CMenu::AppendMenu 或 CMenu::InserMenu 函數&#xff0c;刪除菜單項可以…

POJ 1860: Currency Exchange 【SPFA】

套匯問題&#xff0c;從源點做SPFA&#xff0c;如果有一個點入隊次數大于v次&#xff08;v表示點的個數&#xff09;則圖中存在負權回路&#xff0c;能夠套匯&#xff0c;如果不存在負權回路&#xff0c;則判斷下源點到自身的最長路是否大于自身&#xff0c;使用SPFA時松弛操作…

python腳本:判斷字符是否為中文

# 判斷字符是否為中文 def is_chinese(ch):if u\u4e00 < ch < u\u9fff:return Trueelse:return False

Android 廣播 Broadcast學習

Android Broadcast 廣播 進程內本地廣播 如果你是在你的應用之內使用廣播&#xff0c;即不需要跨進程&#xff0c;考慮使用LocalBroadcastManager &#xff0c;這樣更有效率&#xff08;因為不需要跨進程通信&#xff09;&#xff0c;并且你不用考慮一些其他應用可以發送或接收…

python:將時間戳轉換成格式化日期

import time # 將時間戳轉換成格式化日期 def timestamp_to_str(timestampNone, format%Y-%m-%d %H:%M:%S):if timestamp:time_tuple time.localtime(timestamp) # 把時間戳轉換成時間元祖result time.strftime(format, time_tuple) # 把時間元祖轉換成格式化好的時間retur…

WebApp 里Meta標簽大全

1.先說說mate標簽里的viewport&#xff1a; viewport即可視區域&#xff0c;對于桌面瀏覽器而言&#xff0c;viewport指的就是除去所有工具欄、狀態欄、滾動條等等之后用于看網頁的區域。對于傳統WEB頁面來說&#xff0c;980的寬度在iphone上顯示是很正常的&#xff0c;也是滿屏…

python:封裝CRUD操作

# 封裝數據庫操作 def SELECT(db, cursor, sql):try:# 執行SQL語句db.ping(reconnectTrue)cursor.execute(sql)# 獲取所有記錄列表results cursor.fetchall()logging.debug("select commit")except:logging.error(sql)logging.error("select 語句執行出錯"…

我的osu游戲程序設計(oo)

osu是一款社區元素為主旨的音樂游戲,由澳大利亞人Dean Herbert (peppy)獨立制作并運行. 游戲的方法簡單,就是 1. 圈圈(Circle)&#xff1a;圈圈(Circle) 50。沒打中顯示X,并減少生命值。圈中序號的最后一個的300、100會顯示為激300、喝100。2.滑條(Slider) : 在開始端點擊按住不…

影像數據庫調研

參考Paul Graham比較各種編程語言的方法&#xff0c;我們比較各種數據庫的特點如下&#xff1a; Oracle: 我們需要企業級數據庫。 MySQL: Oracle不開源。 PostgreSQL: MySQL的功能不夠多。 SQLite: 你可以把我嵌入到任何地方。這樣&#xff0c;4種數據庫夠大家用了。 MongoDB: …

linux進程間通信快速入門【三】:信號量(XSI、POSIX以及PV原語)

文章目錄XSIsemgetsemop、semtimedopsemctl基于共享內存demo修改XSI信號量的限制PV原語PV控制并發進程數POSIX信號量使用posix命名信號量使用posix匿名信號量參考在前兩篇文章中我們使用的racingdemo都沒有對臨界區代碼進行加鎖&#xff0c;這里我們介紹以下信號量的使用。Linu…