In this chapter, we will discuss in detail about process management in Unix. When you execute a program on your Unix system, the system creates a special environment for that program. This environment contains everything needed for the system to run the program as if no other program were running on the system.
Whenever you issue a command in Unix, it creates, or starts, a new process. When you tried out the ls command to list the directory contents, you started a process. A process, in simple terms, is an instance of a running program.
The operating system tracks processes through a five-digit ID number known as the pid or the process ID. Each process in the system has a unique pid.
Pids eventually repeat because all the possible numbers are used up and the next pid rolls or starts over. At any point of time, no two processes with the same pid exist in the system because it is the pid that Unix uses to track each process.
Starting a Process
When you start a process (run a command), there are two ways you can run it −
Foreground Processes
Background Processes
Foreground Processes
By default, every process that you start runs in the foreground. It gets its input from the keyboard and sends its output to the screen.
You can see this happen with the ls command. If you wish to list all the files in your current directory, you can use the following command −
This would display all the files, the names of which start with ch and end with .doc −
The process runs in the foreground, the output is directed to my screen, and if the ls command wants any input (which it does not), it waits for it from the keyboard.
While a program is running in the foreground and is time-consuming, no other commands can be run (start any other processes) because the prompt would not be available until the program finishes processing and comes out.
Background Processes
A background process runs without being connected to your keyboard. If the background process requires any keyboard input, it waits.
The advantage of running a process in the background is that you can run other commands; you do not have to wait until it completes to start another!
The simplest way to start a background process is to add an ampersand (&) at the end of the command.
This displays all those files the names of which start with ch and end with .doc −
Here, if the ls command wants any input (which it does not), it goes into a stop state until we move it into the foreground and give it the data from the keyboard.
That first line contains information about the background process — the job number and the process ID. You need to know the job number to manipulate it between the background and the foreground.
Press the Enter key and you will see the following −
The first line tells you that the ls command background process finishes successfully. The second is a prompt for another command.
Listing Running Processes
It is easy to see your own processes by running the ps (process status) command as follows −
One of the most commonly used flags for ps is the -f ( f for full) option, which provides more information as shown in the following example −
Here is the description of all the fields displayed by ps -f command −
Sr.No.
Column & Description
1
User ID that this process belongs to (the person running it)
Parent process ID (the ID of the process that started it)
CPU utilization of process
Process start time
Terminal type associated with the process
CPU time taken by the process
The command that started this process
There are other options which can be used along with ps command −
Sr.No.
Option & Description
1
Shows information about all users
Shows information about processes without terminals
Shows additional information like -f option
Displays extended information
Stopping Processes
Ending a process can be done in several different ways. Often, from a console-based command, sending a CTRL + C keystroke (the default interrupt character) will exit the command. This works when the process is running in the foreground mode.
If a process is running in the background, you should get its Job ID using the ps command. After that, you can use the kill command to kill the process as follows −
Here, the kill command terminates the first_one process. If a process ignores a regular kill command, you can use kill -9 followed by the process ID as follows −
Parent and Child Processes
Each unix process has two ID numbers assigned to it: The Process ID (pid) and the Parent process ID (ppid). Each user process in the system has a parent process.
Most of the commands that you run have the shell as their parent. Check the ps -f example where this command listed both the process ID and the parent process ID.
Zombie and Orphan Processes
Normally, when a child process is killed, the parent process is updated via a SIGCHLD signal. Then the parent can do some other task or restart a new child as needed. However, sometimes the parent process is killed before its child is killed. In this case, the «parent of all processes,» the init process, becomes the new PPID (parent process ID). In some cases, these processes are called orphan processes.
When a process is killed, a ps listing may still show the process with a Z state. This is a zombie or defunct process. The process is dead and not being used. These processes are different from the orphan processes. They have completed execution but still find an entry in the process table.
Daemon Processes
Daemons are system-related background processes that often run with the permissions of root and services requests from other processes.
A daemon has no controlling terminal. It cannot open /dev/tty. If you do a «ps -ef» and look at the tty field, all daemons will have a ? for the tty.
To be precise, a daemon is a process that runs in the background, usually waiting for something to happen that it is capable of working with. For example, a printer daemon waiting for print commands.
If you have a program that calls for lengthy processing, then it’s worth to make it a daemon and run it in the background.
The top Command
The top command is a very useful tool for quickly showing processes sorted by various criteria.
It is an interactive diagnostic tool that updates frequently and shows information about physical and virtual memory, CPU usage, load averages, and your busy processes.
Here is the simple syntax to run top command and to see the statistics of CPU utilization by different processes −
Job ID Versus Process ID
Background and suspended processes are usually manipulated via job number (job ID). This number is different from the process ID and is used because it is shorter.
In addition, a job can consist of multiple processes running in a series or at the same time, in parallel. Using the job ID is easier than tracking individual processes.
Источник
Process Management in Linux
A process means program in execution. It generally takes an input, processes it and gives us the appropriate output. Check Introduction to Process Management for more details about a process. There are basically 2 types of processes.
Foreground processes: Such kind of processes are also known as interactive processes. These are the processes which are to be executed or initiated by the user or the programmer, they can not be initialized by system services. Such processes take input from the user and return the output. While these processes are running we can not directly initiate a new process from the same terminal.
Background processes: Such kind of processes are also known as non interactive processes. These are the processes that are to be executed or initiated by the system itself or by users, though they can even be managed by users. These processes have a unique PID or process if assigned to them and we can initiate other processes within the same terminal from which they are initiated.
Practically Managing the Processes
1. Example of foreground process.
This command will be executed in the terminal and we would be able to execute another command after the execution of the above command.
Note: In this case, the name of the process is sleep 5 but you may change the same as per your need.
2. Stopping a process in between of its execution. To stop a foreground process in between of its execution we may press CTRL+Z to force stop it.
Pressing CTRL+Z in between the execution of the command will stop it.
Note: In this case the name of the process is sleep 100 but you may change the same as per your need.
3. To get the list of jobs that are either running or stopped.
It will display the stopped processes in this terminal and even the pending ones.
4. To run all the pending and force stopped jobs in the background.
This will start the stopped and pending processes in the background.
5. To get details of a process running in background.
Note: In this case the name of the process is sleep 100 but you may change the same as per your need.
6. To run all the pending and force stopped jobs in the foreground.
This will start the stopped and pending processes in the foreground.
7. To run a process in the background without getting impacted by the closing of the terminal.
While executing, it will even store all the output after execution in nohup.out file.
Note: In this case, the process is sleep 100, you may modify it as per your need.
8. To run some processes in the background directly.
This will run the process in the background and will display the process id of the process. Note:- In this case, the process is sleep 100, you may modify it as per your need.
9. To run processes with priority.
The top priority is -20 but as it may affect the system processes so we have used the priority 5.
Note: In this case, the process is sleep 100, you may modify it as per your need.
10. To get the list of all the running processes on your Linux machine.
This will display all the processes that are currently running in your system.
Источник
Изучаем процессы в Linux
В этой статье я хотел бы рассказать о том, какой жизненный путь проходят процессы в семействе ОС Linux. В теории и на примерах я рассмотрю как процессы рождаются и умирают, немного расскажу о механике системных вызовов и сигналов.
Данная статья в большей мере рассчитана на новичков в системном программировании и тех, кто просто хочет узнать немного больше о том, как работают процессы в Linux.
Всё написанное ниже справедливо к Debian Linux с ядром 4.15.0.
Содержание
Введение
Системное программное обеспечение взаимодействует с ядром системы посредством специальных функций — системных вызовов. В редких случаях существует альтернативный API, например, procfs или sysfs, выполненные в виде виртуальных файловых систем.
Атрибуты процесса
Процесс в ядре представляется просто как структура с множеством полей (определение структуры можно прочитать здесь). Но так как статья посвящена системному программированию, а не разработке ядра, то несколько абстрагируемся и просто акцентируем внимание на важных для нас полях процесса:
Идентификатор процесса (pid)
Открытые файловые дескрипторы (fd)
Обработчики сигналов (signal handler)
Текущий рабочий каталог (cwd)
Переменные окружения (environ)
Код возврата
Жизненный цикл процесса
Рождение процесса
Только один процесс в системе рождается особенным способом — init — он порождается непосредственно ядром. Все остальные процессы появляются путём дублирования текущего процесса с помощью системного вызова fork(2) . После выполнения fork(2) получаем два практически идентичных процесса за исключением следующих пунктов:
fork(2) возвращает родителю PID ребёнка, ребёнку возвращается 0;
У ребёнка меняется PPID (Parent Process Id) на PID родителя.
После выполнения fork(2) все ресурсы дочернего процесса — это копия ресурсов родителя. Копировать процесс со всеми выделенными страницами памяти — дело дорогое, поэтому в ядре Linux используется технология Copy-On-Write. Все страницы памяти родителя помечаются как read-only и становятся доступны и родителю, и ребёнку. Как только один из процессов изменяет данные на определённой странице, эта страница не изменяется, а копируется и изменяется уже копия. Оригинал при этом «отвязывается» от данного процесса. Как только read-only оригинал остаётся «привязанным» к одному процессу, странице вновь назначается статус read-write.
Состояние «готов»
Сразу после выполнения fork(2) переходит в состояние «готов». Фактически, процесс стоит в очереди и ждёт, когда планировщик (scheduler) в ядре даст процессу выполняться на процессоре.
Состояние «выполняется»
Как только планировщик поставил процесс на выполнение, началось состояние «выполняется». Процесс может выполняться весь предложенный промежуток (квант) времени, а может уступить место другим процессам, воспользовавшись системным вывозом sched_yield .
Перерождение в другую программу
В некоторых программах реализована логика, в которой родительский процесс создает дочерний для решения какой-либо задачи. Ребёнок в данном случае решает какую-то конкретную проблему, а родитель лишь делегирует своим детям задачи. Например, веб-сервер при входящем подключении создаёт ребёнка и передаёт обработку подключения ему. Однако, если нужно запустить другую программу, то необходимо прибегнуть к системному вызову execve(2) :
или библиотечным вызовам execl(3), execlp(3), execle(3), execv(3), execvp(3), execvpe(3) :
Все из перечисленных вызовов выполняют программу, путь до которой указан в первом аргументе. В случае успеха управление передаётся загруженной программе и в исходную уже не возвращается. При этом у загруженной программы остаются все поля структуры процесса, кроме файловых дескрипторов, помеченных как O_CLOEXEC , они закроются.
Как не путаться во всех этих вызовах и выбирать нужный? Достаточно постичь логику именования:
Все вызовы начинаются с exec
Пятая буква определяет вид передачи аргументов:
l обозначает list, все параметры передаются как arg1, arg2, . NULL
v обозначает vector, все параметры передаются в нуль-терминированном массиве;
Далее может следовать буква p, которая обозначает path. Если аргумент file начинается с символа, отличного от «/», то указанный file ищется в каталогах, перечисленных в переменной окружения PATH
Последней может быть буква e, обозначающая environ. В таких вызовах последним аргументом идёт нуль-терминированный массив нуль-терминированных строк вида key=value — переменные окружения, которые будут переданы новой программе.
Семейство вызовов exec* позволяет запускать скрипты с правами на исполнение и начинающиеся с последовательности шебанг (#!).
Есть соглашение, которое подразумевает, что argv[0] совпадает с нулевым аргументов для функций семейства exec*. Однако, это можно нарушить.
Любопытный читатель может заметить, что в сигнатуре функции int main(int argc, char* argv[]) есть число — количество аргументов, но в семействе функций exec* ничего такого не передаётся. Почему? Потому что при запуске программы управление передаётся не сразу в main. Перед этим выполняются некоторые действия, определённые glibc, в том числе подсчёт argc.
Состояние «ожидает»
Некоторые системные вызовы могут выполняться долго, например, ввод-вывод. В таких случаях процесс переходит в состояние «ожидает». Как только системный вызов будет выполнен, ядро переведёт процесс в состояние «готов». В Linux так же существует состояние «ожидает», в котором процесс не реагирует на сигналы прерывания. В этом состоянии процесс становится «неубиваемым», а все пришедшие сигналы встают в очередь до тех пор, пока процесс не выйдет из этого состояния. Ядро само выбирает, в какое из состояний перевести процесс. Чаще всего в состояние «ожидает (без прерываний)» попадают процессы, которые запрашивают ввод-вывод. Особенно заметно это при использовании удалённого диска (NFS) с не очень быстрым интернетом.
Состояние «остановлен»
В любой момент можно приостановить выполнение процесса, отправив ему сигнал SIGSTOP. Процесс перейдёт в состояние «остановлен» и будет находиться там до тех пор, пока ему не придёт сигнал продолжать работу (SIGCONT) или умереть (SIGKILL). Остальные сигналы будут поставлены в очередь.
Завершение процесса
Ни одна программа не умеет завершаться сама. Они могут лишь попросить систему об этом с помощью системного вызова _exit или быть завершенными системой из-за ошибки. Даже когда возвращаешь число из main() , всё равно неявно вызывается _exit . Хотя аргумент системного вызова принимает значение типа int, в качестве кода возврата берется лишь младший байт числа.
Состояние «зомби»
Сразу после того, как процесс завершился (неважно, корректно или нет), ядро записывает информацию о том, как завершился процесс и переводит его в состояние «зомби». Иными словами, зомби — это завершившийся процесс, но память о нём всё ещё хранится в ядре. Более того, это второе состояние, в котором процесс может смело игнорировать сигнал SIGKILL, ведь что мертво не может умереть ещё раз.
Забытье
Код возврата и причина завершения процесса всё ещё хранится в ядре и её нужно оттуда забрать. Для этого можно воспользоваться соответствующими системными вызовами:
Вся информация о завершении процесса влезает в тип данных int. Для получения кода возврата и причины завершения программы используются макросы, описанные в man-странице waitpid(2) .
Передача argv[0] как NULL приводит к падению.
Бывают случаи, при которых родитель завершается раньше, чем ребёнок. В таких случаях родителем ребёнка станет init и он применит вызов wait(2) , когда придёт время.
После того, как родитель забрал информацию о смерти ребёнка, ядро стирает всю информацию о ребёнке, чтобы на его место вскоре пришёл другой процесс.
Благодарности
Спасибо Саше «Al» за редактуру и помощь в оформлении;
Спасибо Саше «Reisse» за понятные ответы на сложные вопросы.
Они стойко перенесли напавшее на меня вдохновение и напавший на них шквал моих вопросов.