- 进程
- 常用系统调用介绍
getpid()
系统调用getppid()
系统调用fork()
系统调用
- 查看进程:
- 方法一:
ps
指令 - 方法二:
/proc
目录
- 方法一:
- 进程的状态
- 运行状态(R状态)
- 浅度睡眠状态(S状态)
- 暂停状态(T状态)
- 深度睡眠状态(D状态)
- 僵尸状态(Z状态)
- 特殊进程
- 僵尸进程
- 孤儿进程
- 常用系统调用介绍
进程
进程就是在运行的程序,程序是指放在硬盘上的文件。程序首先要加载到内存,CPU才能对它调度执行。
对进程的描述:
使用一个 task_struct
的结构体描述进程,其中包括了进程的主要信息,如进程号,状态等。如此一来,对进程进行操作,就等价于对 task_struct
结构体进行操作。
常用系统调用介绍
getpid()
系统调用
作用:获取当前进程的进程号
示例:
1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 5 int main(void) {6 pid_t p = getpid();7 8 while (1) {9 printf("This is a process, pid = %d\n", p);10 sleep(1); 11 }12 13 return 0;14 }
输出:
[atri@hcss-ecs-d3d5 process]$ ./a.out
This is a process, pid = 20247
This is a process, pid = 20247
This is a process, pid = 20247
getppid()
系统调用
作用: 获取当前进程的父进程的进程号.
示例:
1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 5 int main(void) {6 pid_t p = getpid();7 pid_t pp = getppid();8 while (1) {9 printf("This is a process, pid = %d\n", p);10 printf("This is a process, ppid = %d\n", pp);11 sleep(1);12 }13 14 return 0;15 }
输出:
[atri@hcss-ecs-d3d5 process]$ gcc test_getppid.c
[atri@hcss-ecs-d3d5 process]$ ./a.out
This is a process, pid = 20637
This is a process, ppid = 18027
fork()
系统调用
作用: 创建子进程.
特性: 调用一次, 返回两次. 在子进程中返回0, 在父进程中返回其 pid
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(void)
{printf("main() progress\n");printf("A. pid of main() is %d\n", getpid());printf("B. parent pid of main() is %d\n", getppid());pid_t son = fork();if (son == 0){printf("This is the sub progress of main()\n");printf("1. The value of getpid() is %d\n", getpid());printf("2. The value of getppid() is %d\n", getppid());}else if (son > 0){printf("This is the value that return in main progress.\n");printf("a. The value of getpid() is %d\n", getpid());printf("b. The value of getppid() is %d\n", getppid());}else {printf("Error!\n");}return 0;
}
输出:
[atri@hcss-ecs-d3d5 process]$ ./a.out
main() progress
A. pid of main() is 21309
B. parent pid of main() is 18027
This is the value that return in main progress.
a. The value of getpid() is 21309
b. The value of getppid() is 18027
This is the sub progress of main()
1. The value of getpid() is 21310
2. The value of getppid() is 21309
[atri@hcss-ecs-d3d5 process]$
难以理解: 这里的If, else if 同时成立, 且同时执行.
查看进程:
方法一: ps
指令
[atri@hcss-ecs-d3d5 ~]$ ps -ajxPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND0 1 1 1 ? -1 Ss 0 0:12 /usr/lib/systemd/systemd --switched-root --system --deseriali0 2 0 0 ? -1 S 0 0:00 [kthreadd]2 4 0 0 ? -1 S< 0 0:00 [kworker/0:0H]2 6 0 0 ? -1 S 0 0:00 [ksoftirqd/0]2 7 0 0 ? -1 S 0 0:00 [migration/0]
......
......
字段解释:STAT列中的+表示前台进程组(如S+),无+则为后台进程。TPGID为前台进程组ID,若为-1表示无控制终端。"
技巧:可以用head命令,指定打印表头,再用管道命令配合 grep
命令过滤想查找的进程,如查找 bash
进程:
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep "bash"PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
18026 18027 18027 18027 pts/0 18248 Ss 1000 0:00 -bash
18027 18249 18248 18027 pts/0 18248 S+ 1000 0:00 grep --color=auto bash
[atri@hcss-ecs-d3d5 ~]$
方法二: /proc
目录
该目录中以数字命名的目录为进程 id
[atri@hcss-ecs-d3d5 proc]$ ls -al
total 4
dr-xr-xr-x 111 root root 0 Feb 22 18:54 .
dr-xr-xr-x. 20 root root 4096 Feb 28 18:30 ..
dr-xr-xr-x 9 root root 0 Feb 22 18:54 1
dr-xr-xr-x 9 root root 0 Feb 22 18:54 10
dr-xr-xr-x 9 root root 0 Feb 22 18:54 103
dr-xr-xr-x 9 root root 0 Feb 22 18:54 11
dr-xr-xr-x 9 postfix postfix 0 Mar 1 14:22 11135
......
......
进程的状态
Linux对状态的定义:
/** The task state array is a strange "bitmap" of* reasons to sleep. Thus "running" is zero, and* you can test for combinations of others with* simple bit tests.*/
static const char *task_state_array[] = {//运行状态"R (running)", /* 0 *///浅度睡眠状态"S (sleeping)", /* 1 *///深度睡眠状态"D (disk sleep)", /* 2 *///暂停状态, 可以重启, 前台变后台R+变R, S+变S"T (stopped)", /* 4 */"Z (zombie)", /* 8 */"X (dead)" /* 16 */
};
运行状态(R状态)
有如下程序:
#include <stdio.h>
int main(void) {int a = 1;while(1) {a++;}return 0;
}
执行时查看进程,为 R+
状态,即前台运行状态
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep myprocessPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
23721 26701 26701 23721 pts/0 26701 R+ 1000 0:43 ./myprocess
26710 26776 26775 26710 pts/1 26775 S+ 1000 0:00 grep --color=auto myprocess
浅度睡眠状态(S状态)
将程序稍作修改,如下:
#include <stdio.h>
int main(void) {int a = 1;while(1) {a++;printf("a = %d\n", a);}return 0;
}
再查看当前进程, 发现是 S+
状态, 即前台睡眠状态, 因为此时使用了 printf()
函数, 该函数功能是把数据打在屏幕上, 访问了外设, 所以进程一定会在外设准备数据的时候在其阻塞队列中等待, 至于此时进程的状态是静止阻塞(进程数据在磁盘), 还是活动阻塞(进程数据在内存), 这个就看系统的策略了, 不必纠结, 可以理解为 Linux
把状态给封装起来了, 对用户不可见.
该进程的PCB在阻塞队列和运行队列来回跑, 且在阻塞队列中呆得时间长, 因为外设速度更慢, 准备数据需要的时间也比CPU执行打印操作更长.
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep myprocessPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
23721 27247 27247 23721 pts/0 27247 S+ 1000 0:00 ./myprocess
26710 27261 27260 26710 pts/1 27260 S+ 1000 0:00 grep --color=auto myprocess
[atri@hcss-ecs-d3d5 ~]$
暂停状态(T状态)
在演示暂停状态前, 先补充两个 kill
的信号
使用 kill -l
指令列出其所有可用的信号, 即其对应的数字.
[atri@hcss-ecs-d3d5 process]$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
其中 9) SIGKILL
是终止进程, 19) SIGSTOP
是暂停进程, 18) SIGCONT
是继续执行.
现在再将上面演示 睡眠状态
的进程跑起来, 然后使用 18) SIGCONT
和 19) SIGSTOP
对进程操作, 看看其状态变化.
在该进程向屏幕上打印数据的过程中, 使用了 19) SIGSTOP
指令之后, 可以发现进程的状态变成了 T
, 即暂停状态.
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep myprocessPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
23721 28152 28152 23721 pts/0 28152 S+ 1000 0:00 ./myprocess
26710 28161 28160 26710 pts/1 28160 S+ 1000 0:00 grep --color=auto myprocess
[atri@hcss-ecs-d3d5 ~]$ kill -19 28152
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep myprocessPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
23721 28152 28152 23721 pts/0 23721 T 1000 0:00 ./myprocess
26710 28204 28203 26710 pts/1 28203 S+ 1000 0:00 grep --color=auto myprocess
[atri@hcss-ecs-d3d5 ~]$
此时再用 18) SIGCONT
让进程继续运行, 此时发现, 进程继续执行了, 且原先的 +
消失了, 即此时的进程变成了后台进程:
[atri@hcss-ecs-d3d5 ~]$ kill -18 28152
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep myprocessPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
23721 28152 28152 23721 pts/0 23721 S 1000 0:00 ./myprocess
26710 28433 28432 26710 pts/1 28432 S+ 1000 0:00 grep --color=auto myprocess
[atri@hcss-ecs-d3d5 ~]$
此时该进程变成了后台进程, 在启动该进程的shell上, 使用 CTRL + C已经无法退出该进程了, 但该进程还会在这个shell上打印数据, 需要使用kill命令来停止它.
补充: SIGTSTP(Ctrl+Z)可将前台进程挂起为后台停止状态(T), fg
命令可恢复为前台运行.
深度睡眠状态(D状态)
这个状态不是很常见, 简言之, 就是一个特殊的进程(比如用来向磁盘写入重要数据的文件), 给它一个特殊的权限, 操作系统在内存资源不足的时候也不会把它 kill
掉, 以保证数据的完整性. 但, 如果操作系统中堆满了这种进程之后, 就会导致操作系统资源不足, 造成操作系统本身出现问题. 对于这种进程, 除非断电, 或进程自己醒来, 才能结束掉.
例: "执行sync命令强制写盘时, 相关进程可能短暂进入D状态, 此时不可被kill -9终止.
本部分内容等学完进程控制再做修改
僵尸状态(Z状态)
描述:
当进程退出时, 不能立刻释放其对应的资源, 需要等其父进程或操作系统来读取其返回状态, 在这之后才可以释放, 这个结束到释放资源的过程称为僵尸状态.
特殊进程
僵尸进程
描述:
进程已经退出了, 但父进程并没有对其进行回收.
示例: 构造一对父子进程, 待运行后杀掉子进程, 观察子进程的状态.
#include <stdio.h>
#include <unistd.h>
int main(void) {pid_t p = fork();if (p == 0) {//child;while (1) {printf("child pid = %d\n", getpid());sleep(1);}}else if(p > 0) {//parentwhile (1) {printf("parent pid = %d\n", getpid());sleep(1);}}else {printf("error\n");}return 0;
}
查看进程状态, 并将子进程使用 kill
命令结束, 由于父进程没有做内存回收工作, 所以子进程一直处于待读取的僵尸状态.
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep my_processPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
25745 3126 3126 25745 pts/0 3126 S+ 1000 0:00 ./my_process3126 3127 3126 25745 pts/0 3126 S+ 1000 0:00 ./my_process870 3138 3137 870 pts/1 3137 S+ 1000 0:00 grep --color=auto my_process
[atri@hcss-ecs-d3d5 ~]$ kill -9 3127
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep my_processPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
25745 3126 3126 25745 pts/0 3126 S+ 1000 0:00 ./my_process3126 3127 3126 25745 pts/0 3126 Z+ 1000 0:00 [my_process] <defunct>870 3154 3153 870 pts/1 3153 S+ 1000 0:00 grep --color=auto my_process
孤儿进程
描述:
当一个进程的父进程终止运行的时候, 1号进程(操作系统内核) 会领养该进程, 此时该进程称为孤儿进程.
如果进程成为孤儿前是个前台进程, 成为孤儿后会变成后台进程
示例:
有如下程序:
构造一对父子进程, 待运行后在某个时间使用 kill
命令结束父进程, 观察子进程的 ppid
变化情况
#include <stdio.h>
#include <unistd.h>
int main(void) {pid_t p = fork();if (p == 0) {//child;while (1) {printf("child pid = %d\n", getpid());sleep(1);}}else if(p > 0) {//parentwhile (1) {printf("parent pid = %d\n", getpid());sleep(1);}}else {printf("error\n");}return 0;
}
此时将进程运行起来, 可同时得到父子进程的 pid
:
[atri@hcss-ecs-d3d5 lesson13]$ ./my_process
parent pid = 2224
child pid = 2225
parent pid = 2224
child pid = 2225
......
查看进程信息:
# 此时的父子进程都在运行, 且都是S+状态, 即前台睡眠状态
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep my_processPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
25745 2224 2224 25745 pts/0 2224 S+ 1000 0:00 ./my_process2224 2225 2224 25745 pts/0 2224 S+ 1000 0:00 ./my_process870 2290 2289 870 pts/1 2289 S+ 1000 0:00 grep --color=auto my_process
# 使用kill命令杀掉父进程后, 子进程2225的ppid从2224变到了1,
# 此时的子进程就成了孤儿进程被操作系统接管
# 且变成了后台进程
[atri@hcss-ecs-d3d5 ~]$ kill -9 2224
[atri@hcss-ecs-d3d5 ~]$ ps -ajx | head -1 && ps -ajx | grep my_processPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND1 2225 2224 25745 pts/0 25745 S 1000 0:00 ./my_process870 2307 2306 870 pts/1 2306 S+ 1000 0:00 grep --color=auto my_process
[atri@hcss-ecs-d3d5 ~]$
操作系统为什么要领养孤儿进程?
答: 为了避免该进程变成僵尸进程而占用系统资源.
注意: 使用 kill
命令结束父进程, 父进程没有进入僵尸状态的原因是: 该进程的父进程是 bash
它在它的子进程被 kill
掉的时候会进行状态读取和资源回收.