国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

Linux的進程ID號如何實現

2024-08-27 23:55:06
字體:
來源:轉載
供稿:網友
        這篇文章主要介紹“Linux的進程ID號怎么實現”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Linux的進程ID號怎么實現”文章能幫助大家解決問題。
 
  本文中的代碼摘自 Linux內核5.15.13版本。
 
  Linux進程總是會分配一個號碼用于在其命名空間中唯一地標識它們。該號碼被稱作進程ID號,簡稱PID。用fork或clone產生的每個進程都由內核自動地分配了一個新的唯一的PID值。
 
一、進程ID
1.1、其他ID
  每個進程除了PID這個特征值之外,還有其他的ID。有下列幾種可能的類型
 
  1、 處于某個線程組(在一個進程中,以標志CLONE_THREAD來調用clone建立的該進程的不同的執行上下文,我們在后文會看到)中的所有進程都有統一的線程組ID( TGID)。如果進程沒有使用線程,則其PID和TGID相同。線程組中的主進程被稱作組長( group leader)。通過clone創建的所有線程的task_struct的group_leader成員,會指向組長的task_struct實例。
 
  2、另外,獨立進程可以合并成進程組(使用setpgrp系統調用)。進程組成員的task_struct的pgrp屬性值都是相同的,即進程組組長的PID。進程組簡化了向組的所有成員發送信號的操作,這對于各種系統程序設計應用(參見系統程序設計方面的文獻,例如[ SR05])是有用的。請注意,用管道連接的進程包含在同一個進程組中。
 
  3、 幾個進程組可以合并成一個會話。會話中的所有進程都有同樣的會話ID,保存在task_struct的session成員中。 SID可以使用setsid系統調用設置。它可以用于終端程序設計。
 
1.2、全局ID和局部ID
  名空間增加了PID管理的復雜性。 PID命名空間按層次組織。在建立一個新的命名空間時,該命名空間中的所有PID對父命名空間都是可見的,但子命名空間無法看到父命名空間的PID。但這意味著某些進程具有多個PID,凡可以看到該進程的命名空間,都會為其分配一個PID。 這必須反映在數據結構中。我們必須區分局部ID和全局ID。
 
  1、 全局ID是在內核本身和初始命名空間中的唯一ID號,在系統啟動期間開始的init進程即屬于初始命名空間。對每個ID類型,都有一個給定的全局ID,保證在整個系統中是唯一的。
 
  2、 局部ID屬于某個特定的命名空間,不具備全局有效性。對每個ID類型,它們在所屬的命名空間內部有效,但類型相同、值也相同的ID可能出現在不同的命名空間中。
 
1.3、ID實現
  全局PID和TGID直接保存在task_struct中,分別是task_struct的pid和tgid成員,在sched.h文件里:
 
struct task_struct {...pid_t pid;pid_t tgid;...}
  這兩項都是pid_t類型,該類型定義為__kernel_pid_t,后者由各個體系結構分別定義。通常定義為int,即可以同時使用232個不同的ID。
 
二、管理PID
  一個小型的子系統稱之為PID分配器( pid allocator)用于加速新ID的分配。此外,內核需要提供輔助函數,以實現通過ID及其類型查找進程的task_struct的功能,以及將ID的內核表示形式和用戶空間可見的數值進行轉換的功能。
 
2.1、PID命名空間的表示方式
  在pid_namespace.h文件內有如下定義:
 
struct pid_namespace {
struct idr idr;
struct rcu_head rcu;
unsigned int pid_allocated;
struct task_struct *child_reaper;
struct kmem_cache *pid_cachep;
unsigned int level;
struct pid_namespace *parent;#ifdef CONFIG_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;
  每個PID命名空間都具有一個進程,其發揮的作用相當于全局的init進程。 init的一個目的是對孤兒進程調用wait4,命名空間局部的init變體也必須完成該工作。 child_reaper保存了指向該進程的task_struct的指針。
 
  parent是指向父命名空間的指針, level表示當前命名空間在命名空間層次結構中的深度。初始命名空間的level為0,該命名空間的子空間level為1,下一層的子空間level為2,依次遞推。level的計算比較重要,因為level較高的命名空間中的ID,對level較低的命名空間來說是可見的。從給定的level設置,內核即可推斷進程會關聯到多少個ID。
 
2.2、PID的管理
2.2.1、PID的數據結構
  PID的管理圍繞兩個數據結構展開: struct pid是內核對PID的內部表示,而struct upid則表示特定的命名空間中可見的信息。兩個結構的定義在文件pid.h內,分別如下:
 
/*
 * What is struct pid?
 *
 * A struct pid is the kernel's internal notion of a process identifier.
 * It refers to inpidual tasks, process groups, and sessions.  While
 * there are processes attached to it the struct pid lives in a hash
 * table, so it and then the processes that it refers to can be found
 * quickly from the numeric pid value.  The attached processes may be
 * quickly accessed by following pointers from struct pid.
 *
 * Storing pid_t values in the kernel and referring to them later has a
 * problem.  The process originally with that pid may have exited and the
 * pid allocator wrapped, and another process could have come along
 * and been assigned that pid.
 *
 * Referring to user space processes by holding a reference to struct
 * task_struct has a problem.  When the user space process exits
 * the now useless task_struct is still kept.  A task_struct plus a
 * stack consumes around 10K of low kernel memory.  More precisely
 * this is THREAD_SIZE + sizeof(struct task_struct).  By comparison
 * a struct pid is about 64 bytes.
 *
 * Holding a reference to struct pid solves both of these problems.
 * It is small so holding a reference does not consume a lot of
 * resources, and since a new struct pid is allocated when the numeric pid
 * value is reused (when pids wrap around) we don't mistakenly refer to new
 * processes.
 *//*
 * struct upid is used to get the id of the struct pid, as it is
 * seen in particular namespace. Later the struct pid is found with
 * find_pid_ns() using the int nr and struct pid_namespace *ns.
 */struct upid {
int nr;
struct pid_namespace *ns;};struct pid{
refcount_t count;
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_t wait_pidfd;
struct rcu_head rcu;
struct upid numbers[1];};
  對于struct upid, nr表示ID的數值, ns是指向該ID所屬的命名空間的指針。所有的upid實例都保存在一個散列表中。 pid_chain用內核的標準方法實現了散列溢出鏈表。struct pid的定義首先是一個引用計數器count。 tasks是一個數組,每個數組項都是一個散列表頭,對應于一個ID類型。這樣做是必要的,因為一個ID可能用于幾個進程。所有共享同一給定ID的task_struct實例,都通過該列表連接起來。 PIDTYPE_MAX表示ID類型的數目:
 
enum pid_type{
PIDTYPE_PID,
PIDTYPE_TGID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX,};
2.2.2、PID與進程的聯系
  一個進程可能在多個命名空間中可見,而其在各個命名空間中的局部ID各不相同。 level表示可以看到該進程的命名空間的數目(換言之,即包含該進程的命名空間在命名空間層次結構中的深度),而numbers是一個upid實例的數組,每個數組項都對應于一個命名空間。注意該數組形式上只有一個數組項,如果一個進程只包含在全局命名空間中,那么確實如此。由于該數組位于結構的末尾,因此只要分配更多的內存空間,即可向數組添加附加的項。
 
  由于所有共享同一ID的task_struct實例都按進程存儲在一個散列表中,因此需要在struct task_struct中增加一個散列表元素在sched.h文件內進程的結構頭定義內有
 
struct task_struct {...
/* PID/PID hash table linkage. */
struct pid *thread_pid;
struct hlist_node pid_links[PIDTYPE_MAX];
struct list_head thread_group;
struct list_head thread_node;...};
  將task_struct連接到表頭在pid_links中的散列表上。
 
2.2.3、查找PID
  假如已經分配了struct pid的一個新實例,并設置用于給定的ID類型。它會如下附加到task_struct,在kernel/pid.c文件內:
 
static struct pid **task_pid_ptr(struct task_struct *task, enum pid_type type){
return (type == PIDTYPE_PID) ?
&task->thread_pid :
&task->signal->pids[type];}/*
 * attach_pid() must be called with the tasklist_lock write-held.
 */void attach_pid(struct task_struct *task, enum pid_type type){
struct pid *pid = *task_pid_ptr(task, type);
hlist_add_head_rcu(&task->pid_links[type], &pid->tasks[type]);}
  這里建立了雙向連接: task_struct可以通過task_struct->pids[type]->pid訪問pid實例。而從pid實例開始,可以遍歷tasks[type]散列表找到task_struct。 hlist_add_head_rcu是遍歷散列表的標準函數。
 
三、生成唯一的PID
  除了管理PID之外,內核還負責提供機制來生成唯一的PID。為跟蹤已經分配和仍然可用的PID,內核使用一個大的位圖,其中每個PID由一個比特標識。 PID的值可通過對應比特在位圖中的位置計算而來。因此,分配一個空閑的PID,本質上就等同于尋找位圖中第一個值為0的比特,接下來將該比特設置為1。反之,釋放一個PID可通過將對應的比特從1切換為0來實現。在建立一個新進程時,進程可能在多個命名空間中是可見的。對每個這樣的命名空間,都需要生成一個局部PID。這是在alloc_pid中處理的,在文件kernel/pid.c內有:
 
struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid,
      size_t set_tid_size){
struct pid *pid;
enum pid_type type;
int i, nr;
struct pid_namespace *tmp;
struct upid *upid;
int retval = -ENOMEM;
 
/*
 * set_tid_size contains the size of the set_tid array. Starting at
 * the most nested currently active PID namespace it tells alloc_pid()
 * which PID to set for a process in that most nested PID namespace
 * up to set_tid_size PID namespaces. It does not have to set the PID
 * for a process in all nested PID namespaces but set_tid_size must
 * never be greater than the current ns->level + 1.
 */
if (set_tid_size > ns->level + 1)
return ERR_PTR(-EINVAL);
 
pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
if (!pid)
return ERR_PTR(retval);
 
tmp = ns;
pid->level = ns->level;
 
for (i = ns->level; i >= 0; i--) {
int tid = 0;
 
if (set_tid_size) {
tid = set_tid[ns->level - i];
 
retval = -EINVAL;
if (tid < 1 || tid >= pid_max)
goto out_free;
/*
 * Also fail if a PID != 1 is requested and
 * no PID 1 exists.
 */
if (tid != 1 && !tmp->child_reaper)
goto out_free;
retval = -EPERM;
if (!checkpoint_restore_ns_capable(tmp->user_ns))
goto out_free;
set_tid_size--;
}
 
idr_preload(GFP_KERNEL);
spin_lock_irq(&pidmap_lock);
 
if (tid) {
nr = idr_alloc(&tmp->idr, NULL, tid,
       tid + 1, GFP_ATOMIC);
/*
 * If ENOSPC is returned it means that the PID is
 * alreay in use. Return EEXIST in that case.
 */
if (nr == -ENOSPC)
nr = -EEXIST;
} else {
int pid_min = 1;
/*
 * init really needs pid 1, but after reaching the
 * maximum wrap back to RESERVED_PIDS
 */
if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS)
pid_min = RESERVED_PIDS;
 
/*
 * Store a null pointer so find_pid_ns does not find
 * a partially initialized PID (see below).
 */
nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min,
      pid_max, GFP_ATOMIC);
}
spin_unlock_irq(&pidmap_lock);
idr_preload_end();
 
if (nr < 0) {
retval = (nr == -ENOSPC) ? -EAGAIN : nr;
goto out_free;
}
 
pid->numbers[i].nr = nr;
pid->numbers[i].ns = tmp;
tmp = tmp->parent;
}
  upid->ns->pid_allocated++;
}
spin_unlock_irq(&pidmap_lock);
 
return pid;out_unlock:
spin_unlock_irq(&pidmap_lock);
put_pid_ns(ns);out_free:
spin_lock_irq(&pidmap_lock);
while (++i <= ns->level) {
upid = pid->numbers + i;
idr_remove(&upid->ns->idr, upid->nr);
}
 
/* On failure to allocate the first pid, reset the state */
if (ns->pid_allocated == PIDNS_ADDING)
idr_set_cursor(&ns->idr, 0);
 
spin_unlock_irq(&pidmap_lock);
 
kmem_cache_free(ns->pid_cachep, pid);
return ERR_PTR(retval);}

(編輯:武林網)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 绩溪县| 牡丹江市| 祁东县| 临高县| 鹤壁市| 安阳县| 如皋市| 墨江| 彭泽县| 金昌市| 桦甸市| 仁化县| 瑞丽市| 行唐县| 昌黎县| 福泉市| 叙永县| 习水县| 桦甸市| 通道| 永新县| 饶阳县| 申扎县| 满洲里市| 荣成市| 青阳县| 墨竹工卡县| 乳山市| 扎赉特旗| 油尖旺区| 凤山市| 宿州市| 乌兰察布市| 天峨县| 桃源县| 桓台县| 永年县| 凤山县| 临朐县| 玛多县| 绩溪县|