Linux set task state

Изучение ядра Linux 007-управление процессами (3)

В дескрипторе процесса состояние типа long описывает текущее состояние процесса.

Как показано ниже: Состояние процесса можно разделить на три категории:> 0 для прекращения работы, = 0 для запуска, -1 для не запуска.

5656674-ab407ca364b572fa.png

Фактически, процесс в системе может находиться в одном из следующих пяти состояний:

  • TASK_RUNNING (выполняется) — процесс выполняется, он выполняется или ожидает выполнения в очереди выполнения.
  • TASK_INTERRUPTIBLE (прерываемый) — процесс заблокирован в спящем состоянии и ожидает выполнения определенных условий (таких как операции ввода-вывода). Но когда условие выполнено, ядро ​​установит состояние процесса на выполнение.
  • TASK_UNINTERRUPTIBLE (непрерывный) — процесс в этом состоянии не будет пробужден, даже если он получит сигнал, и будет таким же, как состояние прерывания.
  • __TASK_TRACED-Процессы, отслеживаемые другими процессами, такими как ptrace.
  • __TASK_STOPPED (остановка) — процесс останавливается. Процесс переходит в это состояние при получении сигналов SIGSTOP, SIGSTP, SIGTTIN, SIGTTOU и т. Д., И любой сигнал, полученный во время отладки, также перейдет в это состояние.

Соотношение преобразования каждого состояния выглядит следующим образом:

5656674-eb1896ec26322c96.png

Мы можем найти это определение в коде ядра:linux2.6.34/include/linux/sched.h#L183

/* * Task state bitmask. NOTE! These bits are also * encoded in fs/proc/array.c: get_task_state(). * * We have two separate sets of flags: task->state * is about runnability, while task->exit_state are * about the task exiting. Confusing, but this way * modifying one set can't modify the other one by * mistake. */ #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_ZOMBIE 16 #define EXIT_DEAD 32 /* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_STATE_MAX 512 #define TASK_STATE_TO_CHAR_STR "RSDTtZXxKW 

Установить текущий статус процесса

Процесс в ядре должен отрегулировать состояние определенного процесса. Но обычно это происходит не путем непосредственного изменения значения состояния для завершения изменения состояния, а с помощью одного из следующих четырех макросов:

  • set_task_state
  • __set_task_state
  • set_current_state
  • __set_current_state
#define __set_task_state(tsk, state_value) \ do < (tsk)->state = (state_value); > while (0) #define set_task_state(tsk, state_value) \ set_mb((tsk)->state, (state_value)) /* * set_current_state() includes a barrier so that the write of current->state * is correctly serialised wrt the caller's subsequent test of whether to * actually sleep: * * set_current_state(TASK_UNINTERRUPTIBLE); * if (do_i_need_to_sleep()) * schedule(); * * If the caller does not need such serialisation then use __set_current_state() */ #define __set_current_state(state_value) \ do < current->state = (state_value); > while (0) #define set_current_state(state_value) \ set_mb(current->state, (state_value)) 

Из приведенного выше кода мы ясно видим, что set_current_state (state_value) будет set_task_state (current, state_value).

Посредством комментариев вы также можете понять разницу между добавлением __. Set__current_state () и барьером, чтобы вызывающий мог позже проверить, действительно ли значение состояния записано во время сна, тогда как __set__current () просто и напрямую Изменить значение состояния.

Примечание: current — это макрос, представляющий текущий процесс. Определение этого макроса связано с архитектурой. Для архитектуры x86 оно определяется вlinux2.6.34/arch/x86/include/asm/current.h#L17, Поскольку содержимое файла current.h относительно невелико, я разместил его здесь полностью.

#ifndef _ASM_X86_CURRENT_H #define _ASM_X86_CURRENT_H #include #include #ifndef __ASSEMBLY__ struct task_struct; DECLARE_PER_CPU(struct task_struct *, current_task); static __always_inline struct task_struct *get_current(void) < return percpu_read_stable(current_task); >#define current get_current() #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_CURRENT_H */ 

Контекст процесса

Исполняемый программный код является важной частью процесса, и эти коды обычно загружаются из исполняемого файла в адресное пространство процесса для выполнения. Общая программа выполняется в пространстве пользователя, но когда она запрашивает системный вызов или вызывает исключение, она попадает в пространство ядра. В это время говорят, что ядро ​​выполняется от имени процесса и находится в контексте процесса. Текущий макрос в этом контексте эффективен. Если в этом промежутке не требуется выполнение процесса с более высоким приоритетом, а планировщик делает соответствующие корректировки, программа возобновляет выполнение в пользовательском пространстве при выходе из ядра.

Читайте также:  Перехват bluetooth kali linux

Источник

LINUX KERNEL INTERNALS

printf(«fork program is starting\n»);
pid = fork();
switch(pid)
case -1:
perror(«fork failed»);
exit(1);
case 0:
message = «This is the child»;
n = 5;
break;
default:
message = «This is the parent»;
n = 3;
break;
>

The parent gets over before the child so we can see the shell prompt appears mixed with the output of child process.

Now, lets try to finish the child process first.

int main()
pid_t pid;
char *message;
int n;

printf(«fork program starting\n»);
pid = fork();
switch(pid)
case -1:
perror(«fork failed»);
exit(1);
case 0:
message = «This is the child»;
n = 3;
break;
default:
message = «This is the parent»;
n = 5;
break;
>

int main()
pid_t pid;
char *message;
int n;
int exit_code;

printf(«fork program starting\n»);
pid = fork();
switch(pid)
case -1:
exit(1);
case 0:
message = «This is the child»;
n = 5;
exit_code = 37;
break;
default:
message = «This is the parent»;
n = 3;
exit_code = 0;
break;
>

/* This section of the program waits for the child process to finish. */

if(pid) int stat_val;
pid_t child_pid;

printf(«Child has finished: PID = %d\n», child_pid);
if(WIFEXITED(stat_val))
printf(«Child exited with code %d\n», WEXITSTATUS(stat_val));
else
printf(«Child terminated abnormally\n»);
>
exit (exit_code);
>

fork() vs vfork() vs exec() vs system() vs clone()

Let us first see the standard definition of these system calls.

Fork : The fork call is used to duplicate the current process, the new process identical in almost every way except that it has its own PID. The return value of the function fork distinguishes the two processes, zero is returned in the child and PID of child in parent process.

Exec : The exec call is a way to basically replace the entire current process with a new program. It loads the program into the current process space and runs it from the entry point. As a new process is not created, the process identifier (PID) does not change, but the machine code , data , heap , and stack of the process are replaced by those of the new program. exec() replaces the current process with a the executable pointed by the function. Control never returns to the original program unless there is an exec() error. exec system call can be executed as execl, execlp, execle, execv, execvp, execvpe

Читайте также:  Linux console in background

Vfork: The basic difference between vfork and fork is that when a new process is created with vfork(), the parent process is temporarily suspended, and the child process might borrow the parent’s address space. This strange state of affairs continues until the child process either exits, or calls execve(), at which point the parent process continues.

This means that the child process of a vfork() must be careful to avoid unexpectedly modifying variables of the parent process. In particular, the child process must not return from the function containing the vfork() call, and it must not call exit() (if it needs to exit, it should use _exit(); actually, this is also true for the child of a normal fork()).

The intent of vfork was to eliminate the overhead of copying the whole process image if you only want to do an exec* in the child. Because exec* replaces the whole image of the child process, there is no point in copying the image of the parent.

if ((pid = vfork()) == 0) <
execl(. NULL); /* after a successful execl the parent should be resumed */
_exit(127); /* terminate the child in case execl fails */
>

For other kinds of uses, vfork is dangerous and unpredictable.

With most current kernels, however, including Linux, the primary benefit of vfork has disappeared because of the way fork is implemented. Rather than copying the whole image when fork is executed, copy-on-write techniques are used.

Clone : Clone, as fork, creates a new process. Unlike fork, these calls allow the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers.

When the child process is created with clone, it executes the function application fn(arg). (This differs from fork, where execution continues in the child from the point of the original fork call.) The fn argument is a pointer to a function that is called by the child process at the beginning of its execution. The arg argument is passed to the fn function.

When the fn(arg) function application returns, the child process terminates. The integer returned by fn is the exit code for the child process. The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal.

Читайте также:  Изменить цвет консоли линукс

System : The system () library function uses fork(2) to create a child process that executes the shell command specified in command using execl(3) as follows: execl(«/bin/sh», «sh», «-c», command, (char *) 0); system () returns after the command has been completed.During execution of the command , SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored, in the process that calls system () (these signals will be handled according to their defaults inside the child process that executes command ). If command is NULL, then system () returns a status indicating whether a shell is available on the system

  • In situations where performance is critical and/or memory limited, vfork + exec* can therefore be a good alternative to fork + exec* . The problem is that it is less safe and the man page says vfork is likely to become deprecated in the future.
  • Because memory page tables are not duplicated, vfork is much faster than fork and vfork ‘s execution time is not affected by the amount of memory the parent process uses
  • system() will invoke your systems default command shell, which will execute the command string passed as an argument, that itself may or may not create further processes, that would depend on the command and the system. Either way, at least a command shell process will be created.
  • With system() you can invoke any command, whereas with exec(), you can only invoke an executable file. Shell scripts and batch files must be executed by the command shell.

Process Descriptor and the Task Structure

  • The kernel stores the list of processes in a circular doubly linked list called the task list .
  • Process descriptor is nothing but each element of this task list of the type struct task_struct, which is defined in . The process descriptor contains all the information about a specific process.
  • Some texts on operating system design call this list the task array . Because the Linux implementation is a linked list and not a static array, it is called the task list .
  • The task_struct is a relatively large data structure, at around 1.7 kilobytes on a 32-bit machine.
  • This size, however, is quite small considering that the structure contains all the information that the kernel has and needs about a process.
  • The process descriptor contains the data that describes the executing program open files, the process’s address space, pending signals, the process’s state, and much more.
  • This linked list is stored in kernel space.
  • There is one more structure, thread_info which holds more architecture-specific data than the task_struct..

Источник

Оцените статью
Adblock
detector