Process Creating/ Switching/ Destroying

The set of data that must be loaded into the registers before the process resume sits execution on the CPU is called the hardware context. The hardware context is a subset of the process execution context, which includes all information needed for the process execution. In Linux, part of the hardware context of a process is stored in the TSS segment, while the remaining part is saved in the Kernel Mode stack. The thread_struct structure describes the format of the Linux TSS.

 

We will assume the prev local variable refers to the process descriptor of the process being switched out and next refers to the one being switched in to replace it. We can thus define process switching as the activity consisting of saving the hardware context of prev and replacing it with the hardware context of next. Since process switches occur quite often, it is important to minimize the time spent in saving and loading hardware contexts.

 

Theswitch_to macro performs aprocess switch. It makes use of two parameters denoted as prev and next: the first is theprocess descriptor pointer of the process to be suspended, while the second isthe process descriptor pointer of the process to be executed on the CPU. The macrois invoked by the schedule( ) functionto schedule a new process on the CPU.

 

Unixoperating systems rely heavily on process creation to satisfy user requests. Asan

example,the shell process creates a new process that executes another copy of the shellwhenever the user enters a command.

TraditionalUnix systems treat all processes in the same way: resources owned by the parentprocess are duplicated, and a copy is granted to the child process. Thisapproach makes process creation very slow and inefficient, since it requirescopying the entire address space of the parent process. The child processrarely needs to read or modify all the resources already owned by the parent;in many cases, it issues an immediate execve( ) and wipes out the address space so carefully saved.

 

ModernUnix kernels solve this problem by introducing three different mechanisms:

• The Copy On Write technique allows both the parent andthe child to read the same

physical pages. Whenever eitherone tries to write on a physical page, the kernel

copies its contents into a newphysical page that is assigned to the writing process.

Lightweight processes allow both the parent and the child to share manyper-process

kernel data structures, likethe paging tables (and therefore the entire User Mode

address space) and the openfile tables.

• The vfork( ) system call creates a process that shares the memoryaddress space of its parent. To prevent the parent from overwriting data neededby the child, the

parent's execution is blockeduntil the child exits or executes a new program. We'll

learn more about the vfork( ) systemcall in the following section.

 

When either a clone( ), fork( ), or vfork( ) systemcall is issued, the kernel invokes the do_fork( ) function,which executes the following steps:

1. If the CLONE_PID flag has been specified, the do_fork() functionchecks whether the PID of the parent process is not null; if so, it returns anerror code. Only the swapper process is allowed to set CLONE_PID; this isrequired when initializing a

multiprocessor system.

2. The alloc_task_struct( ) function is invoked in order toget a new 8 KB union task_union memory area to store theprocess descriptor and the Kernel Mode stack ofthe new process.

3. The function follows the current pointer toobtain the parent process descriptor and copies it into the new processdescriptor in the memory area just allocated.

4. A few checks occur to make sure the user has theresources necessary to start a new

process. First, the function checks whether current->rlim[RLIMIT_NPROC].rlim_cur is smallerthan or equal to the current number ofprocesses owned by the user: if so, an errorcode is returned. The function gets thecurrent number of processesowned by the user from a per-user data structure named user_struct. Thisdata structure can be found through a pointer in the user field oftheprocess descriptor.

5. The find_empty_process( ) functionis invoked. If the owner of the parent process is not the superuser, thisfunction checks whether nr_tasks (the total number of processesin the system) is smaller than NR_TASKS-MIN_TASKS_LEFT_FOR_ROOT.  If

so, find_empty_process( ) invokes get_free_taskslot() to find afree entry in the task array. Otherwise, it returns an error.

6. The function writes the new process descriptor pointerinto the previously obtained

task entry and sets the tarray_ptr field ofthe process descriptor to the address of

that entry.

7. If the parent process makes use of some kernel modules,the function increments the corresponding reference counters. Each kernelmodule has its own reference counter, which indicates how many processes areusing it. A module cannot be removed unless its reference counter is null.

8. The function then updates some of the flags includedin the flags field thathave been copied from the parent process:

a. It clears the PF_SUPERPRIV flag,which indicates whether the process has used any of its superuser privileges.

b. It clears the PF_USEDFPU flag.

c. It clears the PF_PTRACED flagunless the CLONE_PTRACEparameterflag is set. When set, the CLONE_PTRACE flag means that the parentprocess is being traced with the ptrace( ) function, so the child shouldbe traced too.

d. It clears PF_TRACESYS flag unless, once again, the CLONE_PTRACE parameter flag is set.

e. It sets the PF_FORKNOEXEC flag, which indicates that thechild process has not yet issued an execve( ) system call.

f. It sets the PF_VFORK flag according to the value ofthe CLONE_VFORKflag. This specifies that the parent process must be woken upwhenever the process (the child) issues an execve( ) systemcall or terminates.

9. Now the function has taken almost everything that it canuse from the parent process; the rest of its activities focus on setting up newresources in the child and letting the kernel know that this new process hasbeen born. First, the function invokes the get_pid( ) functionto obtain a new PID, which will be assigned to the child process (unless the CLONE_PIDflag isset).

10. The function then updates all the process descriptorfields that cannot be inherited from the parent process, such as the fields that specifythe process parenthood relationships.

11. Unless specified differently by the flags parameter,it invokes copy_files(), copy_fs( ), copy_sighand( ), and copy_mm( ) to createnew data structures and copy into them the values of the corresponding parentprocess data structures.

12. It invokes copy_thread( ) to initialize the Kernel Modestack of the child process with the values contained in the CPU registers whenthe clone( ) call was issued (these values have been saved in the Kernel Mode stack of the parent, as described in. However, the function forces the value into the field corresponding to the eax register. The tss.esp field of the TSS of the child process is initialized with the base address of the Kernel Mode stack, and the address of an Assembly language function (ret_from_fork( )) is stored in the tss.eip field.

13. It uses the SET_LINKS macro to insert the new process descriptor in the process list.

14. It uses the hash_pid( ) function to insert the new process descriptor in the pidhash hash table.

15. It increments the values of nr_tasks and current->user->count.

16. It sets the state field of the child process descriptor to TASK_RUNNING and then invokes wake_up_process( ) to insert the child in therunqueue list.

17. If the CLONE_VFORK flag has been specified, thefunction suspends the parent process until the child releases its memoryaddress space (that is, until the child either terminates or executes a new program). In order to dothis, the process descriptor includes a kernel semaphore called vfork_sem.

18. It returns the PID of the child, which will beeventually be read by the parent process in User Mode.

Now we have a complete child process in the runnablestate. But it isn't actually running. It is up to the scheduler to decide when to give the CPU to this child. At some future process switch, the schedule will bestow this favor on the child process by loading a few CPU registers with the values of the tss field ofthe child's process descriptor. In particular,  esp will be loaded with tss.esp (that is, with the address of child's Kernel Mode stack), and eip will be loaded with the address of ret_from_fork(). This Assembly language function, in turn, invokes the ret_from_sys_call( ) function, which reloads all other registers with the values stored in the stack and forces the CPU back to User Mode. The new process will then start its execution right at the end of the fork( ), vfork( ), or clone( ) system call. The value returned by the system call is contained in eax: the value is for the child and equal to the PID for the child's parent.

 

CLONE_VM

The memory descriptor and all page tables.

CLONE_FS :

The table that identifies the root directory and thecurrent working directory.

CLONE_FILES :

The table that identifies the open files.

CLONE_SIGHAND :

The table that identifies the signal handlers.

CLONE_VFORK :

Used for the vfork( ) system call (see later in thissection).

 

The ancestor of all processes, called process0  or, for historical reasons, the swapper process, is a kernel thread created from scratch during the initialization phase of Linux by the start_kernel() function. This ancestor process makes use of the following data structures:

• A process descriptor and a Kernel Mode stack stored in the init_task_union variable. The init_task and init_stack macros yield the addresses of the process descriptor and the stack, respectively.

• The following tables, which the process descriptor points to:

o init_mm

o init_mmap

o init_fs

o init_files

o init_signals

The tables are initialized, respectively, by the following macros:

o INIT_MM

o INIT_MMAP

o INIT_FS

o INIT_FILES

o INIT_SIGNALS

• A TSS segment, initialized by the INIT_TSS macro.

• Two Segment Descriptors, namely a TSSD and an LDTD, which are stored in the GDT.

• A Page Global Directory stored in swapper_pg_dir, whichmay be considered as the kernel Page Global Directory since it is used by allkernel threads.

 

The start_kernel( ) function initializes all the data structures needed by the kernel, enables interrupts, and creates another kernel thread, named process 1, more commonly referred to as the init process :

kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);

 

The newly created kernel thread has PID 1 and shares with process all per-process kernel data structures. Moreover, when selected from the scheduler, the init  process starts executing the init( ) function.

 

After having created the init  process, process executes the cpu_idle( ) function, which essentially consists of repeatedly executing the hlt Assembly language instruction with the interrupts enabled. Process is selected by the scheduler only when there are no other processes in the TASK_RUNNING state.

 

TheUnix operating system allows a process to query the kernel to obtain the PID ofits parent process or the execution state for any of its children. A processmay, for instance, create a child process to perform a specific task and theninvoke a wait( )-like system call tocheck whether the child has terminated. If the child has terminated, its terminationcode will tell the parent process if the task has been carried out successfully.

 

Inorder to comply with these design choices, Unix kernels are not allowed todiscard data included in a process descriptor field right after the processterminates. They are allowed to do so only after the parent process has issueda wait( )-like system call thatrefers to the terminated process. This is why the TASK_ZOMBIE state has been introduced: although the processis technically dead, its descriptor must be saved until the parent process isnotified.

 

Whathappens if parent processes terminate before their children? In such a case,the system might be flooded with zombie processes that might end up using allthe available task entries. As mentionedearlier, this problem is solved by forcing all orphan processes to becomechildren of the init process. In this way, the init process will destroy the zombies whilechecking for the termination of one of its legitimate children through a wait( )-like system call.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值