Linux系統編程--進程間通信

目錄

1. 介紹

1.1 進程間通信的目的

1.2 進程間通信的分類

2. 管道

2.1 什么是管道

2.2 匿名管道

2.2.1 接口

2.2.2 步驟--以父子進程通信為例

2.2.3?站在文件描述符角度-深度理解

2.2.4 管道代碼

2.2.5 讀寫特征

2.2.6?管道特征

2.3 命名管道

2.3.1?接口

2.3.2 代碼實現

?2.3.3 匿名管道和命名管道的區別

3.?system V共享內存

3.1 共享內存的原理?

3.2 步驟

3.3 系統接口

3.4 代碼

3.5 共享內存的優缺點

4.信號量

4.1 相關概念

4.2 信號量 -- 對資源的一種預定

4.3 系統接口


1. 介紹

1.1 進程間通信的目的

  • 數據傳輸:一個進程需要將它的數據發送給另一個進程
  • 資源共享:多個進程之間共享同樣的資源。
  • 通知事件:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件? ? ? ? ? ? ? ? ? ? ? ? (如進程終止 時要通知父進程)。
  • 進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望? ? ? ? ? ? ? ? ? ? ? 能夠攔截另一個進程的所有陷入和異常,并能夠及時知道它的狀態改變
  • 有時候也需要多進程協同進行工作

如何理解進程間通信的本質問題呢?

  • OS需要直接/間接的給通信雙方的進程提供"內存空間"
  • 要通信的不同進程必須看到同一份公共資源

1.2 進程間通信的分類

  • 管道
  • SystemV(本文只討論共享內存) -- 讓通信過程可以跨主機
  • POSIX -- 聚焦在本地通信

2. 管道

2.1 什么是管道

  • 管道是 Unix 中最古老的進程間通信的形式
  • 我們把從一個進程連接到另一個進程的一個數據流稱為一個 管道 ?

管道又分匿名管道和命名管道兩種。?

2.2 匿名管道

2.2.1 接口

#include <ustdio.h>

int pipe(int pipefd[2]);?
參數:piepfd[]為輸出型參數,pipefd[0]為讀文件描述符,pipefd[1]為寫文件描述符,若為其他的文件描述符使用,一般這兩個fd分別為3、4。

返回值:創建成功返回0,失敗返回-1?

2.2.2 步驟--以父子進程通信為例

  • 父進程利用pipe接口創建管道,分別以讀和寫打開一個文件?
  • 父進程fork出子進程
  • 父進程關閉fd[1],子進程關閉fd[0]
  • 這樣父進程就可以往管道文件中寫數據,子進程從管道文件中讀數據,實現了父子進程的通信

注:管道一般是單向的,其實管道也是一個文件("內核級文件")--不需要進行磁盤IO(當然也可以用磁盤文件來實現這個管道操作,但是要進行磁盤讀取,太慢了)

?若是管道中沒有數據了,但是讀端還在讀,OS會直接阻塞當前正在讀取的進程。

2.2.3?站在文件描述符角度-深度理解

2.2.4 管道代碼

????????在這個代碼部分,可以實驗當讀快寫慢、讀慢寫快、只讀關閉、只寫關閉四種情況,這里只給出了只有讀關閉的情況

  #include <iostream>                                                                                                                                                   #include <cstdio>    #include <unistd.h>    #include <cassert>    #include <sys/stat.h>    #include <sys/wait.h>    #include <fcntl.h>    #include <cstring>    using namespace std;    int main()    {    // 第一步:創建管道文件    int fds[2];    int n = pipe(fds);    assert(n == 0);    // 0 1 2應該是被占用的 _-> 3 4    cout << "fds[0]: " << fds[0] << endl;    cout << "fds[1]: " << fds[1] << endl;    // 第二步:fork    pid_t id = fork();    assert(id >= 0);    if(id == 0)    {    // 子進程的通信代碼 子進程寫入    close(fds[0]);    // 通信代碼    // string msg = "hello, i am child!";    int cnt = 0;    const char* s = "我是子進程,我正在給你發消息!";    while(1)    {    cnt++;    char buffer[1024]; // 只有子進程能看到    snprintf(buffer, sizeof buffer, "child->parent say: %s[%d][%d]", s, cnt, getpid());    // 往文件中寫入數據    write(fds[1], buffer, strlen(buffer));    // sleep(50); // 細節 每隔一秒寫一次    // break;    }cout << "子進程關閉寫端" << endl;close(fds[1]);exit(0);}                                                                                                                                                                 // 父進程的通信代碼  父進程讀取close(fds[1]);while(1){char buffer[1024];// cout << "AAAAAAAAAAAAAAA" <<endl;// 父進程在這里阻塞等待ssize_t s = read(fds[0], buffer, sizeof(buffer) - 1);//  cout << "BBBBBBBBBBBBBBB" <<endl;if(s > 0) {buffer[s] = 0;cout << " Get Message# " << buffer <<" | my pid: " << getpid() << endl;}else if(s == 0){cout << "read: " << s << endl;break;}// cout << "Get Message#" << buffer << " | my pid: " << getppid() << endl;// 細節:父進程可沒有進行sleep//sleep(5);// close(fds[0]);break;}close(fds[0]);int status = 0;cout << "父進程關閉讀端" << endl;n = waitpid(id, &status, 0);assert(n == id);cout << "pid->" << n << ":" << (status & 0x7F) << endl; // 信號為13:SIGPIPE 中止了寫入進程return 0;}

? ? ? ?由上面代碼結果可以看出,當讀關閉時,OS會終止寫端,給寫進程發送信號,終止寫端。寫進程收到13號信號

2.2.5 讀寫特征

  • 讀快,寫慢 -- 讀進程會阻塞,等到管道中有數據時繼續讀取,子進程沒有寫入的那段時間,? ? ? ? ? ? ? ? ? ? ? ? ? 若管道中沒有數據時,父進程會在read處阻塞等待
  • 讀慢,寫快 -- 寫進程正常寫數據,管道寫滿時,會在write處阻塞,讀進程就緒時,繼續讀取? ? ? ? ? ? ? ? ? ? ? ? ?數據
  • 寫關閉 --?管道中的數據會被讀取完畢后返回EOF,此時?read?函數會返回0,最后等待子進程關? ? ? ? ? ? ? ? ? 閉讀端
  • 讀關閉 -- OS會中止寫端,給寫端發送信號--13 SIGPIPE,終止寫端

2.2.6?管道特征

  • 管道的生命周期隨進程
  • 管道可以用來進行具有血緣關系的進程通信,常用于父子進程
  • 管道是面向字節流的
  • 單向通信 -- 半雙工通信
  • 互斥與同步機制 -- 對共享資源進行保護額方案

2.3 命名管道

  • 管道應用的一個限制就是只能在具有共同祖先(具有親緣關系)的進程間通信。
  • 如果我們想在不相關的進程之間交換數據,可以使用FIFO文件來做這項工作,它經常被稱為命名管道。
  • 命名管道是一種特殊類型的文件
  • 在用命名管道實現兩個進程通信時,任意一個進程調用mkfifo創建命名管道即可

2.3.1?接口

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char* pathname, mode_t mode);

參數:pathname:命名管道的路徑名? mode:管道權限

返回值:成功返回0;失敗返回-1,并設置errno來指示錯誤原因

int unlink(const char* pathname);? --在進程結束后,清理和刪除命名管道。

參數:命名管道的路徑名

返回值:成功返回0;失敗返回-1,并設置errno來指示錯誤原因

?命名管道可以從命令行上創建,命令行方法是使用下面這個命令:

mkfifo filename? # filename為命名管道文件名

2.3.2 代碼實現

用命名管道實現 server&client 通信
server.cc:
#include <iostream>
#include "comm.hpp"
using namespace std;int main()
{bool r = createFifo(NAMED_PIPE);assert(r);(void)r;cout << "server begin" << endl;int rfd  =open(NAMED_PIPE, O_RDONLY);  // 只讀方式打開cout << "server end" << endl;                                                                        if(rfd < 0)                                                                                          {                                                                                                    cout << "文件打開失敗!" << endl;                                                                exit(1);                                                                                         }                                                                                                    // read                                                                                              char buffer[1024];                                                                                   while(true)                                                                                          {                                                                                                    ssize_t s = read(rfd, buffer, sizeof buffer - 1);                                                if(s > 0)                                                                                        {                                                                                                buffer[s] = 0;                                                                               std::cout << "client->server:" << buffer << endl;                                            }                                                                                                else if(s == 0)    {                                                                                                cout << "client quit, me too!" << endl;                                                      break;                                                                                                                                                              }    else{    cout << "err string:" << strerror(errno) << endl;    break;    }    }close(rfd);// sleep(10);removeFifo(NAMED_PIPE);return 0;
}

client.cc

#include <iostream>    
#include "comm.hpp"    
using namespace std;    int main()    
{    // 與server打開同一個文件    cout << "client begin" << endl;    int wfd = open(NAMED_PIPE, O_WRONLY);    cout << "client end" << endl;    if(wfd < 0)    {    cout << "文件打開失敗!" << endl;    exit(1);    }    // write    char buffer[1024];    while(true)    {    cout << "Please Say# ";                                                                                                                                         fgets(buffer, sizeof(buffer)-1, stdin);    if(strlen(buffer) > 0) buffer[strlen(buffer)-1] = 0;    ssize_t s = write(wfd, buffer, strlen(buffer));    assert(s == strlen(buffer));    (void)s;    }    close(wfd);    return 0;    
}   

comm.hpp?

#pragma once    #include <string>    
#include <iostream>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <cstring>    
#include <cassert>    
#include <cerrno>    
#include <unistd.h>    
#include <sys/wait.h>    
#include <fcntl.h>    #define NAMED_PIPE "/tmp/mypipe.106"    bool createFifo(const std::string& path)    
{    umask(0);    int n = mkfifo(path.c_str(), 0600);  // 只允許擁有者通信    if(n == 0) return true;    else{                                                                                                                                                               std::cout << "erro" << errno << "err string: " << strerror(errno) << std::endl;    return false;    }    
}    void removeFifo(const std::string & path)    
{    int n = unlink(path.c_str());    assert(n == 0); // debug有效,release里面就無效    (void)n;  // 不想有警告    
}   

可以看到client可以向server端發送數據,server收到并打印到屏幕中,實驗結果如下圖所示:?

下圖為命名管道的信息:?

?

?2.3.3 匿名管道和命名管道的區別

  • 匿名管道由pipe函數創建并打開。
  • 命名管道由mkfifo函數創建,打開用open
  • FIFO(命名管道)與pipe(匿名管道)之間唯一的區別在它們創建與打開的方式不同,一但這些工作完成之后,它們具有相同的語義。

3.?system V共享內存

3.1 共享內存的原理?

????????共享內存區是最快的IPC形式。一旦這樣的內存映射到共享它的進程的地址空間,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用來傳遞彼此的數據

原理:是不同的進程通過各自的PCB和頁表訪問同一快共享內存

3.2 步驟

  • 申請一塊空間
  • 將創建好的內存映射(將進程和共享內存掛接)到不同的進程地址空間
  • 若未來不想通信:取消進程和內存的映射關系--去關聯、釋放共享內存?

3.3 系統接口

#include <sys/ipc.h>

#include <sys.shm.h>

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

參數:key: 進行唯一性標識 -- 將key使用shmget設置進入共享內存屬性中,用來表示共享? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 內存在內核中的唯一性

? ? ? ? ? ?size:申請空間大小--一般為4KB的整數倍

? ? ? ? ? ?shmflg:IPC_CREAT--如果指定的共享內存不存在,創建;如果存在,獲取共享內存

? ? ? ? ? ? ? ? ? ? ? ? ? IPC_EXCL--無法單獨使用? 使用:IPC_CREAT|IPC_EXCL:如果不存在,? ? ? ? ? ? ? ? ? ? ? ? ? ? 創建--創建的一定是一個新的共享內存;存在則出錯返回,還可以通過其? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 設置共享內存的權限
返回值:成功返回標識符shmid;失敗,返回-1,與文件不同

key_t ftok(char* pathname, char proj_id); --
使用給定路徑名命名的文件的標識(必須引用一個現有的,可訪問的文件)和proj_id的最低有效8位(必須是非零的)來生成key_t類型的System V IPC密鑰

返回值:成功返回key_t值,失敗返回-1

解析:

? ? ? ? 創建共享內存時,如何保證其在系統中是唯一的?-- 通過參數key確定的,只要保證另一個要通信的進程看到相同的key值,通過在各個共享內存的屬性中查找相同的key,即可找到同一塊共享內存--通過相同的pathname和proj_id在不同的進程中調用ftok獲得相同的key。那么key在哪里呢? -- 在結構體struct stm中。

IPC資源的特征:共享內存的生命周期是隨OS的,不是隨進程的,若沒有對共享內存進行手動的刪除,那么該資源不會消失

查看IPC資源的命令:ipcs -m(共性內存)? /-q(消息隊列)/-s(信號量)

刪除IPC資源的執行:ipcrm -m shmid

操作共享內存

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

參數:shmid:shmget的返回值--要控制哪一個共享內存

????????? ?cmd:IPC_RMID -- 刪除共享內存段 誰創建誰刪除

? ? ? ? ? ? ? ? ? ? ? IPC_STAT -- 獲取共享內存屬性

? ? ? ? ? ? ? ? ? ? ? IPC_SET -- 設置共享內存屬性

? ? ? ? ? ?buf:

返回值:失敗返回-1

關聯進程

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

參數:shmid:

????????? ?shmaddr:將共享內存映射到哪一個地址空間中,一般設為nullptr?核心自動選擇? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 一個地址

? ? ? ? ? ?shmflg:一般設置為0,讀寫權限

返回值:共享內存空間的起始地址

去關聯:并不是刪除共享內存,而是去除PCB和共享內存的映射關系

int shmdt(const void* shmaddr);

參數:shmaddr-由shmat所返回的指針
返回值:失敗返回-1

3.4 代碼

// common.hpp
#ifndef _COMM_HPP_                                                                                                                                                      
#define _COMM_HPP_    #include <iostream>    
#include <sys/ipc.h>    
#include <sys/types.h>    
#include <sys/shm.h>    
#include <cerrno>    
#include <cstring>    
#include <cstdlib>    
#include <unistd.h>    #define PATHNAME "."    
#define PROJ_ID 0x66    
#define MAX_SIZE 4096    key_t getKey()    
{    key_t k = ftok(PATHNAME, PROJ_ID);  // 可以獲取同樣的key!    if(k < 0)    {    // cin cout cerr -> stdin stdout stderr -> 0,1,2    std::cerr << errno << ":" << strerror(errno) << std::endl;    exit(1);    }    return k;    
}    int getShmHelper(key_t k, int flags)    
{    int shmId = shmget(k, MAX_SIZE, flags);    if(shmId < 0)    {    std::cerr << errno << ":" << strerror(errno) << std::endl;    exit(2);    }    return shmId;    
}    
// 給之后的進程獲取共享內存
int getShm(key_t k)
{return getShmHelper(k, IPC_CREAT/*可以設定為0*/); 
}// 給第一個進程使用 創建共享內存 
int creatShm(key_t k)
{return getShmHelper(k, IPC_EXCL | IPC_CREAT | 0600); // 0600為權限
}void *attachShm(int shmId)
{void *mem = shmat(shmId, nullptr, 0);  // 64位系統 指針占8字節if((long long)mem == -1L){std::cerr << "shmat " << errno << ":" << strerror(errno) << std::endl;exit(3);}return mem;
}void detachShm(void *start)
{if(shmdt(start) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;}
}void delShm(int shmId)
{if(shmctl(shmId, IPC_RMID, nullptr) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;}
}#endif//shm_client.cc//
#include "common.hpp"                                                                                                                                                   
using namespace std;    int main()    
{    key_t k = getKey();    printf("0x%x\n", k);    int shmId = getShm(k);    printf("shmId:%d\n", shmId);    // 關聯    char *start = (char*)attachShm(shmId);    printf("sttach success, address start: %p\n", start);    // 使用    const char* message = "hello server, 我是另一個進程,正在和你通信!";    pid_t id = getpid();    int cnt = 1;    // char buffer[1024];    while(true)    {    sleep(1);    // 直接將需要傳遞的信息寫在共享內存字符串中 省去了很多拷貝的過程 提高了傳輸信息的效率    snprintf(start, MAX_SIZE, "%s[pid:%d][消息編號:%d]", message, id, cnt++);    // snprintf(buffer, sizeof(buffer), "%s[pid:%d][消息編號:%d]", message, id, cnt);    // memcpy(start, buffer, strlen(buffer)+1);    }    // 去關聯    detachShm(start);    // done    return 0;    
} /shm_server.cc///
#include "common.hpp"                                                                                                                                                   
using namespace std;    int main()    
{    key_t k = getKey();    printf("0x%x\n", k);    // 申請共享內存    int shmId = creatShm(k);    printf("shmId:%d\n", shmId);    sleep(3);    // 關聯    // 將共享內存看為一個字符串    char *start = (char*)attachShm(shmId);    printf("sttach success, address start: %p\n", start);    // 使用    while(true)    {    printf("client say: %s\n", start);    sleep(1);    }    // 去關聯    detachShm(start);    sleep(5);    // 刪除共享內存    delShm(shmId);    return 0;    
}    

上面的代碼我們看到的現象是:

通過共享內存的方式實現了進程間通信

3.5 共享內存的優缺點

優點:

  • 共享內存是所有通信中最快的,大大減少數據的拷貝次數

缺點:

  • 不會給我們進行同步和互斥,沒有對數據進行任何保護

問題--同樣的代碼,管道和共享內存方式實現各需要多少次拷貝??

4.信號量

4.1 相關概念

信號量?-- 本質是一個計數器,通常用來表示公共資源中,資源數量的多少問題

公共資源?-- 被多個進程同時訪問的資源,訪問沒有保護的公共資源時,可能會導致數據不一致? ? ? ? ? ? ? ? ? ? ? ? ?問題

為什么要讓不同進程看到同一份資源? -- 實現進程間的協同工作,但是進程是具有獨立性的,? ? ? 為了讓進程看到同一份資源,提出了進程間通信的方法,但是又帶來了新的問題--數據不一致問題

臨界資源:將被保護起來的公共資源稱為臨界資源,但是大部分的資源是獨立的,只有少量的屬于臨? ? ? ? ? ? ? ? ? ? ? 界資源,資源就是內存、文件、網絡等

臨界區:進程訪問臨界資源的代碼被稱為臨界區,與之對應的為非臨界區

保護公共資源:互斥、同步

原子性:要么不做,要做就做完,只有兩種狀態

4.2 信號量 -- 對資源的一種預定

為什么要有信號量?

設sem=20; sem--;// P操作,訪問公共資源;sem++;// V操作,釋放公共資源? --PV操作

所有的進程在訪問公共資源前都必須先申請sem信號量,前提是所有進程必須先看到同一個信號量,那么信號量本身就是公共資源--也要保證自己的安全--信號量++、--的操作是原子操作

如果一個信號量的初始值為1,二維信號量/互斥信號量

4.3 系統接口

頭文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

申請信號量

int semget(key_t key, int nsems, int semflg);

參數:? ?key:對申請的信號量進行唯一性標識

? ? ? ? ? ? ? nsems:申請幾個信號量,與信號量的值無關

? ? ? ? ? ? ? semflg:與共享內存的flag相同含義

返回值:成功返回信號量標識符semid,失敗返回-1

刪除信號量

int semctl(int semid, int semnum, int cmd,...);

參數:semid:信號量id,semget返回的值

? ? ? ? ? ?semnum:信號量集的下標

? ? ? ? ? ?cmd:IPC_RMID、IPC_STAT、IPC_SET

返回值:失敗返回-1?

操作信號量

int semop(int semid, struct sembuf* sops, unsigned nsops);

參數:semid:信號量id

???????????sops:

信號量的詳細操作會在多線程部分講解?

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

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

相關文章

集成平臺建設方案(Doc原件)

基礎支撐平臺作為系統總體架構的核心&#xff0c;不僅要促進與各應用子系統和第三方系統的順暢交互&#xff0c;還需確保內部業務在該平臺上能夠靈活擴展。針對這一需求&#xff0c;我們對基礎支撐平臺提出了以下要求&#xff1a; (1) 平臺需基于其基礎架構&#xff0c;為多源異…

python基礎:設置代碼格式

隨著編寫的程序越來越長&#xff0c;有必要了解一些代碼格式的約定&#xff0c;讓你的代碼盡可以能易于閱讀。 python代碼編寫規范為PEP8&#xff0c;有興趣的朋友可以下載觀看&#xff0c;這里僅作簡要說明。 1、縮進 PEP8建議每級縮進都使用4個空格。多數情況下編程語言的…

vscode-創建vue3項目-修改暗黑主題-常見錯誤-element插件標簽-用法涉及問題

文章目錄 1.vscode創建運行編譯vue3項目2.添加項目資源3.添加element-plus元素4.修改為暗黑主題4.1.在main.js主文件中引入暗黑樣式4.2.添加自定義樣式文件4.3.html頁面html標簽添加樣式 5.常見錯誤5.1.未使用變量5.2.關閉typescript檢查5.3.調試器支持5.4.允許未到達代碼和未定…

UE5的安裝與基本操作(一)

文章目錄 前言安裝UE5新建第一個游戲項目基本游覽方式對目標進行變換各種變換對齊 快速定位目標 總結 前言 Unreal Engine 5 (UE5) 是一款由 Epic Games 開發的實時 3D 創作平臺&#xff0c;用于制作游戲、電影、動畫、建筑可視化和其他類型的交互式體驗。UE5 提供了一系列強大…

Flutter第十五彈 Flutter插件

目標&#xff1a; 1.Flutter插件是什么&#xff1f;有什么作用&#xff1f; 插件 (plugin) 是 package 的一種&#xff0c;全稱是 plugin package&#xff0c;我們簡稱為 plugin&#xff0c;中文叫插件。 2.怎么創建Flutter插件&#xff1f; 一、什么是插件 在flutter中&am…

【成都活動邀請函】7月6 | PowerData 數字經濟-“成都“開源行!

【成都活動邀請函】7月6 | PowerData 數字經濟-"成都"開源行&#xff01; 活動介紹活動信息線上直播掃碼報名往期活動回顧專注數據開源&#xff0c;推動大數據發展 活動介紹 九天開出一成都&#xff0c;萬戶千門入畫圖。 自古以來&#xff0c;成都便是國家發展的重要…

第2章-Python編程基礎

#本章目標 1&#xff0c;了解什么是計算機程序 2&#xff0c;了解什么是編程語言 3&#xff0c;了解編程語言的分類 4&#xff0c;了解靜態語言與腳本語言的區別 5&#xff0c;掌握IPO程序編寫方法 6&#xff0c;熟練應用輸出函數print與輸入函數input 7&#xff0c;掌握Python…

【機器學習】機器學習的重要技術——生成對抗網絡:理論、算法與實踐

引言 生成對抗網絡&#xff08;Generative Adversarial Networks, GANs&#xff09;由Ian Goodfellow等人在2014年提出&#xff0c;通過生成器和判別器兩個神經網絡的對抗訓練&#xff0c;成功實現了高質量數據的生成。GANs在圖像生成、數據增強、風格遷移等領域取得了顯著成果…

leetCode.97. 交錯字符串

leetCode.97. 交錯字符串 題目思路 代碼 class Solution { public:bool isInterleave(string s1, string s2, string s3) {int n s1.size(), m s2.size();if ( s3.size() ! n m ) return false;vector<vector<bool>> f( n 1, vector<bool> (m 1));s1 …

C語言使用void *類型作為函數傳參

C語言使用void *怎么理解&#xff1a; 根據本人的理解&#xff0c;他就是指向操作數據區的首地址而已 凡是void指的數據區都要進行第二次初始化數據類型&#xff08;即dtype p(dtype)pdata&#xff09;*。 舉兩個例子&#xff1a; 傳入函數&#xff1a; void tx_data(void …

Sparse4D v3: Advancing End-to-End 3D Detection and Tracking

Sparse4D v3: Advancing End-to-End 3D Detection and Tracking 相關內容&#xff1a;總覽&#xff0c;Sparse4D v1&#xff0c;Sparse4D v2&#xff0c; 單位&#xff1a;地平線(Sparse4D v1 v2 原班人馬) GitHub&#xff1a;https://github.com/HorizonRobotics/Sparse4D …

昇思25天學習打卡營第5天 | 網絡構建

目錄 1.定義模型類 2.模型層 nn.Flatten nn.Dense nn.ReLU nn.SequentialCell nn.Softmax 3.模型參數 代碼實現&#xff1a; 總結 神經網絡模型是由神經網絡層和Tensor操作構成的&#xff0c; mindspore.nn提供了常見神經網絡層的實現&#xff0c; 在MindSpore中&a…

啟動spring boot項目停止 提示80端口已經被占用

可能的情況: 檢查并結束占用進程: 首先,你需要確定哪個進程正在使用80端口。在Windows上,可以通過命令行輸入netstat -ano | findstr LISTENING | findstr :80來查看80端口的PID,然后在任務管理器中結束該進程。在

AI智能客服項目拆解(1) 產品大綱

本文作為拆解AI智能客服項目的首篇&#xff0c;以介紹產品大綱為主。后續以某AI智能客服產品為例&#xff0c;拆解相關技術細節。 AI智能客服是一種基于人工智能技術的客戶服務解決方案&#xff0c;旨在提高客戶滿意度和優化企業運營。利用人工智能和自然語言處理技術&#xff…

MySQL之索引失效的情況

什么情況下索引會失效&#xff1f; 違反最左前綴原則范圍查詢右邊的列不能使用索引不要在索引列上進行運算操作字符串不加單引號導致索引失效以%開頭的like模糊查詢 什么情況下索引會失效&#xff1f; 示例&#xff0c;有user表如下 CREATE TABLE user (id bigint(20) NOT NU…

實驗1 多層感知器設計(MLP)

1.實驗目的 掌握多層感知器的原理。掌握多層感知器的設計、訓練和測試。2.實驗要求 設計一個多層感知器,用于對給定的數據進行分類。要求代碼格式規范,注釋齊全,程序可正常運行。 3.模型設計 實驗設計一個多層感知機,三層機構,只含一個隱藏層,輸入層,隱藏層,輸出層 1…

JAVA期末速成庫(11)第十二章

一、習題介紹 第十二章 Check Point&#xff1a;P454 12.1&#xff0c;12.9&#xff0c;12.10&#xff0c;12,12 二、習題及答案 12.1 What is the advantage of using exception handling? 12.1使用異常處理的優勢是什么? 答:使用異常處理有以下優勢&#xff1a; 1. 提高…

C++ 模板類的示例-數組

類模板可以有非通用類型參數&#xff1a;1&#xff09;通常是整型&#xff08;C20標準可以用其它的類型&#xff09;&#xff1b;2&#xff09;實例化模板時必須用常量表達式&#xff1b;3&#xff09;模板中不能修改參數的值&#xff1b;4&#xff09;可以為非通用類型參數提供…

Android中使用performClick觸發點擊事件

Android中使用performClick觸發點擊事件 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討在Android開發中如何使用 performClick() 方法來觸發點擊…

數據庫-python SQLite3

數據庫-python SQLite3 一&#xff1a;sqlite3 簡介二: sqlite3 流程1> demo2> sqlite3 流程 三&#xff1a;sqlite3 step1> create table2> insert into3> update4> select1. fetchall()2. fetchone()3. fetchmany() 5> delete6> other step 四&#…