電子產業(yè)一站式賦能平臺

PCB聯(lián)盟網

搜索
查看: 93|回復: 0
收起左側

一文讀懂什么是進程

[復制鏈接]

317

主題

317

帖子

3149

積分

四級會員

Rank: 4

積分
3149
跳轉到指定樓層
樓主
發(fā)表于 2024-11-12 11:52:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
什么是程序?程序:可執(zhí)行文件或者包含一堆可運行CPU指令的和數據

什么是進程?進程 = 程序 + 執(zhí)行
進程是執(zhí)行中的程序,除了可執(zhí)行代碼外還包含進程的活動信息和數據,比如用來存放函數變量、局部變量、返回值的用戶棧,存放進程相關數據的數據段,內核中進程間切換的內核棧,動態(tài)分配的堆。
進程是系統(tǒng)分配資源的基本單位(內存、CPU時間片)
進程是用來實現多進程并發(fā)執(zhí)行的一個實體,實現對CPU的虛擬化,讓每個進程感覺都擁有一個CPU,核心技術就是上下文切換和進程調度。
早期操作系統(tǒng)程序都是單個運行的,CPU利用率低下,為了提高CPU的利用率,加載多個程序到內存并發(fā)運行,在單核CPU中這種屬于偽并發(fā)。
其實在同一時間只運行一個程序

進程控制塊描述符進程控制塊描述符task_struct主要包含:
進程狀態(tài)(state):表示進程當前的狀態(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)

進程調度信息(sched_info):包括進程的調度策略、優(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:表示進程是否在就緒隊列中,即是否正在等待被調度執(zhí)行。
prio:表示進程的動態(tài)優(yōu)先級。
static_prio:表示進程的靜態(tài)優(yōu)先級。
normal_prio:表示進程的普通優(yōu)先級。
rt_priority:表示實時進程的優(yōu)先級。
se:sched_entity 結構體,用于普通進程的調度實體。
rt:sched_rt_entity 結構體,用于實時進程的調度實體。
dl:sched_dl_entity 結構體,用于周期性實時進程的調度實體。
dl_server:指向調度該進程的周期性實時進程的指針。
sched_class:指向調度類(sched_class)的指針,用于確定進程的調度策略和行為。

進程標識符(pid):唯一標識一個進程的數字標識符,內核使用bitmap保證進程分配唯一的pid。
一個線程組中所有的線程使用和線程組組長相同的pid,它會被存進tgid中
使用getpid()系統(tǒng)調用獲取pid
  pid_t   pid;
  pid_t   tgid;進程堆棧(stack):用于保存進程的函數調用棧信息。
void  *stack;引用計數(usage):用于跟蹤進程的引用計數,確保在不再需要時能夠正確釋放資源。
refcount_t  usage;進程標志位(flags):存儲進程的各種標志位信息,比如是否在運行、是否被掛起等。
unsigned int  flags;多處理器支持字段(on_cpu、wake_entry):用于處理多處理器環(huán)境下的調度和喚醒操作。
#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進程間家庭關系
    /*
     * 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):當進程剛被創(chuàng)建時,處于創(chuàng)建態(tài)。在這個階段,操作系統(tǒng)正在為進程分配資源和初始化進程控制塊等信息。
就緒態(tài)(Ready):進程已經準備好運行,但由于還未獲得處理器資源,暫時無法執(zhí)行。進程在就緒隊列中等待被調度執(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)回收。



查看進程狀態(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 結構體的各個成員,包括線程信息、棧信息、調度優(yōu)先級、CPU 信息、內存管理、信號處理、文件系統(tǒng)等等。
這些成員變量記錄了進程的狀態(tà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)建函數

fork
fork函數用于創(chuàng)建一個新的進程,新進程是調用進程(父進程)的副本。
函數名稱:fork
頭文件:#include
返回類型:pid_t(進程ID類型)
參數:無參數
返回值:在父進程中,fork返回新創(chuàng)建子進程的進程ID(PID),在子進程中,fork返回0。如果出現錯誤,fork返回-1。
通過調用fork函數,父進程會創(chuàng)建一個子進程,子進程會繼承父進程的數據、堆棧、文件描述符等信息,但是它們各自有獨立的內存空間。

vfork
vfork函數用于創(chuàng)建一個新的進程,但與fork不同的是,vfork保證子進程先運行,調用do_fork時比fork多了兩個標志位,
CLONE_VFORK和CLONE_VM,分別代表父進程被掛起,直到子進程釋放資源和父子進程運行在相同的內存空間。
函數名稱:vfork
頭文件:#include
返回類型:pid_t(進程ID類型)
參數:無參數
返回值:在父進程中,vfork返回新創(chuàng)建子進程的進程ID(PID),在子進程中,vfork返回0。如果出現錯誤,vfork返回-1。

clone
clone函數用于創(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 */ );
函數名稱:clone
頭文件:#include
返回類型:int
參數:
fn:指向新線程/進程入口點函數的指針
child_stack:子進程/線程的棧指針
flags:用于指定創(chuàng)建新線程/進程的選項
arg:傳遞給新線程/進程入口點函數的參數
...:可選參數,包括ptid、newtls和ctid
返回值:成功時返回新線程/進程的PID(對于線程,返回0表示成功),失敗時返回-1。

kthread_create
kthread_create函數用于創(chuàng)建一個新的內核線程,該線程在內核空間中運行,可以執(zhí)行內核級別的任務。
通過調用kthread_create函數,可以在Linux內核中創(chuàng)建一個新的內核線程,用于執(zhí)行后臺任務、定時任務等內核級別的工作。內核線程與用戶空間的線程有所不同,它們在內核空間中運行,可以訪問內核數據結構和執(zhí)行特權操作。
struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...);
函數名稱:kthread_create
返回類型:struct task_struct *(指向內核線程結構體的指針)
參數:threadfn:指向內核線程函數的指針,即內核線程的入口點函數
data:傳遞給內核線程函數的參數
namefmt:內核線程的名稱格式字符串
...:可變參數,用于指定內核線程的調度優(yōu)先級等其他選項
功能:
返回值:成功時返回指向新創(chuàng)建內核線程的task_struct結構體指針,失敗時返回NULL。

do_fork
fork/vfork/clone/kthread_create底層都是通過調用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會被設置為創(chuàng)建它的父進程的PID,而不是繼承父進程的PPID。
信號處理器:子進程不會繼承父進程設置的信號處理器,它們會有各自獨立的信號處理器。
文件鎖:子進程不會繼承父進程設置的文件鎖。
定時器:子進程不會繼承父進程設置的定時器。
共享內存和信號量:子進程不會繼承父進程的共享內存和信號量。
資源限制:子進程不會繼承父進程設置的資源限制,如文件打開數限制等。
execve:
功能:execve系統(tǒng)調用用于加載并執(zhí)行一個新的程序,替換當前進程的映像(代碼和數據)為新程序的映像。
參數:execve接受三個參數,分別是要執(zhí)行的程序路徑、命令行參數數組和環(huán)境變量數組。
返回值:如果execve執(zhí)行成功,則不會返回,因為當前進程的映像已被替換為新程序的映像;如果出現錯誤,則返回-1。
特點:execve會將當前進程的映像替換為新程序的映像,新程序開始執(zhí)行時,會繼承當前進程的PID等信息,但不會保留原有進程的任何狀態(tài)。
寫時復制技術在傳統(tǒng)的unix操作系統(tǒng)中,創(chuàng)建新進程時會復制父進程所擁有的資源,但是子進程不一定需要父進程的全部資源。
在現代的操作系統(tǒng)中采用了寫時復制copy on write,COW技術,在創(chuàng)建子進程時只需要復制進程的地址空間頁表,
只讀共享進程地址空間,當父子進程其中一方需要修改頁面數據時,觸發(fā)缺頁異常,此時才會從復制內容。

終止進程1.程序主動主動調用exit退出
2.進程收到SIGKILL信號
kill -15 PID # 發(fā)送SIGTERM信號
kill -9 PID # 發(fā)送SIGKILL信號
3.觸發(fā)內核異常
4.收到不能處理的信號

僵尸進程和孤兒進程僵尸進程:
定義:當一個進程終止,但其父進程沒有及時處理該進程的終止狀態(tài)信息(稱為SIGCHLD信號),導致該進程的進程描述符仍然存在,但進程已經終止,此時該進程就成為僵尸進程。
特點:僵尸進程不占用系統(tǒng)資源,但會占用進程表中的一個條目。
解決方法:父進程應該及時處理SIGCHLD信號,通過調用wait()或waitpid()等系統(tǒng)調用來回收子進程的資源,防止子進程變成僵尸進程。
孤兒進程:
定義:當一個進程的父進程提前終止,而該進程本身還在運行,此時該進程成為孤兒進程。
特點:孤兒進程會被init進程(PID為1)接管,init進程會成為孤兒進程的新父進程。
影響:孤兒進程的父進程終止后,孤兒進程會繼續(xù)運行,直到自己終止或被init進程接管。


0號進程**0、1號進程代碼來源0.11版本內核**
0號進程通常指的是內核線程(kernel thread)或者是調度進程(scheduler process),其PID為0。這個進程在系統(tǒng)啟動時就已經存在,并且在整個系統(tǒng)運行期間都存在。
0號進程通常被稱為內核線程,因為它在內核空間運行,不屬于用戶空間的任何進程。它在系統(tǒng)中扮演著重要的角色,負責系統(tǒng)的調度、內存管理、I/O操作等核心功能。由于它是內核的一部分,因此沒有對應的用戶空間程序,也不會被用戶直接創(chuàng)建或終止。
在Linux系統(tǒng)中,0號進程通常是系統(tǒ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)); // 設置TSS
  set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); // 設置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寄存器
  // 設置定時器模式、以及設置高低位組成一個周期
  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);     // 設置系統(tǒng)調用的入口
}

1號進程在Linux系統(tǒng)中,1號進程通常指的是init進程,其PID為1。init進程是系統(tǒng)中所有進程的祖先進程,是系統(tǒng)啟動時由內核創(chuàng)建的第一個用戶級進程。init進程負責系統(tǒng)的初始化、進程的管理和系統(tǒng)的關機等任務。
void main(void)     /* This really IS void, no error here. */
{           /* The startup routine assumes (well, ...) this */
  mem_init(main_memory_start,memory_end); // 初始化內存映射
  trap_init();                            // 初始化中斷捕獲
  blk_dev_init();                         // 塊設備初始化
  chr_dev_init();                         // 字符設備初始化
  tty_init();                             // 終端初始化
  time_init();                            // 時間初始化
  sched_init();                           // 初始化0進程
  buffer_init(buffer_memory_end);         // 緩沖區(qū)初始化
  hd_init();                              // 初始化硬盤
  floppy_init();                          // 初始化軟盤
  sti();                                  // 開啟全局中斷
  move_to_user_mode();                    // 將進程0特權調到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文件,此時是標準輸入設備文件
    (void) dup(0);                          // 從tty復制句柄,打開標準輸出設備文件
    (void) dup(0);                          // 繼續(xù)復制句柄,打開標準錯誤輸出設備
  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關閉輸入
  if (open("/etc/rc",O_RDONLY,0))     // 使用/etc/rc替換輸入設備,加載一些開機需要執(zhí)行的東西
  _exit(1);                       // 替換失敗就寄了
  // do_execve(fs/exec.c)
  execve("/bin/sh",argv_rc,envp_rc);  // 執(zhí)行shell,參數分別是shell執(zhí)行參數(="NULL")其環(huán)境變量(="/")
  // 由于輸入已經改成了/etc/rc文件了,所以這里在運行/etc/rc的內容
  _exit(2);
    }
  if (pid>0)                              // 進程1暫停工作,等待子進程工作完成(子進程只有進程2)
  while (pid != wait(&i))             // 暫不深入wait -> 補充: 進程2退出了,回到了這里,pid = 2
            /* nothing */;
  while (1) {
  if ((pid=fork())
回復

使用道具 舉報

發(fā)表回復

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則


聯(lián)系客服 關注微信 下載APP 返回頂部 返回列表