0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何組織PID命名空間的各種ID?PID命名空間基本概念簡(jiǎn)析

冬至子 ? 來(lái)源:技術(shù)基本功修煉 ? 作者:郭玲 ? 2023-07-18 14:59 ? 次閱讀

Linux 支持以下命名空間類(lèi)型:

  • Mount (CLONE_NEWNS;2.4.19,2002)
  • UTS (CLONE_NEWUTS; 2.6.19,2006)
  • IPC (CLONE_NEWIPC; 2.6.19,2006)
  • PID (CLONE_NEWPID; 2.6.24,2008)
  • Network(CLONE_NEWNET;2.6.29,2009)
  • User (CLONE_NEWUSER;3.8,2013)
  • Cgroup(CLONE_NEWCGROUP;4.6,2016)

命名空間 API 由三個(gè)系統(tǒng)調(diào)用(clone()、unshare()和setns())以及許多/proc文件組成。CLONE_NEW* 常量包括:

CLONE_NEWIPC,CLONE_NEWNS , CLONE_NEWNET , CLONE_NEWPID ,CLONE_NEWUSERCLONE_NEWUTS 。

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

有二十多個(gè)不同的CLONE_*標(biāo)志 控制clone()操作的各個(gè)方面,包括父進(jìn)程和子進(jìn)程是否共享資源,例如虛擬內(nèi)存、打開(kāi)的文件描述符和信號(hào)配置。

如果在調(diào)用中指定了CLONE_NEW* 之一,則會(huì)創(chuàng)建相應(yīng)類(lèi)型的 新命名空間 ,并且新進(jìn)程將成為該****命名空間的成員;可以在flags中指定多個(gè) CLONE_NEW* 。

在本文中,我們將研究 clone系統(tǒng)調(diào)用的 PID 命名空間部分,以及內(nèi)核如何組織 PID 命名空間的各種ID。本文分析基于內(nèi)核版本 linux-5.15.60。

一、PID命名空間基本概念

PID命名空間隔離的全局資源是“進(jìn)程ID編號(hào)”空間。這意味著“不同PID命名空間”中的進(jìn)程可以具有“相同的進(jìn)程ID”。PID命名空間用于“在主機(jī)系統(tǒng)之間遷移的容器”,同時(shí)保持容器內(nèi)部進(jìn)程的相同進(jìn)程ID。

與傳統(tǒng)Linux(或UNIX)系統(tǒng)上的進(jìn)程一樣,在PID命名空間中的進(jìn)程ID是唯一的,并且從 PID 1開(kāi)始按順序分配。同樣地,與傳統(tǒng)Linux系統(tǒng)一樣,PID 1——init進(jìn)程是特殊的:它是在命名空間內(nèi)創(chuàng)建的第一個(gè)進(jìn)程,并且在命名空間內(nèi)執(zhí)行某些管理任務(wù)。

通過(guò)調(diào)用帶有 CLONE_NEWPID 標(biāo)志的clone()函數(shù)可以“創(chuàng)建一個(gè)新的PID命名空間”。我們將展示一個(gè)簡(jiǎn)單的示例程序,使用clone()函數(shù)創(chuàng)建一個(gè)新的PID命名空間,并使用該程序來(lái)解釋PID命名空間的一些基本概念。

主程序使用clone()函數(shù)創(chuàng)建一個(gè)新的PID命名空間,并顯示生成子進(jìn)程的PID:

child_pid = clone(childFunc,
              child_stack + STACK_SIZE,   /* Points to start of downwardly growing stack */
              CLONE_NEWPID | SIGCHLD, argv[1]);
printf("PID returned by clone(): %ldn", (long) child_pid);

新創(chuàng)建的子進(jìn)程在childFunc()中開(kāi)始執(zhí)行,該函數(shù)接收clone()調(diào)用的最后一個(gè)參數(shù)(argv[1])作為它的參數(shù)。這個(gè)參數(shù)后面再解釋。childFunc()函數(shù)顯示由clone()創(chuàng)建的子進(jìn)程的進(jìn)程ID和父進(jìn)程ID,并最后執(zhí)行標(biāo)準(zhǔn)的sleep程序:

printf("childFunc(): PID = %ldn", (long) getpid());
printf("ChildFunc(): PPID = %ldn", (long) getppid()); 
   ...
execlp("sleep", "sleep", "1000", (char *) NULL);

當(dāng)我們運(yùn)行這個(gè)程序時(shí),輸出的前幾行如下:

[root@haha demo]# ./pidns_init_sleep /proc30
PID returned by clone(): 25070
childFunc(): PID = 1
childFunc(): PPID = 0
Mounting
procfs at /proc30

前兩行輸出顯示了從兩個(gè)不同PID命名空間的角度來(lái)看子進(jìn)程的PID:調(diào)用clone()的“調(diào)用者的命名空間”和“子進(jìn)程所在的命名空間”。

換句話說(shuō),子進(jìn)程有兩個(gè)PID:在父命名空間中為 25070,在clone()調(diào)用創(chuàng)建的新PID命名空間中為1。下一行輸出顯示了子進(jìn)程在所在PID命名空間中的父進(jìn)程ID(即getppid()返回的值)。

父進(jìn)程PID為0,展示了PID命名空間操作的一個(gè)小特殊情況。

正如我們后面詳細(xì)介紹的那樣,PID命名空間形成了一個(gè)層次結(jié)構(gòu):一個(gè)進(jìn)程只能看到“自己所在的PID命名空間”和 嵌套在該P(yáng)ID命名空間下的“子命名空間中”的進(jìn)程。

由于由clone()“創(chuàng)建的子進(jìn)程的父進(jìn)程”處于不同的命名空間中,子進(jìn)程無(wú)法“看到”父進(jìn)程;因此,getppid()將父進(jìn)程PID報(bào)告為零。

要解釋pidns_init_sleep的最后一行輸出,我們需要回到一個(gè)我們?cè)谟懻揷hildFunc()函數(shù)實(shí)現(xiàn)時(shí)跳過(guò)的代碼片段。

在Linux系統(tǒng)上,每個(gè)進(jìn)程都有一個(gè)特殊的目錄路徑"/proc/PID",其中PID表示進(jìn)程的ID。這個(gè)目錄包含了描述該進(jìn)程的虛擬文件。

這個(gè)機(jī)制被稱(chēng)為PID命名空間模型。在一個(gè)PID命名空間中,只有屬于該命名空間或其子命名空間的進(jìn)程的信息會(huì)顯示在對(duì)應(yīng)的"/proc/PID"目錄中。

[root@haha linux-5.15.60]# mount |grep "proc on /proc"
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
proc on /proc2 type proc (rw,relatime)
proc on /proc2 type proc (rw,relatime)
proc on /proc10 type proc (rw,relatime)
proc on /proc20 type proc (rw,relatime)
proc on /proc30 type proc (rw,relatime)
[root@haha linux-5.15.60]#

但是,要使與PID命名空間對(duì)應(yīng)的"/proc/PID"目錄可見(jiàn),需要將proc文件系統(tǒng)掛載到該P(yáng)ID命名空間。我們可以在一個(gè)PID命名空間內(nèi)的shell中,運(yùn)行 mount命令來(lái)實(shí)現(xiàn):

mount -t proc proc /mount_point

另外,也可以使用mount()系統(tǒng)調(diào)用來(lái)掛載procfs,我們程序的childFunc()函數(shù)就是這樣的:

char *mount_point = arg;
  if (mount_point != NULL) {
      mkdir(mount_point, 0555);       /* Create directory for mount point */
      if (mount("proc", mount_point, "proc", 0,NULL) == -1)
          errExit("mount");
      printf("Mounting procfs at %sn", mount_point);
   }

在我們的shell會(huì)話中,在/proc上掛載的procfs將顯示父PID命名空間中可見(jiàn)的進(jìn)程的PID子目錄,而在/proc30 上掛載的procfs將顯示駐留在子PID命名空間中的進(jìn)程的PID子目錄。

讓我們回到運(yùn)行pidns_init_sleep的shell會(huì)話。我們停止程序并使用ps命令在父命名空間的上下文中檢查父進(jìn)程和子進(jìn)程的一些細(xì)節(jié)。

圖片

上述輸出的最后一行中的"PPID"值(25069)顯示“執(zhí)行sleep的進(jìn)程”的父進(jìn)程是執(zhí)行pidns_init_sleep的進(jìn)程。

通過(guò)使用readlink命令來(lái)顯示/proc/PID/ns/pid符號(hào)鏈接,我們可以看到這兩個(gè)進(jìn)程位于不同的PID命名空間中:

[root@haha demo]# readlink /proc/25069/ns/pid
pid:[4026531836]
[root@haha demo]# readlink /proc/25070/ns/pid
pid:[4026537948]
[root@haha demo]#

此時(shí),我們還可以使用新掛載的procfs來(lái)獲取有關(guān)新PID命名空間中進(jìn)程的信息,從該命名空間的角度來(lái)看。首先,我們可以使用以下命令獲取該命名空間中的PID列表:

[root@haha demo]# ls -d /proc30/[1-9]*
/proc30/1

如上所示,PID命名空間只包含一個(gè)進(jìn)程,其PID(在該命名空間內(nèi))為1。我們還可以使用/proc/PID/status文件作為另一種方法,獲取關(guān)于該進(jìn)程的一些相同信息,就像我們之前在shell會(huì)話中看到的那樣:

[root@haha demo]# cat /proc30/1/status | egrep '^(Name|PP*id)'
Name: sleep
Pid:   1
PPid:  0
[root@haha
demo]#

文件中的PPid字段為0,與getppid()報(bào)告子進(jìn)程的父進(jìn)程ID為0的事實(shí)相匹配。(子命名空間看不到父命名空間的進(jìn)程)

二、嵌套的PID命名空間

如前所述,PID(進(jìn)程標(biāo)識(shí)符)命名空間以父子關(guān)系的層級(jí)嵌套方式存在。在一個(gè)PID命名空間內(nèi),可以看到同一命名空間中的所有其他進(jìn)程,以及屬于后代命名空間的所有進(jìn)程。

在這里,“看到”意味著能夠進(jìn)行基于特定PID的系統(tǒng)調(diào)用(例如,使用kill()向進(jìn)程發(fā)送信號(hào))。子PID命名空間中的進(jìn)程無(wú)法看到僅存在于父PID命名空間(或更遠(yuǎn)的祖先命名空間)中的進(jìn)程。

一個(gè)進(jìn)程在PID命名空間層級(jí)中的每一層都會(huì)有一個(gè)PID,從其所在的PID命名空間一直到根PID命名空間。調(diào)用getpid()始終報(bào)告與進(jìn)程所在命名空間相關(guān)聯(lián)的PID。

我們可以使用這里顯示的程序(multi_pidns.c)來(lái)展示進(jìn)程在每個(gè)可見(jiàn)的命名空間中具有不同的PID。為簡(jiǎn)潔起見(jiàn),我們將簡(jiǎn)單地解釋程序的功能,而不是逐行解析其代碼。

該程序以嵌套PID命名空間中的子進(jìn)程遞歸方式創(chuàng)建一系列子進(jìn)程。在調(diào)用程序時(shí)指定的命令行參數(shù)確定要?jiǎng)?chuàng)建多少個(gè)子進(jìn)程和PID命名空間:

./multi_pidns 5

除了創(chuàng)建一個(gè)新的子進(jìn)程,每個(gè)遞歸步驟還在一個(gè)唯一命名的掛載點(diǎn)上掛載procfs文件系統(tǒng)。在遞歸的最后,最后一個(gè)子進(jìn)程執(zhí)行了sleep程序。上述命令行輸出如下:

[root@haha demo]# ls -d /proc4/[1-9]* 
/proc4/1  /proc4/2  /proc4/3  /proc4/4  /proc4/5
[root@haha demo]# ls -d /proc3/[1-9]* 
/proc3/1  /proc3/2  /proc3/3  /proc3/4
[root@haha demo]# ls -d /proc2/[1-9]* 
/proc2/1  /proc2/2  /proc2/3
[root@haha demo]# ls -d /proc1/[1-9]* 
/proc1/1  /proc1/2
[root@haha demo]# ls -d /proc0/[1-9]* 
/proc0/1

查看每個(gè)procfs中的PID,我們可以看到每個(gè)連續(xù)的procfs "級(jí)別"包含的PID越來(lái)越少,這也表示了每個(gè)PID命名空間只顯示屬于該P(yáng)ID命名空間或其后代命名空間的進(jìn)程。

讓我們看下在所有可見(jiàn)的命名空間中,遞歸結(jié)束時(shí)的PID:

[root@haha demo]# grep -H 'Name:.*sleep'/proc?/[1-9]*/status
/proc0/1/status:Name:   sleep
/proc1/2/status:Name:   sleep
/proc2/3/status:Name:   sleep
/proc3/4/status:Name:   sleep
/proc4/5/status:Name:   sleep
[root@haha demo]#

換句話說(shuō),在最深層嵌套的 PID 命名空間 ( /proc0 ) 中,執(zhí)行sleep的進(jìn)程的 PID 為 1,而在創(chuàng)建的最頂層 PID 命名空間 ( /proc4 ) 中,該進(jìn)程的 PID 為 5。

三、內(nèi)核實(shí)現(xiàn)PID命名空間

要了解內(nèi)核如何組織和管理進(jìn)程ID,首先要知道進(jìn)程ID 的類(lèi)型:

內(nèi)核中進(jìn)程ID 的類(lèi)型用 pid_type 來(lái)描述,它定義在 includelinuxpid.h 中

enum pid_type {
  PIDTYPE_PID,
  PIDTYPE_TGID,
  PIDTYPE_PGID,
  PIDTYPE_SID,
  PIDTYPE_MAX,
};
  • PID 是內(nèi)核唯一區(qū)分每個(gè)進(jìn)程的ID。使用 fork 或 clone 系統(tǒng)調(diào)用時(shí)生成的進(jìn)程將被內(nèi)核分配一個(gè)新的唯一 PID 值。
  • TGID 是線程組ID。在一個(gè)進(jìn)程中,如果使用 clone_THREAD 標(biāo)志來(lái)調(diào)用 clone創(chuàng)建的進(jìn)程,那么它就是該進(jìn)程的一個(gè)線程(即輕量級(jí)進(jìn)程,Linux沒(méi)有嚴(yán)格的進(jìn)程概念),它們?cè)谝粋€(gè)線程組中。同一線程組中所有進(jìn)程都有相同的TGID,但由于是不同的進(jìn)程,所以它們的PID不同;線程的領(lǐng)導(dǎo)者(也稱(chēng)為主線程)的TGID 與其 PID 相同。
  • PGID 獨(dú)立進(jìn)程可以組成進(jìn)程組(使用 setpgrp 系統(tǒng)調(diào)用),進(jìn)程組可以簡(jiǎn)化向組內(nèi)所有進(jìn)程發(fā)送信號(hào)的操作。例如,通過(guò)管道連接的連接屬于同一個(gè)進(jìn)程組。進(jìn)程組ID 稱(chēng)為 PGID。進(jìn)程組中所有的進(jìn)程都有相同的 PGID,等于組長(zhǎng)的 PID。
  • SID 可以將多個(gè)進(jìn)程組組成一個(gè)會(huì)話組(使用 setsid 系統(tǒng)調(diào)用),可用于終端編程。會(huì)話組中所有進(jìn)程都有相同的SID,該SID 存儲(chǔ)在 task_struct 的 session 成員中。

PID命名空間的層級(jí)關(guān)系如下:有 4 個(gè)命名空間。父命名空間派生兩個(gè)子命名空間,其中一個(gè)子命名空間派生另一個(gè)子命名空間。

圖片

由于每個(gè)命名空間是相互隔離的,所以每個(gè)命名空間可以有一個(gè) PID 為1的進(jìn)程。由于命名空間的層次性,父命名空間是知道子命名空間的存在的,所以子命名空間需要映射到父命名空間,

因此上圖中 第 1 級(jí) 的兩個(gè)兩個(gè)子命名空間中的 6 個(gè)進(jìn)程 都映射到 其父命名空間的 PID 號(hào) 5~ 10.

系統(tǒng)使用 struct task_struct 表示一個(gè)進(jìn)程,進(jìn)程中存儲(chǔ)了全局ID 和 本地ID。

全局ID ---- 內(nèi)核本身和初始命名空間中的唯一ID。 系統(tǒng)啟動(dòng)時(shí) init 進(jìn)程屬于初始命名空間。全局ID 包括 pid_t pid 和 pid_t tgid 。默認(rèn)情況下 pid_t 用 int 表示。

本地ID ---- 對(duì)于一個(gè)特定的命名空間來(lái)說(shuō),它在其命名空間中分配的ID就是本地ID。本地ID 用 struct pid * thread_pid 表示。

圖片

PID 數(shù)據(jù)結(jié)構(gòu)

成員 tasks 是一個(gè)數(shù)組,每個(gè)數(shù)組項(xiàng)是一個(gè)哈希表頭,對(duì)應(yīng)一個(gè)ID 類(lèi)型,因此一個(gè)ID 可用于多個(gè)進(jìn)程(比如多個(gè)進(jìn)程的進(jìn)程組相同)。

struct upid {
  int nr;// ID 的具體值
  struct pid_namespace* ns;
};
struct pid {
  refcount_t count;// 引用數(shù), 一個(gè)PID 可能用于多個(gè)進(jìn)程
  unsigned int level;
  spinlock_t lock;
   /* lists of tasks that use this pid */
  struct hlist_head tasks[PIDTYPE_MAX];
  struct hlist_head inodes;
   /* wait queue for pidfd notifications */
  wait_queue_head_twait_pidfd;
  struct rcu_head rcu;
   struct upid numbers[1]; // 柔性數(shù)組,特定命名空間可見(jiàn)的信息, 數(shù)組大小為level
};

PID 命名空間結(jié)構(gòu)

struct pid_namespace {
  struct idr idr;
  struct rcu_head rcu;
  unsigned int pid_allocated; // 已分配多少個(gè)pid
  struct task_struct* child_reaper; // 指向當(dāng)前命名空間的 init 進(jìn)程,每個(gè)命名空間都有一個(gè)相當(dāng)于全局init進(jìn)程的進(jìn)程
  struct kmem_cache* pid_cachep; // 指向分配pid 的slab地址
  unsigned int level;// 當(dāng)前命名空間的級(jí)別。初始命名空間的級(jí)別為0,其子命名空間級(jí)別為1,依次遞增。
  struct pid_namespace* parent; // 指向父命名空間
#ifdefCONFIG_BSD_PROCESS_ACCT
  struct fs_pin* bacct;
#endif
  struct user_namespace* user_ns;
  struct ucounts* ucounts;
  int reboot;/* group exit code if this pidns was rebooted */
  struct ns_common ns;
} __randomize_layout;

假設(shè)一個(gè)進(jìn)程組中有A、B 兩個(gè)進(jìn)程,且進(jìn)程組組長(zhǎng)為A,進(jìn)程A 是在 2 級(jí)命名空間中創(chuàng)建的,它的pid為45 ,映射到1級(jí)命名空間,分配給它的pid為123;然后它被映射到級(jí)別 0 的命名空間,分配給它的 pid 是 27760。

進(jìn)程A 創(chuàng)建了一個(gè)線程 A1, 那么 A, A1, B 的命名空間和進(jìn)程的關(guān)系如下圖所示:

  • 進(jìn)程 A 的成員 struct pid* thread_pid 是內(nèi)核對(duì)進(jìn)程標(biāo)識(shí)符的內(nèi)部表示方式。
  • struct pid 以哈希鏈表的方式存儲(chǔ),可以通過(guò)數(shù)字pid值快速找到它和它所引用的進(jìn)程。
  • struct pid 保存了 嵌套的多個(gè)命名空間的指針 和 進(jìn)程在此命名空間的進(jìn)程標(biāo)識(shí)符 nr。
  • 命名空間使用基數(shù)樹(shù)保存當(dāng)前命名空間的 所有 struct pid,基數(shù)樹(shù)的索引就是 進(jìn)程在此命名空間的進(jìn)程標(biāo)識(shí)符。

圖片

最后有個(gè)問(wèn)題:如何通過(guò)PID 快速找到 task_struct?

內(nèi)核代碼通過(guò) find_task_by_vpid 來(lái)實(shí)現(xiàn)這個(gè)功能,其實(shí)通過(guò)上面這張圖就可以得出結(jié)論,簡(jiǎn)單的步驟如下:

首先,通過(guò) pid 和 命名空間nr,在基數(shù)樹(shù)上找到對(duì)應(yīng)的 struct pid;

然后,通過(guò) pid_type 在 struct pid 找到對(duì)應(yīng)的節(jié)點(diǎn)struct hlist_node;

最后,根據(jù)內(nèi)核的 container_of 機(jī)制 和 struct hlist_node 可以找到 struct task_struct 結(jié)構(gòu)體。

struct task_struct* find_task_by_vpid(pid_t vnr) {
  return find_task_by_pid_ns(vnr,task_active_pid_ns(current));
}


struct task_struct* find_task_by_pid_ns(pid_t nr, struct pid_namespace* ns) {
  RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "find_task_by_pid_ns() needs rcu_read_lock() protection");
  return pid_task(find_pid_ns(nr, ns),PIDTYPE_PID);
}


struct pid* find_pid_ns(int nr, struct pid_namespace* ns) {
  return idr_find(&ns- >idr, nr);
}


struct task_struct* pid_task(struct pid* pid, enum pid_type type) {
  struct task_struct* result = NULL;
  if (pid)
{
      structhlist_node* first;
      first = rcu_dereference_check(hlist_first_rcu(&pid- >tasks[type]),
          lockdep_tasklist_lock_is_held());
      if (first)
          result =hlist_entry(first, struct task_struct, pid_links[(type)]);
   }
  return result;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 存儲(chǔ)器
    +關(guān)注

    關(guān)注

    38

    文章

    7366

    瀏覽量

    163099
  • Linux系統(tǒng)
    +關(guān)注

    關(guān)注

    4

    文章

    587

    瀏覽量

    27182
  • PID控制
    +關(guān)注

    關(guān)注

    10

    文章

    449

    瀏覽量

    39930
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    鴻蒙TypeScript學(xué)習(xí)第19天【命名空間

    命名空間一個(gè)最明確的目的就是解決重名問(wèn)題。
    的頭像 發(fā)表于 04-17 15:43 ?768次閱讀
    鴻蒙TypeScript學(xué)習(xí)第19天【<b class='flag-5'>命名</b><b class='flag-5'>空間</b>】

    C++筆記008:C++命名空間 namespace的作用和使用解析

    。因此引入命名空間(namespace)這個(gè)概念,專(zhuān)門(mén)用于解決上面的問(wèn)題,就像在“A”這個(gè)名字前面加上額外的附加信息一樣(額外的附加信息…..這句是不是病句),命名
    發(fā)表于 08-11 12:30

    Linux的命名空間機(jī)制

    Linux命名空間概述
    發(fā)表于 03-18 14:40

    命名空間的實(shí)現(xiàn)

    (1) 在用fork或clone系統(tǒng)調(diào)用創(chuàng)建新進(jìn)程時(shí),有特定的選項(xiàng)可以控制是與父進(jìn)程共享命名空間,還是建立新的命名空間。(2) unshare系統(tǒng)調(diào)用將進(jìn)程的某些部分從父進(jìn)程分離,其中
    發(fā)表于 05-24 06:21

    hbase shell創(chuàng)建命名空間

    一.hbase shell創(chuàng)建命名空間hbase shellcreate_namespace "gofish"二.python實(shí)現(xiàn)hbase增刪改查# -*- coding
    發(fā)表于 07-28 06:45

    python常規(guī)包與命名空間

    python常規(guī)包與命名空間包1. 常規(guī)包在 Python 3.3 之前或者說(shuō) Python 2 中,一個(gè)包想要被導(dǎo)入使用,那么該包內(nèi)必須要有 __init__.py 文件,這個(gè)文件是 Python
    發(fā)表于 03-11 15:46

    nvs_open和nvs_get從不存在的命名空間中工作會(huì)有何影響?

    我有一些設(shè)備在它們的 nvs 存儲(chǔ)中有不同的命名空間和分區(qū)來(lái)存儲(chǔ)不同的值,但共享相同的代碼。在我的代碼中,當(dāng)特定命名空間不存在時(shí),我已經(jīng)做了例外處理:代碼:esp_err_t ret
    發(fā)表于 03-01 09:03

    nvs_open和nvs_get從不存在的命名空間中工作是怎么回事?

    我有一些設(shè)備在它們的 nvs 存儲(chǔ)中有不同的命名空間和分區(qū)來(lái)存儲(chǔ)不同的值,但共享相同的代碼。在我的代碼中,當(dāng)特定命名空間不存在時(shí),我已經(jīng)做了例外處理:esp_err_t ret = n
    發(fā)表于 04-14 06:30

    模糊PID控制和空間矢量調(diào)制的通用變頻器設(shè)計(jì)

    模糊PID控制和空間矢量調(diào)制的通用變頻器設(shè)計(jì)
    發(fā)表于 04-13 15:42 ?27次下載

    集群模式_Data_ONTAP_中的命名空間

    集群模式_Data_ONTAP_中的命名空間
    發(fā)表于 12-28 11:17 ?0次下載

    C++中命名空間的幾大用法

    譯者注:可能很多程序員對(duì)C++已經(jīng)非常熟悉,但是對(duì)命名空間經(jīng)常使用到的地方還不是很明白,這篇文章就針對(duì)命名空間這一塊做了一個(gè)敘述。 命名
    發(fā)表于 09-28 18:31 ?0次下載

    基于PID調(diào)節(jié)相關(guān)的15個(gè)基本概念詳解

    PID入門(mén)讀此文,必須熟透于心的15個(gè)PID基本概念
    的頭像 發(fā)表于 01-08 09:14 ?6588次閱讀
    基于<b class='flag-5'>PID</b>調(diào)節(jié)相關(guān)的15個(gè)<b class='flag-5'>基本概念</b>詳解

    關(guān)于工業(yè)控制PID系統(tǒng)中的十五個(gè)基本概念

    PID調(diào)節(jié)系統(tǒng)PID功能由PID調(diào)節(jié)器或DCS系統(tǒng)內(nèi)部功能程序模塊實(shí)現(xiàn),了解與PID調(diào)節(jié)相關(guān)的一些基本概念,有助于
    發(fā)表于 11-13 14:21 ?1854次閱讀

    一文了解C++的命名空間

    在C++中,變量、函數(shù)和類(lèi)都是大量存在的,這些變量、函數(shù)和類(lèi)的名稱(chēng)將都存在于全局命名空間中,會(huì)導(dǎo)致很多沖突, 使用命名空間的目的是對(duì)標(biāo)識(shí)符的名稱(chēng)進(jìn)行本地化,以避免
    的頭像 發(fā)表于 06-29 14:48 ?2243次閱讀
    一文了解C++的<b class='flag-5'>命名</b><b class='flag-5'>空間</b>

    PID剛?cè)腴T(mén)?新手必看的15個(gè)PID基本概念!

    PID調(diào)節(jié)系統(tǒng)PID功能由PID調(diào)節(jié)器或DCS系統(tǒng)內(nèi)部功能程序模塊實(shí)現(xiàn),了解與PID調(diào)節(jié)相關(guān)的一些基本概念,有助于
    的頭像 發(fā)表于 09-25 19:40 ?2024次閱讀
    <b class='flag-5'>PID</b>剛?cè)腴T(mén)?新手必看的15個(gè)<b class='flag-5'>PID</b><b class='flag-5'>基本概念</b>!