Linux:IPC - System V

Linux:IPC - System V

    • 共享內存 shm
      • 創建共享內存
        • shmget
        • shmctl
        • ftok
      • 掛接共享內存
        • shmat
        • shmdt
      • shm特性
    • 消息隊列 msg
      • msgget
      • msgctl
      • msgsnd
      • msgrcv
    • 信號量 sem
    • System V 管理機制


System V IPC 是Linux系統中一種重要的進程間通信機制,它主要包括共享內存 shm消息隊列 msg信號量 sem。本博客講解基于System V的進程間通信。


共享內存 shm

共享內存,顧名思義就是一塊被多個進程共享的內存,由于進程間具有獨立性,毫無疑問這一塊內存不應該由某一個進程進行開辟,而是由操作系統親自開辟。

在這里插入圖片描述

如上圖,共享內存會被進程的頁表直接映射到自己的進程地址空間的共享區,從而通過地址空間直接對內存操作,這就是多個進程共享一塊內存的基本原理。

那么我們現在就來看看如何創建出一個共享內存shm


創建共享內存

shmget

shmget 函數是 shm 中用于創建或獲取共享內存段的函數。需要頭文件<sys/ipc.h><sys/shm.h>函數原型如下:

int shmget(key_t key,size_t size,int shmflg)

返回值:

shmget返回一個整型,這個整型叫做shmid,用于標識唯一的shm

  1. key: 用于標識要創建或獲取的共享內存段

keySystem V的唯一標識符,注意不是shm的,而是所有System V的,你可以通過一個key值來標識任意一個system V

  • 標識唯一的shmshmid
  • 標識唯一的system Vkey
  1. size: 指定要創建的共享內存段的大小,單位為字節

注意:共享內存以4 kb為基本單位開辟內存,也就是4096 byte,因此開辟shm的時候,這個參數最好設置為4096的倍數。哪怕你只申請了1 byte的內存,實際上還是會開辟4096 byte大小的空間。

  1. shmflg: 用于指定共享內存段的訪問權限和其他選項

這是一個用于控制共享內存的開辟方式,以及各個屬性的選項,本質是一個位圖。

  • IPC_CREAT: 如果指定的key不存在,則創建一個新的共享內存段,如果已經存在,則直接獲得原先的共享內存

  • IPC_EXCL: 如果指定的key已經存在,則創建失敗

要注意IPC_EXCL只能配合IPC_CREAT一起使用,不能單獨使用IPC_EXCL

另外的,共享內存也可以設置讀寫權限,直接將權限值的八進制按位或到第三個參數中即可

示例:

int main()
{int shmid = shmget(1, 4096, IPC_CREAT | IPC_EXCL | 0666);return 0;
}

以上函數中,就是一個簡單的創建共享內存的過程:

  • 第一個參數1:即這個共享內存的system V標識符key = 1
  • 第二個參數傳入4096:即開辟的共享內存大小為4096 byte
  • 第三個參數為IPC_CREAT | IPC_EXCL | 0666:如果當前的key不存在,則創建對應的共享內存,共享內存的初始權限為0666,如果存在,則創建失敗。

那么我們要如何知道成功創建了一個共享內存呢?

通過ipcs指令,可以看當前所有的system V的總體情況:

在這里插入圖片描述

如果只想看共享內存,則輸入ipcs -m

在這里插入圖片描述

可以看到,我們創建了一個共享內存,key = 0x00000001shmid = 2,擁有者onwer = box-he,初始權限perm = 666,大小bytes = 4096

那么現在有一個問題就是:我們的進程已經結束了,但是進程創建的共享內存還在

也就是說,共享內存如果不主動釋放,那么共享內存就會一直存在,除非重啟操作系統。這個特性叫做:共享內存的生命周期隨內核

如果想要刪除一個共享內存,有兩種方式:通過指令 / 通過接口。

通過ipcrm -m xxx,可以刪除shmidxxx的共享內存。

示例:

在這里插入圖片描述

一開始存在一個shmid = 2的共享內存,通過指令ipcrm -m 2,就可以刪除這個共享內存了。

如果想要通過接口刪除共享內存,則通過shmctl接口。


shmctl

shmctl 用于控制共享內存的各種屬性。

其包含以下功能:

  • 獲取共享內存段的狀態信息
  • 修改共享內存段的屬性
  • 刪除共享內存段

shmctl包含在頭文件<sys/ipc.h><sys/shm.h>中,函數原型如下:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

返回值:

  • 成功時返回 0
  • 失敗時返回 -1,并設置 errno 變量

參數如下:

  • shmid:要操作的共享內存段的標識符
  • cmd:要執行的操作,可以是以下值之一:
    - IPC_STAT:獲取共享內存段的狀態信息
    - IPC_SET:設置共享內存段的某些屬性
    - IPC_RMID:刪除共享內存段
  • buf:指向 shmid_ds 結構體的指針,用于存儲或設置共享內存段的屬性

在講解以上三種模式前,要先介紹一下一個結構體shmid_ds

struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */shmatt_t        shm_nattch;  /* No. of current attaches */...
};

shmid_ds 是一個Linux中給共享內存定義的結構體,這個結構體用于存儲一個共享內存的基本信息。

當第二個參數cmdIPC_STAT,此時就可以獲取一個共享內存的基本信息。

示例:

int main()
{int id = shmget(1, 4096, IPC_CREAT | IPC_EXCL | 0666);struct shmid_ds shm;shmctl(id, IPC_STAT, &shm);cout << "atime:" << shm.shm_atime << endl;cout << "ctime:" << shm.shm_ctime << endl;cout << "cpid:" << shm.shm_cpid << endl;return 0;
}

輸出結果:

在這里插入圖片描述

以上示例中,通過一個struct shmid_ds類型的結構體變量shm,通過調用shmctl(id, IPC_STAT, &shm);獲取到了這個共享內存的相關信息。

其中shmctl(id, IPC_STAT, &shm);
第一個參數id:要控制的共享內存的shmid
第二個參數IPC_STAT:表示當前shmctl的作用是獲取共享內存相關信息
第三個參數&shm:表示獲取到的信息存入變量shm

當第二個參數cmdIPC_SET,就可以設置共享內存的某些屬性

示例:

int main()
{int id = shmget(1, 4096, IPC_CREAT | IPC_EXCL | 0666);struct shmid_ds shm;shmctl(id, IPC_STAT, &shm);cout << "atime:" << shm.shm_atime << endl;shm.shm_atime = 1 ;//修改shm信息shmctl(id, IPC_SET, &shm);//重新獲取shm信息shmctl(id, IPC_STAT, &shm);cout << "atime:" << shm.shm_atime << endl;return 0;
}

一開始通過shmctl(id, IPC_STAT, &shm);把共享內存的相關信息存儲到了結構體shm中,然后把結構體的shm_atime 成員設置為1

再通過shmctl(id, IPC_SET, &shm);把共享內存的信息設置成和shm一致,此時第二個參數為IPC_SET

第三次調用shmctl(id, IPC_STAT, &shm);,此時再把共享內存的信息同步到shm中,最后輸出shmshm_atime 成員。

輸出結果:

在這里插入圖片描述

可以看到,當第二個參數為IPC_SET時,可以修改共享內存的相關屬性。

當第二個參數為IPC_RMID,表示要刪除共享內存段

此時由于我們要刪除共享內存,第三個參數就用不上了,此時設置為空指針即可

示例:

int main()
{shmctl(3, IPC_RMID, nullptr);return 0;
}

以上代碼中shmctl(3, IPC_RMID, nullptr);就表示要刪除shmid = 3的共享內存。

輸出結果:

在這里插入圖片描述

一開始存在一個shmid = 3 的共享內存,經過./test.exe后,這個共享內存就被刪除了,也就是執行了shmctl(3, IPC_RMID, nullptr);

最后再看一次第二個參數的作用:

  • cmd:要執行的操作,可以是以下值之一:
    - IPC_STAT:獲取共享內存段的狀態信息
    - IPC_SET:設置共享內存段的某些屬性
    - IPC_RMID:刪除共享內存段

現在應該可以理解三種情況的作用了。


ftok

創建之前所有示例中,通過shmget創建共享內存時,第一個參數key我都設置為了1,但其實這是非常不符合規范的。操作系統中存在非常多的進程,如果多個進程通過system V通信,那就不能使用相同的key值,如果key設置的太簡單,就很容易沖突

ftok函數,就利用算法生成不易重復的key值。

使用ftok函數需要頭文件<sys/types.h><sys/ipc.h>,函數原型如下:

key_t ftok(const char* pathname, int proj_id);

參數:

  • pathname:當前操作系統下的某一條路徑
  • proj_id:一個數字

也就是說,只要傳入一個路徑和一個數字,ftok就會生成一個key值

示例:

int main()
{key_t key = ftok("./test.cpp", 1);cout << key << endl;return 0;
}

利用相對路徑,使用了路徑./test.cpp作為第一個參數,第二個參數設置為了1,最后輸出ftok生成的key值。

輸出結果:

在這里插入圖片描述

最后生成的key值為17003535

要注意的是,path必須是一個存在的路徑,ftok函數會利用路徑所指向的文件的屬性,以及傳入的第二個參數,一起來產生這個key值。

需要進程間通信的雙方,只需要事先約定好這個path以及第二個整型,就可以利用ftok產生相同的key值,進而訪問同一塊共享內存了。


到目前為止,我們只是講解了如何來開辟一個共享內存,還沒有真正使用這一塊共享內存來實現進程間通信

也就是下圖中的藍色部分:

在這里插入圖片描述

接下來就看看如何使用這一塊共享內存。

掛接共享內存

shmat

共享內存是被直接映射到進程地址空間的共享區的,進程可以通過訪問進程地址空間來訪問共享內存,那么現在的問題就是:如何讓一個內存映射到進程地址空間中?這個把共享內存映射到進程地址空間的過程,叫做掛接共享內存

掛接共享內存需要通過shmat接口實現,需要頭文件<sys/types.h><sys/shm.h>,函數原型如下:

void *shmat(int shmid, const void *shmaddr, int shmflg);

參數:

  • shmid:即被掛接的共享內存的shmid
  • shmaddr:指明這個共享內存要掛接到哪一個地址,一般來說我們不會主動指定地址,這個參數直接傳入空指針即可,操作系統會自動幫我們選擇合適的地址掛接
  • shmflg:掛接共享內存的模式,
    • 傳入0:以讀寫的方式掛接共享內存
    • 傳入SHM_RDONLY:以只讀的方式掛接共享內存

返回值:

  • 如果掛接共享內存出錯,返回-1
  • 如果掛接共享內存成功,返回掛接后共享內存的地址

shmdt

如果你想要取消對共享內存的掛接,使用shmdt接口即可,需要頭文件<sys/types.h><sys/shm.h>,函數原型如下:

int shmdt(const void *shmaddr);

只需要把掛接到的共享內存的地址傳入shmdt即可取消掛接。


現在我們就利用共享內存來完成一次進程間通信,現有A.exeB.exe兩個進程,A負責發送消息,B負責接收消息。

A進程代碼如下:

int main()
{key_t key = ftok("./test.cpp", 1);int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);char* ptr = (char*)shmat(shmid, nullptr, 0);for(int ch = 'A'; ch <= 'Z'; ch++){ptr[ch - 'A'] = ch;sleep(1);}shmctl(shmid, IPC_RMID, nullptr);return 0;
}

一開始A通過ftok生成了一個key值,然后利用shmget生成了一個共享內存,再用shmat將共享內存掛接到進程地址空間,此時ptr指針就指向這個共享內存了,隨后利用循環將字母A - Z寫入共享內存中,每秒寫入一個。最后利用shmctl關閉共享內存。

B進程代碼如下:

int main()
{key_t key = ftok("./test.cpp", 1);int shmid = shmget(key, 4096, IPC_CREAT);char* ptr = (char*)shmat(shmid, nullptr, 0);while(true){cout << ptr << endl;sleep(5);}return 0;
}

B進程也通過ftok生成了key值,由于參數都是"./test.cpp"1,所以生成的key和A一定是一樣的。

隨后通過shmget獲得共享內存的shmid,這個共享內存是由A維護的,因此B只需要拿到shmid即可,共享內存的創建和銷毀都由A來控制。

隨后B進程在一個循環中,每隔五秒讀取一次共享內存。

輸出結果:

在這里插入圖片描述

可以看到,A進程向共享內存寫入的數據,此時就可以被B拿到了,這就是基于共享內存shm的進程間通信。


shm特性

共享內存有以下一些主要特性:

  1. 內存共享:多個進程可以同時訪問和修改同一塊共享內存區域。這種共享內存機制可以讓進程之間高效地交換數據,而無需通過系統調用或者其他進程間通信機制。

  2. 快速訪問:相比于其他進程間通信機制,如管道、消息隊列等,共享內存的訪問速度更快,因為數據直接存儲在內存中,不需要進行數據的拷貝和上下文切換

  3. 靈活性:共享內存可以在進程之間自由分配和管理,大小和位置都可以靈活設置。這種靈活性使得共享內存非常適合用于復雜的進程間通信場景。

  4. 同步問題多個進程可以并發訪問和修改共享內存,因此需要使用信號量、互斥鎖等同步機制來協調對共享內存的訪問,避免數據競爭和不一致性問題。

  5. 內存分配:共享內存是由內核管理的,進程無法直接分配和釋放共享內存,必須通過系統調用如 shmget()shmctl() 來完成。


system V 的后兩種通信方式消息隊列 msg信號量 sem都非常不常用了,本博客中只是簡單講解,不深入研究。

消息隊列 msg

消息隊列顧名思義,是一個被操作系統維護的隊列:

在這里插入圖片描述

進程可以向消息隊列中寫入或者讀取消息,上圖中,每個黃色的小方塊就是一條消息,在消息的頭部會有一個區域用于標識,這條消息是哪一個進程發出的。

msgget

msgget 用于創建一個消息隊列,需要頭文件<sys/types.h><sys/ipc.h><sys/msg.h>,函數原型如下:

int msgget(key_t key, int msgflg);

返回值:

msgget返回一個整型,這個整型叫做msgid,用于標識唯一的msg

參數:

  • key:即標識唯一的system Vkey

  • msgflg: 用于指定消息隊列的訪問權限和其他選項

    • IPC_CREAT: 如果指定的key不存在,則創建一個新的消息隊列,如果已經存在,則直接獲得原先的消息隊列

    • IPC_EXCL: 如果指定的key已經存在,則創建失敗

IPC_EXCL只能配合IPC_CREAT一起使用,不能單獨使用IPC_EXCL

消息隊列也可以設置讀寫權限,直接將權限值的八進制按位或到第二個參數中即可

你會發現,其實消息隊列和共享內存的使用方法幾乎是一摸一樣的!


msgctl

msgctl 用于控制共享內存的各種屬性,需要頭文件<sys/types.h><sys/ipc.h><sys/msg.h>,函數原型如下:

int msgctl(int msgid, int cmd, struct msgid_ds* buf);

返回值:

  • 成功時返回 0
  • 失敗時返回 -1,并設置 errno 變量

參數如下:

  • shmid:要操作的消息隊列的標識符
  • cmd:要執行的操作,可以是以下值之一:
    - IPC_STAT:獲取消息隊列的狀態信息
    - IPC_SET:設置消息隊列的某些屬性
    - IPC_RMID:刪除消息隊列
  • buf:指向 msgid_ds 結構體的指針,用于存儲或設置消息隊列的屬性

msgid_ds源碼如下:

struct msqid_ds {struct ipc_perm msg_perm;     /* Ownership and permissions */time_t          msg_stime;    /* Time of last msgsnd(2) */time_t          msg_rtime;    /* Time of last msgrcv(2) */time_t          msg_ctime;    /* Time of last change */unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t       msg_qnum;     /* Current number of messagesin queue */msglen_t        msg_qbytes;   /* Maximum number of bytesallowed in queue */pid_t           msg_lspid;    /* PID of last msgsnd(2) */pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};

這一塊和共享內存也幾乎是一摸一樣的,不過多解釋了。


剛剛兩個接口解決的是消息隊列的創建與釋放,接下來看看消息隊列如何向隊列中寫入與讀取。

msgsnd

msgsnd用于向消息隊列寫入,需要頭文件<sys/types.h><sys/ipc.h><sys/msg.h>,函數原型如下:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

函數返回值:

  • 成功時,返回 0
  • 失敗時,返回 -1 并設置 errno 變量

參數:

  1. msqid:要發送消息的消息隊列的標識符
  2. msgp:指向要發送的消息內容的指針
  3. msgsz:要發送的消息內容的長度,以字節為單位
  4. msgflg:控制 msgsnd() 行為的標志位,一般來說傳入0即可。常用的有:
    • IPC_NOWAIT: 如果消息隊列已滿,立即返回而不是阻塞
    • MSG_NOERROR: 如果消息內容太長,截斷后仍然發送

此處要著重講解一下第二個參數msgp

msgp 參數是一個指向要發送消息內容的指針。通常情況下,這個消息內容會被存儲在一個自定義的結構體中,這個結構體要滿足以下格式:

struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */
};

這個結構體有兩個成員變量:

  1. mtype:這是一個 long 類型的消息類型標識符。發送消息時,接收方可以根據消息類型來選擇性地接收消息。

  2. mtext:這是一個字符數組,用于存儲實際的消息內容,它的大小可以根據需要進行調整。

在使用 msgsnd() 函數發送消息時,msgp 參數就是指向這個 msg_buf 結構體的指針。

我們在發送消息時,只需要定義一個結構體,結構體的名稱可以是任意的,第一個成員必須是long類型,第二個成員必須是char的數組,數組長度任意。

第一個成員一般用于標識不同進程,比如在一個消息隊列中,A進程發送的消息,mtype設置為1B進程發送的消息,mtype設置為2,這樣就可以根據這個成員來辨別一條消息是哪個進程發送的了。


msgrcv

msgrcv用于從消息隊列提取,需要頭文件<sys/types.h><sys/ipc.h><sys/msg.h>,函數原型如下:

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

返回值:

  • 成功時,返回實際接收的消息長度
  • 失敗時,返回 -1 并設置 errno 變量

參數:

  1. msqid:要從中接收消息的消息隊列的標識符
  2. msgp:指向用于存儲接收消息內容的緩沖區的指針
  3. msgsz:緩沖區 msgp 的大小,以字節為單位
  4. msgtyp:指定要接收的消息類型。可以是以下幾種情況:
    • 如果 msgtyp > 0,則接收第一個 mtype 等于 msgtyp 的消息。
    • 如果 msgtyp == 0,則接收消息隊列中第一個消息,不管 mtype 是什么。
  5. msgflg: 控制 msgrcv() 行為的標志位,一般設為0即可。常用的有:
    • IPC_NOWAIT:如果消息隊列為空,立即返回而不是阻塞
    • MSG_NOERROR:如果接收的消息內容太長,將其截斷后仍然返回

再簡單講解兩個消息隊列相關的系統指令:

ipcs -q用于查看消息隊列:

在這里插入圖片描述

ipcrm -q xxx:用于刪除msgidxxx的消息隊列


接下來講解system V的最后一種通信方式信號量 sem

信號量 sem

信號量的基本原理,在于把一份資源拆分為很多份小資源:

在這里插入圖片描述

多個進程可以分別訪問這個資源的一小部分:

在這里插入圖片描述

但是不允許多個進程同時訪問一個小份資源

而信號量的作用就是預定資源,信號量本質是一個計數器,用于記錄當前還有多少可以分配的資源

信號量的申請過程如下:

  1. 進程訪問資源前,要先申請一個信號量,用于預定資源,一旦預定成功,信號量的數目減少一個,即當前剩余的資源少一個。從預定成功開始,這一份資源就不能被其他進程再訪問了
  2. 進程申請到信號量后,就可以正常訪問這一份資源了
  3. 當進程使用完,于是釋放信號量,此時信號量數目加一,即當前剩余資源增加一個。

關于信號量,本博客不講解接口如何使用了,其使用方式比較麻煩,需要很大篇幅,而且信號量也不常用。


System V 管理機制

同為system V系列,共享內存 shm消息隊列 msg信號量 sem是有共性的,操作系統對這三者進行統一的管理。

Linux中,描述三者的結構體如下:

在這里插入圖片描述

其中共享內存 shm被結構體shmid_kernel管理,消息隊列 msg被結構體msg_quque管理,信號量 sem被結構體sem_array管理。不過以上結構體中,成員并不是完全的,我只截取了一小部分。

Linux是如下對system V進行管理的:

在這里插入圖片描述

ipc_ids結構體的entires成員指向了結構體ipc_id_aryipc_id_ary的第二個成員是一個柔性數組,該數組是一個指針數組,指向了不同的system V結構體。此時Linuxsystem V的管理就變成了對數組的增刪查改。

那么現在有一個問題就是:為什么一個數組可以指向三種不同類型的結構體變量?

我們再回到三個描述system V的結構體:

在這里插入圖片描述

它們三個結構體的第一個成員分別是shm_permq_permsem_perm,這三者其實都是同一個結構體類型struct kern_ipc_perm,而Linux就是通過這個struct kern_ipc_perm來同時管理三種結構體的。

在這里插入圖片描述

ipc_id_ary中,第二個成員數組的類型是struct kern_ipc_perm*,也就是指向struct kern_ipc_perm指針,這個struct kern_ipc_perm存儲了三種system V都具有的屬性。struct kern_ipc_perm結構體同時也都是三個system V的結構體的第一個成員,因此在訪問具體的某個結構體時,只需要進行一次指針的強制類型轉換即可

Linux就是通過這樣一種方式,把所有的system V都統一地管理了起來。


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

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

相關文章

物理內存與虛擬內存的區別

物理內存和虛擬內存是計算機系統中重要的概念&#xff0c;它們有著不同的特點和作用。 物理內存&#xff1a; 物理內存是計算機實際存在的內存&#xff0c;通常指的是RAM&#xff08;隨機存取存儲器&#xff09;。物理內存直接映射到計算機的物理地址空間&#xff0c;可以直接被…

? 傳知代碼 ? 高速公路車輛速度檢測軟件

&#x1f49b;前情提要&#x1f49b; 本文是傳知代碼平臺中的相關前沿知識與技術的分享~ 接下來我們即將進入一個全新的空間&#xff0c;對技術有一個全新的視角~ 本文所涉及所有資源均在傳知代碼平臺可獲取 以下的內容一定會讓你對AI 賦能時代有一個顛覆性的認識哦&#x…

【NumPy】全面解析NumPy的where函數:高效條件操作指南

&#x1f9d1; 博主簡介&#xff1a;阿里巴巴嵌入式技術專家&#xff0c;深耕嵌入式人工智能領域&#xff0c;具備多年的嵌入式硬件產品研發管理經驗。 &#x1f4d2; 博客介紹&#xff1a;分享嵌入式開發領域的相關知識、經驗、思考和感悟&#xff0c;歡迎關注。提供嵌入式方向…

哈希沖突的常見解決方法【附C++代碼】

在C中&#xff0c;哈希表是一種常用的數據結構&#xff0c;用于實現快速的插入、刪除和查找操作。 哈希表的核心在于哈希函數&#xff0c;它將輸入的關鍵字轉換為一個數組索引。然而&#xff0c;不同的關鍵字可能映射到相同的索引&#xff0c;這種情況稱為哈希沖突。 有效地解…

走進全球LED顯示龍頭艾比森,深挖逆勢增長43%的數智化邏輯

在大環境不景氣的情況下&#xff0c;有一家智能制造企業在2023年營收40億&#xff0c;同比增長高達43%&#xff0c;海外營收增長約 46%&#xff0c;并且連續12年單品牌出口額第一。 這就是全球LED顯示龍頭艾比森。 5月9日&#xff0c;紛享銷客帶領近70位企業高管走進紛享銷客…

使用Nginx將服務器目錄、文件共享出來

1.配置映射路徑&#xff0c;加入映射目錄 location /abc/ { autoindex on; autoindex_localtime on; charset utf-8; alias /usr/mydir/; } 2.重載Nginx配置 nginx -s reload 3.訪問 http://XXX.XXX.XXX.XXX/abc/ 即可 注&#xff1a; 如果…

短視頻再度重逢:四川京之華錦信息技術公司

短視頻再度重逢 在數字化時代的浪潮中&#xff0c;短視頻以其獨特的魅力迅速崛起&#xff0c;成為現代人生活中不可或缺的一部分。而當我們談論起短視頻&#xff0c;我們不僅僅是在談論一種娛樂方式&#xff0c;更是在談論一種情感的載體&#xff0c;一種回憶的媒介。今天&…

PHP8.0 match函數

match 表達式是 PHP 8.0 引入的一個新的控制結構&#xff0c;它提供了一種簡潔且更強大的方式來進行條件匹配。與 switch 語句相比&#xff0c;match 表達式具有以下優勢&#xff1a; 返回值&#xff1a;match 是一個表達式&#xff0c;它會返回一個值。嚴格比較&#xff1a;m…

MyBatis系統學習篇 - MyBatis逆向工程

MyBatis的逆向工程是指根據數據庫表結構自動生成對應的Java實體類、Mapper接口和XML映射文件的過程。逆向工程可以幫助開發人員快速生成與數據庫表對應的代碼&#xff0c;減少手動編寫重復代碼的工作量。 我們在MyBatis中通過逆向工具來幫我簡化繁瑣的搭建框架&#xff0c;減少…

iOS推送證書過期處理

蘋果推送證書的有效期都是一年&#xff0c;將要過期的時候&#xff0c;蘋果官方會發郵件提醒。 一、過期 在電腦上找到并打開其它->鑰匙串訪問&#xff1b; 我的證書可以看到各個App的推送證書&#xff0c;如果過期了&#xff0c;顯示紅色X 二、重新創建 1、登陸apple開…

如何解決三層單點故障

我給他整成下面這樣行不行呀 一個pc的默認網關只有一個&#xff0c;pc1配置的是1.1&#xff0c;那么路由壞了&#xff0c;他還是給1.1發送數據&#xff0c;冗余的那個也沒用上呀 用VRRP&#xff08;虛擬路由冗余協議&#xff09;解決以上問題 那光把這個R1和R2虛擬成一個R3&…

android usb轉串口

Android USB通信&#xff08;host轉串口&#xff09;_android usb 實現串口通信-CSDN博客

Windows內核函數 - 文件的讀操作

DDK提供了文件讀操作的內核函數&#xff0c;其函數聲明如下&#xff1a; NTSTATUS ZwWriteFile(IN HANDLE FileHandle,IN HANDLE Event,IN PIO_APC_ROUTINE ApcRoutine,IN PVOID ApcContext,out PIO_STATUS_BLOCK IoStatusBlock,IN PVOID Buffer,IN ULONG Length,IN PLARGE_IN…

windows 執行node報錯 800A1391

在項目下執行node -v的時候&#xff0c;拋了這個錯誤&#xff0c;一開始沒發現有啥問題 現在一看&#xff0c;這個報錯里的node怎么是個文件... 出現這個問題&#xff0c;是因為項目下&#xff0c;有個同名的文件叫node.js&#xff0c;搞得windows一時不知道是想打開node.js文…

代碼隨想錄算法訓練營Day51 | 300.最長遞增子序列 674. 最長連續遞增序列 718. 最長重復子數組

代碼隨想錄算法訓練營Day51 | 300.最長遞增子序列 674. 最長連續遞增序列 718. 最長重復子數組 LeetCode 300.最長遞增子序列 題目鏈接&#xff1a;LeetCode 300.最長遞增子序列 思路&#xff1a; 選取最長子序列&#xff0c;并收集 class Solution { public:int lengthOfL…

通過提示工程將化學知識整合到大型語言模型中

在當今快速發展的人工智能領域&#xff0c;大型語言模型&#xff08;LLMs&#xff09;正成為科學研究的新興工具。這些模型以其卓越的語言處理能力和零樣本推理而聞名&#xff0c;為解決傳統科學問題提供了全新的途徑。然而&#xff0c;LLMs在特定科學領域的應用面臨挑戰&#…

第四十六天 | 279.完全平方數 139.單詞拆分

題目&#xff1a;279.完全平方數 本題比較簡單&#xff0c;幾天沒做背包但是這道題很快ac了 嘗試解答&#xff1a; 題目類型&#xff1a;給定一個背包容量&#xff0c;求裝滿背包的最少物品數&#xff0c;且每個物品可以放多次&#xff0c;完全背包 1.dp[j]數組含義&#xff…

如何選擇適合自己需求的揚州獨立服務器方案?

在互聯網時代&#xff0c;獨立服務器是網絡建設的重要組成部分。選擇適合自己需求的揚州獨立服務器方案至關重要。下面&#xff0c;我們將介紹如何選擇合適的揚州獨立服務器&#xff0c;并推薦萊卡云&#xff08;Lcayun&#xff09;服務器商。 明確需求 要明確自己的需求是什…

大型央企國企信創化與數字化轉型規劃實施方案(71頁PPT)

方案介紹&#xff1a; 隨著全球信息技術的迅猛發展&#xff0c;數字化轉型已成為企業提升競爭力、實現可持續發展的必經之路。作為國家經濟的重要支柱&#xff0c;大型央企國企在信創化與數字化轉型方面承載著重要的責任和使命。本方案旨在通過系統性的規劃和實施&#xff0c;…

rpc理解

rpc 遠程過程調用 rpc與http的區別 1.性能高 2.使用復雜 3.可擴展性高 4 跨語言支持 5.可以使用服務發現&#xff0c;負載均衡&#xff0c;熔斷降級 rpc遠程調用&#xff0c;必須傳輸數據&#xff0c;需要序列化。 序列化有多種方式&#xff1a; jdk原生序列化&#xff0c…