|
什么是程序?程序:可執(zhí)行文件或者包含一堆可運行CPU指令的和數(shù)據(jù)
什么是進程?進程 = 程序 + 執(zhí)行
進程是執(zhí)行中的程序,除了可執(zhí)行代碼外還包含進程的活動信息和數(shù)據(jù),比如用來存放函數(shù)變量、局部變量、返回值的用戶棧,存放進程相關(guān)數(shù)據(jù)的數(shù)據(jù)段,內(nèi)核中進程間切換的內(nèi)核棧,動態(tài)分配的堆。
進程是系統(tǒng)分配資源的基本單位(內(nèi)存、CPU時間片)
進程是用來實現(xiàn)多進程并發(fā)執(zhí)行的一個實體,實現(xiàn)對CPU的虛擬化,讓每個進程感覺都擁有一個CPU,核心技術(shù)就是上下文切換和進程調(diào)度。
早期操作系統(tǒng)程序都是單個運行的,CPU利用率低下,為了提高CPU的利用率,加載多個程序到內(nèi)存并發(fā)運行,在單核CPU中這種屬于偽并發(fā)。
其實在同一時間只運行一個程序
0q2ntwqaodt640133689456.png (38.24 KB, 下載次數(shù): 0)
下載附件
保存到相冊
0q2ntwqaodt640133689456.png
前天 06:41 上傳
進程控制塊描述符進程控制塊描述符task_struct主要包含:
進程狀態(tài)(state):表示進程當(dāng)前的狀態(tài),比如運行、睡眠、停止等。
/* Used in tsk->__state: */
#define TASK_RUNNING 0x00000000
#define TASK_INTERRUPTIBLE 0x00000001
#define TASK_UNINTERRUPTIBLE 0x00000002
#define __TASK_STOPPED 0x00000004
#define __TASK_TRACED 0x00000008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x00000010
#define EXIT_ZOMBIE 0x00000020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
進程調(diào)度信息(sched_info):包括進程的調(diào)度策略、優(yōu)先級等信息
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
struct sched_entity se;
struct sched_rt_entity rt;
struct sched_dl_entity dl;
struct sched_dl_entity *dl_server;
const struct sched_class *sched_class;on_rq:表示進程是否在就緒隊列中,即是否正在等待被調(diào)度執(zhí)行。
prio:表示進程的動態(tài)優(yōu)先級。
static_prio:表示進程的靜態(tài)優(yōu)先級。
normal_prio:表示進程的普通優(yōu)先級。
rt_priority:表示實時進程的優(yōu)先級。
se:sched_entity 結(jié)構(gòu)體,用于普通進程的調(diào)度實體。
rt:sched_rt_entity 結(jié)構(gòu)體,用于實時進程的調(diào)度實體。
dl:sched_dl_entity 結(jié)構(gòu)體,用于周期性實時進程的調(diào)度實體。
dl_server:指向調(diào)度該進程的周期性實時進程的指針。
sched_class:指向調(diào)度類(sched_class)的指針,用于確定進程的調(diào)度策略和行為。
進程標(biāo)識符(pid):唯一標(biāo)識一個進程的數(shù)字標(biāo)識符,內(nèi)核使用bitmap保證進程分配唯一的pid。
一個線程組中所有的線程使用和線程組組長相同的pid,它會被存進tgid中
使用getpid()系統(tǒng)調(diào)用獲取pid
pid_t pid;
pid_t tgid;進程堆棧(stack):用于保存進程的函數(shù)調(diào)用棧信息。
void *stack;引用計數(shù)(usage):用于跟蹤進程的引用計數(shù),確保在不再需要時能夠正確釋放資源。
refcount_t usage;進程標(biāo)志位(flags):存儲進程的各種標(biāo)志位信息,比如是否在運行、是否被掛起等。
unsigned int flags;多處理器支持字段(on_cpu、wake_entry):用于處理多處理器環(huán)境下的調(diào)度和喚醒操作。
#ifdef CONFIG_SMP
int on_cpu;
struct __call_single_node wake_entry;
unsigned int wakee_flips;
unsigned long wakee_flip_decay_ts;
struct task_struct *last_wakee;
/*
* recent_used_cpu is initially set as the last CPU used by a task
* that wakes affine another task. Waker/wakee relationships can
* push tasks around a CPU where each wakeup moves to the next one.
* Tracking a recently used CPU allows a quick search for a recently
* used CPU that may be idle.
*/
int recent_used_cpu;
int wake_cpu;
#endif進程間家庭關(guān)系
/*
* Pointers to the (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->real_parent->pid)
*/
/* Real parent process: */
struct task_struct __rcu *real_parent;
/* Recipient of SIGCHLD, wait4() reports: */
struct task_struct __rcu *parent;
/*
* Children/sibling form the list of natural children:
*/
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;
進程的5種狀態(tài)創(chuàng)建態(tài)(New):當(dāng)進程剛被創(chuàng)建時,處于創(chuàng)建態(tài)。在這個階段,操作系統(tǒng)正在為進程分配資源和初始化進程控制塊等信息。
就緒態(tài)(Ready):進程已經(jīng)準(zhǔn)備好運行,但由于還未獲得處理器資源,暫時無法執(zhí)行。進程在就緒隊列中等待被調(diào)度執(zhí)行。
運行態(tài)(Running):進程正在執(zhí)行指令,占用 CPU 資源。在任意時刻,每個 CPU 上只能有一個進程處于運行態(tài)。
阻塞態(tài)(Blocked):進程由于等待某種事件(如 I/O 操作完成、信號量變?yōu)榉橇愕龋┒鴷簳r無法繼續(xù)執(zhí)行,進入阻塞態(tài)。在等待期間,進程不占用 CPU 資源。
終止態(tài)(Terminated):進程執(zhí)行完畢或被操作系統(tǒng)終止后,進入終止態(tài)。在終止態(tài)下,進程的資源被釋放,但仍保留進程控制塊等信息,直到被操作系統(tǒng)回收。
d0umtjrlsti640133689556.png (125.38 KB, 下載次數(shù): 1)
下載附件
保存到相冊
d0umtjrlsti640133689556.png
前天 06:41 上傳
查看進程狀態(tài)
static void show_task(struct task_struct *volatile tsk)
{
unsigned int p_state = READ_ONCE(tsk->__state);
char state;
/*
* Cloned from kdb_task_state_char(), which is not entirely
* appropriate for calling from xmon. This could be moved
* to a common, generic, routine used by both.
*/
state = (p_state == TASK_RUNNING) ? 'R' :
(p_state & TASK_UNINTERRUPTIBLE) ? 'D' :
(p_state & TASK_STOPPED) ? 'T' :
(p_state & TASK_TRACED) ? 'C' :
(tsk->exit_state & EXIT_ZOMBIE) ? 'Z' :
(tsk->exit_state & EXIT_DEAD) ? 'E' :
(p_state & TASK_INTERRUPTIBLE) ? 'S' : '?';
printf("%16px %16lx %16px %6d %6d %c %2d %s
", tsk,
tsk->thread.ksp, tsk->thread.regs,
tsk->pid, rcu_dereference(tsk->parent)->pid,
state, task_cpu(tsk),
tsk->comm);
}
init_task
初始化了 init_task 結(jié)構(gòu)體的各個成員,包括線程信息、棧信息、調(diào)度優(yōu)先級、CPU 信息、內(nèi)存管理、信號處理、文件系統(tǒng)等等。
這些成員變量記錄了進程的狀態(tài)、資源分配、調(diào)度信息等重要內(nèi)容。
struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
.__state = 0,
.stack = init_stack,
.usage = REFCOUNT_INIT(2),
.flags = PF_KTHREAD,
.prio = MAX_PRIO - 20,
.static_prio = MAX_PRIO - 20,
.normal_prio = MAX_PRIO - 20,
.policy = SCHED_NORMAL,
.cpus_ptr = &init_task.cpus_mask,
.user_cpus_ptr = NULL,
.cpus_mask = CPU_MASK_ALL,
.nr_cpus_allowed= NR_CPUS,
.mm = NULL,
.active_mm = &init_mm,
.faults_disabled_mapping = NULL,
.restart_block = {
.fn = do_no_restart_syscall,
},
.se = {
.group_node = LIST_HEAD_INIT(init_task.se.group_node),
},
.rt = {
.run_list = LIST_HEAD_INIT(init_task.rt.run_list),
.time_slice = RR_TIMESLICE,
},
.tasks = LIST_HEAD_INIT(init_task.tasks),
.ptraced = LIST_HEAD_INIT(init_task.ptraced),
.ptrace_entry = LIST_HEAD_INIT(init_task.ptrace_entry),
.real_parent = &init_task,
.parent = &init_task,
.children = LIST_HEAD_INIT(init_task.children),
.sibling = LIST_HEAD_INIT(init_task.sibling),
.group_leader = &init_task,
RCU_POINTER_INITIALIZER(real_cred, &init_cred),
RCU_POINTER_INITIALIZER(cred, &init_cred),
.comm = INIT_TASK_COMM,
.thread = INIT_THREAD,
.fs = &init_fs,
.files = &init_files,
.signal = &init_signals,
.sighand = &init_sighand,
.nsproxy = &init_nsproxy,
.pending = {
.list = LIST_HEAD_INIT(init_task.pending.list),
.signal = {{0}}
},
.blocked = {{0}},
.alloc_lock = __SPIN_LOCK_UNLOCKED(init_task.alloc_lock),
.journal_info = NULL,
INIT_CPU_TIMERS(init_task)
.pi_lock = __RAW_SPIN_LOCK_UNLOCKED(init_task.pi_lock),
.timer_slack_ns = 50000, /* 50 usec default slack */
.thread_pid = &init_struct_pid,
.thread_node = LIST_HEAD_INIT(init_signals.thread_head),
INIT_PREV_CPUTIME(init_task)
};
EXPORT_SYMBOL(init_task);
進程創(chuàng)建函數(shù)
vp4u0flqegz640133689656.png (33.24 KB, 下載次數(shù): 0)
下載附件
保存到相冊
vp4u0flqegz640133689656.png
前天 06:41 上傳
fork
fork函數(shù)用于創(chuàng)建一個新的進程,新進程是調(diào)用進程(父進程)的副本。
函數(shù)名稱:fork
頭文件:#include
返回類型:pid_t(進程ID類型)
參數(shù):無參數(shù)
返回值:在父進程中,fork返回新創(chuàng)建子進程的進程ID(PID),在子進程中,fork返回0。如果出現(xiàn)錯誤,fork返回-1。
通過調(diào)用fork函數(shù),父進程會創(chuàng)建一個子進程,子進程會繼承父進程的數(shù)據(jù)、堆棧、文件描述符等信息,但是它們各自有獨立的內(nèi)存空間。
vfork
vfork函數(shù)用于創(chuàng)建一個新的進程,但與fork不同的是,vfork保證子進程先運行,調(diào)用do_fork時比fork多了兩個標(biāo)志位,
CLONE_VFORK和CLONE_VM,分別代表父進程被掛起,直到子進程釋放資源和父子進程運行在相同的內(nèi)存空間。
函數(shù)名稱:vfork
頭文件:#include
返回類型:pid_t(進程ID類型)
參數(shù):無參數(shù)
返回值:在父進程中,vfork返回新創(chuàng)建子進程的進程ID(PID),在子進程中,vfork返回0。如果出現(xiàn)錯誤,vfork返回-1。
clone
clone函數(shù)用于創(chuàng)建一個新的線程或進程,可以指定不同的選項來控制創(chuàng)建的行為
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, unsigned long newtls, pid_t *ctid */ );
函數(shù)名稱:clone
頭文件:#include
返回類型:int
參數(shù):
fn:指向新線程/進程入口點函數(shù)的指針
child_stack:子進程/線程的棧指針
flags:用于指定創(chuàng)建新線程/進程的選項
arg:傳遞給新線程/進程入口點函數(shù)的參數(shù)
...:可選參數(shù),包括ptid、newtls和ctid
返回值:成功時返回新線程/進程的PID(對于線程,返回0表示成功),失敗時返回-1。
kthread_create
kthread_create函數(shù)用于創(chuàng)建一個新的內(nèi)核線程,該線程在內(nèi)核空間中運行,可以執(zhí)行內(nèi)核級別的任務(wù)。
通過調(diào)用kthread_create函數(shù),可以在Linux內(nèi)核中創(chuàng)建一個新的內(nèi)核線程,用于執(zhí)行后臺任務(wù)、定時任務(wù)等內(nèi)核級別的工作。內(nèi)核線程與用戶空間的線程有所不同,它們在內(nèi)核空間中運行,可以訪問內(nèi)核數(shù)據(jù)結(jié)構(gòu)和執(zhí)行特權(quán)操作。
struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...);
函數(shù)名稱:kthread_create
返回類型:struct task_struct *(指向內(nèi)核線程結(jié)構(gòu)體的指針)
參數(shù):threadfn:指向內(nèi)核線程函數(shù)的指針,即內(nèi)核線程的入口點函數(shù)
data:傳遞給內(nèi)核線程函數(shù)的參數(shù)
namefmt:內(nèi)核線程的名稱格式字符串
...:可變參數(shù),用于指定內(nèi)核線程的調(diào)度優(yōu)先級等其他選項
功能:
返回值:成功時返回指向新創(chuàng)建內(nèi)核線程的task_struct結(jié)構(gòu)體指針,失敗時返回NULL。
do_fork
fork/vfork/clone/kthread_create底層都是通過調(diào)用do_fork創(chuàng)建進程
long do_fork(unsigned long clone_flags,unsigned long stack_start, unsigned long stack_size,int __user *parent_tidptr,int __user *child_tidptr)
{
return _do_fork(clone_flags, stack_start, stack_size,
parent_tidptr, child_tidptr, 0);
}
clone_flags:用于指定創(chuàng)建新進程/線程的選項,包括是否共享地址空間、文件描述符等。
stack_start:新進程/線程的棧起始地址。
stack_size:新進程/線程的棧大小。
parent_tidptr:指向父進程/線程的線程ID的指針。
child_tidptr:指向子進程/線程的線程ID的指針。
子進程不會繼承的一些主要屬性和資源:
進程ID(PID):子進程會有自己獨立的進程ID,不會繼承父進程的PID。
父進程ID(PPID):子進程的父進程ID會被設(shè)置為創(chuàng)建它的父進程的PID,而不是繼承父進程的PPID。
信號處理器:子進程不會繼承父進程設(shè)置的信號處理器,它們會有各自獨立的信號處理器。
文件鎖:子進程不會繼承父進程設(shè)置的文件鎖。
定時器:子進程不會繼承父進程設(shè)置的定時器。
共享內(nèi)存和信號量:子進程不會繼承父進程的共享內(nèi)存和信號量。
資源限制:子進程不會繼承父進程設(shè)置的資源限制,如文件打開數(shù)限制等。
execve:
功能:execve系統(tǒng)調(diào)用用于加載并執(zhí)行一個新的程序,替換當(dāng)前進程的映像(代碼和數(shù)據(jù))為新程序的映像。
參數(shù):execve接受三個參數(shù),分別是要執(zhí)行的程序路徑、命令行參數(shù)數(shù)組和環(huán)境變量數(shù)組。
返回值:如果execve執(zhí)行成功,則不會返回,因為當(dāng)前進程的映像已被替換為新程序的映像;如果出現(xiàn)錯誤,則返回-1。
特點:execve會將當(dāng)前進程的映像替換為新程序的映像,新程序開始執(zhí)行時,會繼承當(dāng)前進程的PID等信息,但不會保留原有進程的任何狀態(tài)。
寫時復(fù)制技術(shù)在傳統(tǒng)的unix操作系統(tǒng)中,創(chuàng)建新進程時會復(fù)制父進程所擁有的資源,但是子進程不一定需要父進程的全部資源。
在現(xiàn)代的操作系統(tǒng)中采用了寫時復(fù)制copy on write,COW技術(shù),在創(chuàng)建子進程時只需要復(fù)制進程的地址空間頁表,
只讀共享進程地址空間,當(dāng)父子進程其中一方需要修改頁面數(shù)據(jù)時,觸發(fā)缺頁異常,此時才會從復(fù)制內(nèi)容。
終止進程1.程序主動主動調(diào)用exit退出
2.進程收到SIGKILL信號
kill -15 PID # 發(fā)送SIGTERM信號
kill -9 PID # 發(fā)送SIGKILL信號
3.觸發(fā)內(nèi)核異常
4.收到不能處理的信號
僵尸進程和孤兒進程僵尸進程:
定義:當(dāng)一個進程終止,但其父進程沒有及時處理該進程的終止?fàn)顟B(tài)信息(稱為SIGCHLD信號),導(dǎo)致該進程的進程描述符仍然存在,但進程已經(jīng)終止,此時該進程就成為僵尸進程。
特點:僵尸進程不占用系統(tǒng)資源,但會占用進程表中的一個條目。
解決方法:父進程應(yīng)該及時處理SIGCHLD信號,通過調(diào)用wait()或waitpid()等系統(tǒng)調(diào)用來回收子進程的資源,防止子進程變成僵尸進程。
孤兒進程:
定義:當(dāng)一個進程的父進程提前終止,而該進程本身還在運行,此時該進程成為孤兒進程。
特點:孤兒進程會被init進程(PID為1)接管,init進程會成為孤兒進程的新父進程。
影響:孤兒進程的父進程終止后,孤兒進程會繼續(xù)運行,直到自己終止或被init進程接管。
0號進程**0、1號進程代碼來源0.11版本內(nèi)核**
0號進程通常指的是內(nèi)核線程(kernel thread)或者是調(diào)度進程(scheduler process),其PID為0。這個進程在系統(tǒng)啟動時就已經(jīng)存在,并且在整個系統(tǒng)運行期間都存在。
0號進程通常被稱為內(nèi)核線程,因為它在內(nèi)核空間運行,不屬于用戶空間的任何進程。它在系統(tǒng)中扮演著重要的角色,負責(zé)系統(tǒng)的調(diào)度、內(nèi)存管理、I/O操作等核心功能。由于它是內(nèi)核的一部分,因此沒有對應(yīng)的用戶空間程序,也不會被用戶直接創(chuàng)建或終止。
在Linux系統(tǒng)中,0號進程通常是系統(tǒng)中所有進程的祖先,即所有進程的父進程。當(dāng)一個進程的父進程終止時,該進程會成為孤兒進程,并被0號進程(init進程,PID為1)接管。
sched_init(); // 初始化0進程
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); // 設(shè)置TSS
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); // 設(shè)置LDT
p = gdt+2+FIRST_TSS_ENTRY; // 獲取TSS
for(i=1;ia=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); // 清空NT
ltr(0); // 掛載TSS到TR寄存器
lldt(0); // 掛載LDTR寄存器
// 設(shè)置定時器模式、以及設(shè)置高低位組成一個周期
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
set_intr_gate(0x20,&timer_interrupt); // 開啟定時器中斷
outb(inb_p(0x21)&~0x01,0x21); // 允許時鐘中斷
set_system_gate(0x80,&system_call); // 設(shè)置系統(tǒng)調(diào)用的入口
}
1號進程在Linux系統(tǒng)中,1號進程通常指的是init進程,其PID為1。init進程是系統(tǒng)中所有進程的祖先進程,是系統(tǒng)啟動時由內(nèi)核創(chuàng)建的第一個用戶級進程。init進程負責(zé)系統(tǒng)的初始化、進程的管理和系統(tǒng)的關(guān)機等任務(wù)。
void main(void) /* This really IS void, no error here. */
{ /* The startup routine assumes (well, ...) this */
mem_init(main_memory_start,memory_end); // 初始化內(nèi)存映射
trap_init(); // 初始化中斷捕獲
blk_dev_init(); // 塊設(shè)備初始化
chr_dev_init(); // 字符設(shè)備初始化
tty_init(); // 終端初始化
time_init(); // 時間初始化
sched_init(); // 初始化0進程
buffer_init(buffer_memory_end); // 緩沖區(qū)初始化
hd_init(); // 初始化硬盤
floppy_init(); // 初始化軟盤
sti(); // 開啟全局中斷
move_to_user_mode(); // 將進程0特權(quán)調(diào)到3級
if (!fork()) { /* we count on this going ok */
init(); // 子進程進行初始化
}
void init(void)
{
int pid,i; // pid用于fork
setup((void *) &drive_info); // 配置系統(tǒng),包括磁盤、文件系統(tǒng)
(void) open("/dev/tty0",O_RDWR,0); // 打開tty文件,此時是標(biāo)準(zhǔn)輸入設(shè)備文件
(void) dup(0); // 從tty復(fù)制句柄,打開標(biāo)準(zhǔn)輸出設(shè)備文件
(void) dup(0); // 繼續(xù)復(fù)制句柄,打開標(biāo)準(zhǔn)錯誤輸出設(shè)備
printf("%d buffers = %d bytes buffer space
\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);
printf("Free mem: %d bytes
\r",memory_end-main_memory_start);
if (!(pid=fork())) { // 到這里就要啟動進程2了
// fs/open.c
close(0); // 進程2關(guān)閉輸入
if (open("/etc/rc",O_RDONLY,0)) // 使用/etc/rc替換輸入設(shè)備,加載一些開機需要執(zhí)行的東西
_exit(1); // 替換失敗就寄了
// do_execve(fs/exec.c)
execve("/bin/sh",argv_rc,envp_rc); // 執(zhí)行shell,參數(shù)分別是shell執(zhí)行參數(shù)(="NULL")其環(huán)境變量(="/")
// 由于輸入已經(jīng)改成了/etc/rc文件了,所以這里在運行/etc/rc的內(nèi)容
_exit(2);
}
if (pid>0) // 進程1暫停工作,等待子進程工作完成(子進程只有進程2)
while (pid != wait(&i)) // 暫不深入wait -> 補充: 進程2退出了,回到了這里,pid = 2
/* nothing */;
while (1) {
if ((pid=fork()) |
|