目录
掌握:进程概念、进程包含内容、进制状态、查看进程信息、前后台进程切换、改变进行优先级、创建子进程、父子进程、结束进程
程序
进程
进程控制块(pcb) :进程标识PID 、进程用户 、进程状态、优先级 、文件描述符表
进程放在RAM种,程序放在ROM中(单片机就在flash中)
运行态:进程正在运行,或者准备运行
等待态:进程在等待一个事件的发生或某种系统资源 (分为可中断和不可中断)
停止态:进程被中止,收到信号后可继续运行
死亡态:已终止的进程,但pcb没有被释放(僵尸态)
- ps 查看系统进程快照
- top 查看进程动态信息
- /proc 查看进程详细信息
- ps 命令详细参数:
- -e:显示所有进程
- -l:长格式显示更加详细的信息
- -f 全部列出,通常和其他选项联用
表头 | 含义 |
F | 进程标志,说明进程的权限,常见的标志有两个:
|
S | 进程状态。进程状态。常见的状态有以下几种:
|
UID | 运行此进程的用户的 ID; |
PID | 进程的 ID; |
PPID | 父进程的 ID; |
C | 该进程的 CPU 使用率,单位是百分比; |
PRI | 进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行; |
NI | 进程的优先级,数值越小,该进程越早被执行; |
ADDR | 该进程在内存的哪个位置; |
SZ | 该进程占用多大内存; |
WCHAN | 该进程是否运行。"-"代表正在运行; |
TTY | 该进程由哪个终端产生; |
TIME | 该进程占用 CPU 的运算时间,注意不是系统时间; |
CMD | 产生此进程的命令名; |
top 查看进程动态信息
shift +> 后翻页
ps -elf|grep watchdog
shift +< 前翻页
top -p PID 查看某个进程
示例:
- ps -elf|grep watchdog
- //列出所有进程,找到字符串有watchdog的进程
示例:
ls /proc/
/proc
是一个虚拟文件系统,提供了关于运行中进程和系统内核状态的信息。
数字代表某个进程目录
ls /proc/数字
nice 按用户指定的优先级运行进程
- nice [-n NI值] 命令
- NI 范围是 -20~19。数值越大优先级越低
- 普通用户调整 NI 值的范围是 0~19,而且只能调整自己的进程。
- 普通用户只能调高 NI 值,而不能降低。如原本 NI 值为 0,则只能调整为大于 0。
- 只有 root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程。
renice 改变正在运行进程的优先级
renice [优先级] PID
示例:
- nice -n 10 command //将启动一个新的 shell 进程,并将它的优先级设置为 10。
- renice -n 5 -p 1234 //将把进程 ID 为 1234 的进程的优先级设置为 5。
示例:
创建一个循环程序,编译执行
- #include
- #include
-
- int main(int argc, char * argv[])
- {
- while(1)
- {
- sleep(1);
- }
- return 0;
- }
后台运行两种方式:
CTRL+Z
的组合键可以将当前正在运行的前台进程挂起,并放入后台,同时返回一个进程挂起的提示信息。这个被挂起的进程会停止执行,但它的状态不会被清除,直到被唤醒或终止。此时查看进程状态,可以发现状态为T,代表停止。
- linux@linux:~/Desktop$ ps -elf|grep a.out
- 0 T linux 2869 2784 0 80 0 - 506 signal 09:46 pts/1 00:00:00 ./a.out
- 0 S linux 3226 2784 0 80 0 - 1171 pipe_w 10:18 pts/1 00:00:00 grep --color=auto a.out
-
-
- //T代表已经a.out程序已经停止
- jobs 查看后台进程
- bg 将挂起的进程在后台运行
- fg 把后台运行的进程放到前台运行,接序号
示例:
- linux@linux:~/Desktop$ jobs
- [1]+ Stopped ./a.out
- linux@linux:~/Desktop$ fg 1
- ./a.out
-
-
- linux@linux:~/Desktop$ bg 1
- [1]+ ./a.out &
- linux@linux:~/Desktop$ ps -elf|grep a.out
- 0 S linux 2869 2784 0 80 0 - 506 hrtime 09:46 pts/1 00:00:00 ./a.out
- 0 S linux 3329 2784 0 80 0 - 1171 pipe_w 10:32 pts/1 00:00:00 grep --color=auto a.out
- linux@linux:~/Desktop$ jobs
- [1]+ Running ./a.out &
子进程为由另外一个进程(对应称之为父进程)所创建的进程
除了系统启动时的第一个进程(通常是 init 或 systemd或0号进程)外,所有其他进程都是由父进程创建的。这种父子进程的关系形成了一个进程树或进程层级结构。
- #include
- pid_t fork(void);
示例:
- #include
- #include
-
- int main(int argc,char **argv)
- {
- pid_t pid;
- printf("before fork\n");
- pid = fork();
- printf("after fork\n");
- printf("pid=%d\n",(int)pid);
-
- }
-
-
- linux@linux:~/Desktop$ ./a.out
- before fork
- after fork
- pid=3607 //父打印子进程号
- linux@linux:~/Desktop$
- after fork
- pid=0 //子进程打印0
注意:
1 子进程只执行fork之后的代码
2.父子进程执行顺序是操作系统决定的。
如果需要控制谁先执行,需要加一些延时
- #include
- #include
-
- int main(int argc,char **argv){
-
- pid_t pid;
- printf("before fork\n");
- pid = fork();
- if(pid>0){
- printf("This is father process\n");
- printf("pid=%d\n",(int)pid);
- printf("father after fork\n");
- }else if(pid==0){
- printf("This is child process\n");
- printf("pid=%d\n",(int)pid);
- printf("child after fork\n");
- }else if(pid<0){
- perror("fork");
- return 0;
- }
- // printf("pid=%d\n",(int)pid);
- // printf("after fork\n");
-
-
- }
子进程继承了父进程的内容(从fork以后执行)
父子进程有独立的地址空间,互不影响
若父进程先结束 :
-子进程成为孤儿进程,被init进程收养
-子进程变成后台进程
若子进程先结束:
- 父进程如果没有及时回收,子进程变成僵尸进程
- #include
- #include
-
- int main(int argc,char **argv){
-
- pid_t pid;
- printf("before fork\n");
- pid = fork();
- if(pid>0){
- printf("This is father process\n");
- printf("pid=%d\n",(int)pid);
- printf("father after fork\n");
- while(1){
- sleep(1);
- printf("father sleep\n");
- }
- }else if(pid==0){
- printf("This is child process\n");
- printf("pid=%d\n",(int)pid);
- printf("child after fork\n");
- while(1){
- sleep(1);
- printf("child sleep\n");
- }
- }else if(pid<0){
- perror("fork");
- return 0;
- }
- // printf("pid=%d\n",(int)pid);
- // printf("after fork\n");
-
-
- }
kill -9 分别演示父进程和子进程先结束的状态。
1代表子进程被init收养了
0代表僵尸进程
子进程从何处开始运行?
答:子进程从fork()函数的调用处开始运行。
父子进程谁先执行?
答:父子进程的执行顺序是不确定的,取决于操作系统的调度算法。在一般情况下,父进程和子进程是并发执行的,可能会交替执行,也可能先执行父进程再执行子进程。
父进程能否多次调用fork? 子进程呢?
答:父进程可以多次调用fork()函数创建多个子进程。每次调用fork()函数都会返回两次,在父进程中返回子进程的进程ID(PID),在子进程中返回0。这样可以通过判断返回值来区分父进程和子进程,并根据需要执行不同的代码逻辑。
子进程也可以调用fork()函数创建自己的子进程,从而形成进程的层次结构,但需要注意,子进程创建的子进程与原始父进程无关。
示例:是否创建了5个进程?
- #include
- #include
-
- int main(){
- pid_t pid;
- int i;
- for(i=0;i<5;i++){
- pid = fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- printf("child process\n");
- sleep(5);
- }else{
- printf("Father process\n");
- sleep(5);
- }
- }
-
- sleep(100);
-
-
- }
实际子进程在fork,父进程也在fork
示例:只让父进程fork
- #include
- #include
-
- int main(){
- pid_t pid;
- int i;
- for(i=0;i<5;i++){
- pid = fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- printf("child process\n");
- sleep(5);
- break;
- }else{
- printf("Father process\n");
- sleep(5);
- }
- }
-
- sleep(100);
-
-
- }
示例:只让子进程fork
- #include
- #include
-
- int main(){
- pid_t pid;
- int i;
- for(i=0;i<5;i++){
- pid = fork();
- if(pid<0){
- perror("fork");
- return 0;
- }else if(pid==0){
- printf("child process\n");
- sleep(5);
- }else{
- printf("Father process\n");
- sleep(5);
- break;
- }
- }
-
- sleep(100);
-
-
- }
- #include
- #include
- void exit(int status);
- void _exit(int status);
- void _Exit(int status); //与小写一样
结束当前的进程并将status返回
exit结束进程时会刷新(流)缓冲区(区别)
示例1:
主函数(main函数)在结束时默认会调用exit()函数。exit()函数用于正常终止程序,并返回一个退出状态给操作系统。不当主函数执行完毕或遇到return语句时,会自动隐式地调用exit()函数。这会导致程序退出,并将返回值作为退出状态码传递给操作系统。
- #include
- #include
//exit 和_exit引用不一样 -
- int main(void) {
- printf(“this process will exit”);
- exit(0);
- printf(“never be displayed”); //不打印
- }
-
-
- $ ./a.out
- this process will be exit //加不加exit始终会打印
示例2:
- #include
- #include
-
- int main(void) {
- printf(“using exit…\n”);
- printf(“This is the end”);
- exit(0);
- }
-
- $ ./a.out
- using exit…
- This is the end
示例3:不刷新缓存区的_exit()
- #include
- #include
- #include
-
- int main(int argc,char**argv){
-
- printf("hello world"); //不打印
- _exit(0);
- printf("after exit"); //不打印
- return 0;
- }
-
-
- //什么都不打印
子进程结束时由父进程回收
孤儿进程由init进程回收
若没有及时回收会出现僵尸进程
- #include
-
- pid_t wait(int *status);
示例:
- #include
- #include
- #include
- #include
-
- int main(int argc, char** argv){
-
- pid_t pid;
- pid_t rpid;
- pid = fork();
- int status;
- if(pid<0){
- perror("fork");
- return 0;
- }
- else if(pid == 0){
- sleep(10);
- printf("child will exit\n");
- exit(2);
-
- }else if(pid >0){
- rpid = wait(&status);
- printf("Get child status=%x\n",WEXITSTATUS(status));
- }
-
- }
-
返回值是多个内容组成的标志位,需要调用宏定义打印返回值
查看是否有僵尸进程
- #include
- #include
- #include
- #include
-
- int main(int argc, char** argv){
-
- pid_t pid;
- pid_t rpid;
- pid = fork();
- int status;
- if(pid<0){
- perror("fork");
- return 0;
- }
- else if(pid == 0){
- sleep(10);
- printf("child will exit\n");
- exit(2);
-
- }else if(pid >0){
- rpid = wait(&status);
- sleep(20);
- printf("Get child status=%x\n",WEXITSTATUS(status));
- }
-
- while(1){
- sleep(1);
- }
-
- }
如果去除wait,产生了僵尸进程
进程返回值和结束方式
子进程通过exit / _exit / return 返回某个值(0-255)
父进程调用wait(&status) 回收
- #include
- pid_t waitpid(pid_t pid, int *status, int option);
参数:
pid
options
options提了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用
WNOHANG :若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
WUNTRACED: 返回终止子进程信息和因信号停止的子进程信息
wait(wait_stat) 等价于waitpid(-1,wait_stat,0)
示例:
- waitpid(pid, &status, 0);
-
- waitpid(pid, &status, WNOHANG);
-
- waitpid(-1, &status, 0);
-
- waitpid(-1, &status, WNOHANG);
- #include
- #include
- #include
- #include
-
- int main(int argc, char** argv){
-
- pid_t pid;
- pid_t rpid;
- pid = fork();
- int status;
- if(pid<0){
- perror("fork");
- return 0;
- }
- else if(pid == 0){
- sleep(10);
- printf("child will exit\n");
- exit(2);
-
- }else if(pid >0){
- rpid = wait(&status);
- sleep(20);
- //waitpid(-1,&status,WNOHANG); //只有一个进程,-1和参数pid效果一样
- waitpid(pid,&status,0);
- printf("Get child status=%x\n",WEXITSTATUS(status));
- }
-
- while(1){
- sleep(1);
- }
-
- }
示例2
waitpid早就执行了,但是exit还会执行,注意这种情况可以考虑waitpid前也加延时函数,晚于exit用于进程回收