进程
进程是计算机中正在运行的程序的实例。每个进程都有自己的地址空间、内存、文件和设备、线程以及其他系统资源。操作系统通过调度和管理进程来实现多任务处理,使得多个进程可以同时运行并与用户交互。在操作系统中,进程是基本的资源分配单位,它可以独立运行,也可以与其他进程进行通信和协作。
进程是程序的一次运行活动。
常用命令
在linux系统中,有几个常用的命令对进程进行操作。查看进程命令ps和top。结束进程命令kill,以及管道和重定向命令。
-
静态显示系统进程信息(ps)
ps指令是linux系统标准的进程查看工具,通过它可以查看系统中进程的详细信息
常用参数有:
a:显示所有进程(包括其他用户的进程) u:以用户为主的格式来显示进程情况 x:显示没有控制终端的进程 e:显示环境变量 f:做全格式列出 l:长格式显示 r:只显示正在运行的进程 p:按照进程ID列出进程
例子:
ps # 显示当前用户进程 ps -aux # 显示全部跟用户有关进程的所有信息
常与管道组合使用:
ps -aux |grep a # 显示有关a进程的所有信息
-
动态显示系统进程信息(top)
top命令相当于windows系统中的任务管理器,top是一个动态显示的过程,他通过不断的刷新当前的状态以动态的显示进程。调用top之后,它将独占前台,直到用户终止该程序(ctrl + c)。
语法:
top [-d] | top [-bnp]
常用参数:
-b:以批处理模式操作。 -c:显示完整的命令行。 -d:屏幕刷新间隔时间。 -I:忽略失效过程。 -s:保密模式。 -S:累积模式。 -i:不显示闲置和僵死进程。 -n:更新显示次数。
进程标识符
每个进程都有一个非负整数表示唯一的ID,叫做pid,类似我们的身份证。
编程调用getpid函数获取本身的进程标识符,调用getppid获取父进程的进程标识符。
Linux进程控制
进程创建
在Linux系统中,创建进程的方式有两种:操作系统创建,和父进程创建。操作系统创建的进程是平等关系的,而父进程创建的子进程不是平等关系,而且相互之间存在资源继承的关系。而且父进程创建的子进程又可以创建子进程,从而形成一个进程家族。
fork函数
系统创建进程的通用方法是使用函数fork();我们通过在Linux终端输入man 2 fork命令,我们看到fork的使用说明。
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=
.png&pos_id=img-S99QMrCK-1733831801522)
该函数的返回类型为整形,若是父进程则放回值为一个正整数,是子进程的pid号。若为子进程,则返回值为0。我们可以写段代码进行试验。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t pid;printf("befor fork\n");printf("father process pid = %d \n",getpid());pid = fork();if(pid > 0){printf("this is father;pid = %d\n",getpid());}else if(pid == 0){printf("this is son; pid = %d\n",getpid());}return 0;
}
我们看到以下结果:
如果你使用man命令,发现他找不到这些,出现以下情况:
不要慌,按照以下步骤操作一下。
# 1、更新一下资源 sudo apt-get update # 2、安装标准c相关的帮助文档 sudo apt-get install libc-dev sudo apt-get install glibc-doc
vfork函数
man 2 vfork
命令看到的结果
#include <sys/types.h>
#include <unistd.h>pid_t vfork(void);
我们看到与fork函数一个样子,该函数也是放回一个整形,也是创建一个新的进程,但是他们两个函数是不一样的。与fork函数作比较,虽然功能和参数都类似,但是也有自己的独特之处。例如:
- fork函数创建子进程是对父进程的完全拷贝,所谓完全拷贝就是将父进程的代码完全复制一份运行,这样子子进程就能完全独立于父进程运行,这样子的代码具有良好的并发性;而vfork函数创建的子进程,是和父进程共享地址空间,子进程需要完全运行在父进程的地址空间上,子进程对地址空间的数据修改同样会影响到父进程。
- vfork函数创建的子进程会优先运行,当它执行完exit或者exec后,父进程才可以运行;而fork函数创建的子进程运行的优先级取决于系统的调度算法。
由于vfork函数不会复制父进程的地址空间,会节省系统大量的开销,运行速度也非常快。
我们同样可以用几个代码来看看效果。
首先我们用fork函数来写一段代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid;int cnt = 0;printf("befor fork\n");printf("father process pid = %d \n",getpid());pid = fork();if(pid > 0){while(1){printf("this is father;pid = %d\n",getpid());sleep(2);printf("father: %d \n",cnt);}}else if(pid == 0){while(1){cnt ++;printf("this is son; pid = %d\n",getpid());sleep(2);if(cnt >= 5){printf("the son exit\n");exit(0);}}}return 0;
}
运行结果如下:
hyx@hyx-virtual-machine:~/c_project/c_Process$ ./a.out
befor fork
father process pid = 6759
this is father;pid = 6759
this is son; pid = 6760
father: 0
this is father;pid = 6759
this is son; pid = 6760
father: 0
this is father;pid = 6759
this is son; pid = 6760
father: 0
this is father;pid = 6759
this is son; pid = 6760
^C
通过运行结果我们看到,父进程和子进程在同时运行,并且在父进程中,子进程更改了值,但是父进程这边没有。
那这边用vfork修改一下代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid;int cnt = 0;printf("befor fork\n");printf("father process pid = %d \n",getpid());pid = vfork();if(pid > 0){while(1){printf("this is father;pid = %d\n",getpid());sleep(2);printf("father: %d \n",cnt);}}else if(pid == 0){while(1){cnt ++;printf("this is son; pid = %d\n",getpid());sleep(2);if(cnt >= 3){printf("the son exit\n");exit(0);}}}return 0;
}
运行结果:
befor fork
father process pid = 6975
this is son; pid = 6976
this is son; pid = 6976
this is son; pid = 6976
the son exit
this is father;pid = 6975
father: 3
this is father;pid = 6975
father: 3
this is father;pid = 6975
^C
通过结果我们发现,vfork函数创建的子进程会优先执行,在调用exit函数之后,父进程才开始运行,并且在子进程中对cnt的值进行了修改,在父进程中这个值也被修改了。
进程等待
在Linux系统中,当多个进程同时进行的时候,进程间需要协作工作,可能用到进程等待的操作。进程间的等待包括父子进程间的等待和进程组内成员间的等待。
同时用了wait方法接受推出的状态,如果子进程退出状态,没有被收集,子进程将变成僵尸进程。
进程等待有两种方法:wait和waitpid。
在Linux系统终端中使用帮助命令main wait
,得到函数以下信息:
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *wstatus);pid_t waitpid(pid_t pid, int *wstatus, int options);
wait函数
wait函数返回类型为整形,wstatus为一个整形指针,用于存放子进程的结束状态。当wait被调用时,父进程处于挂起状态,只有当子进程结束返回。如果wait调用的父进程没有子进程,则返回失败。
调用成功:返回等待状态进程的pid; 调用失败:返回-1
我们同样用上面的代码的基础上,进行简单 的修改来看看情况。
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid;int cnt = 0;int status;printf("befor fork\n");printf("father process pid = %d \n",getpid());pid = fork();if(pid > 0){wait(&status);printf("status = %d\n",status);while(1){printf("this is father;pid = %d\n",getpid());sleep(2);printf("father: %d \n",cnt);}}else if(pid == 0){while(1){cnt ++;printf("this is son; pid = %d\n",getpid());sleep(2);if(cnt >= 3){printf("the son exit\n");exit(1);}}}return 0;
}
运行结果如下:
befor fork
father process pid = 7374
this is son; pid = 7375
this is son; pid = 7375
this is son; pid = 7375
the son exit
status = 65280
this is father;pid = 7374
father: 0
this is father;pid = 7374
^C
通过运行结果我们可以看到,用wait同样也可以使子线程优先运行。但是,接受到的status不是我们以为的值。那是因为,我们如果想要的是放回的值,那么我们还需要添加那些用于解释进程退出状态的宏。
我们将WEXITSTATU(status)
代替status
,我们就能得到正常的输出了。
宏
waitpid函数
waitpid函数,返回值为整型,调用更加灵活,用于等待指定的进程。
status参数是个整形的指针用于存放子进程的结束状态。
pid参数是个整型参数,pid用于指定所等待的进程。
options参数指定所作的操作,取值0:表示进程挂起等待结束;取值WNOHANG表示不使进程挂起而即刻返回;取值WUNTRACED表示进程已经结束并返回。
调用成功返回等待状态的ID;调用失败返回-1。
我们魔改一下wait的代码,使用waitpid代替wait。代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid;int cnt = 0;int status;printf("befor fork\n");printf("father process pid = %d \n",getpid());pid = fork();if(pid > 0){waitpid(pid,&status,0);printf("status = %d\n",WEXITSTATUS(status));while(1){printf("this is father;pid = %d\n",getpid());sleep(2);printf("father: %d \n",cnt);}}else if(pid == 0){while(1){cnt ++;printf("this is son; pid = %d\n",getpid());sleep(2);if(cnt >= 3){printf("the son exit\n");exit(37);}}}return 0;
}
运行结果:
befor fork
father process pid = 7530
this is son; pid = 7531
this is son; pid = 7531
this is son; pid = 7531
the son exit
status = 37
this is father;pid = 7530
^C
通过运行结果,我们可以看到,用waitpid实现了wait函数同样的效果。
system函数
在linux环境下,我们使用man system
看到system函数的信息如下:
#include <stdlib.h>int system(const char *command);
我们可以在linux环境中使用system函数执行系统指令。command是一个字符串指针,指向表示命令的字符串。system函数可以执行系统命令,同时也可以调用fork、exec、waitpid。
接下来我们试一试:
#include <stdio.h>
#include <stdlib.h>int main()
{printf("show ls -a \n");system("ls -a");printf("done\n");exit(0);return 0;
}
结果如下:
show ls -a
. .. 01fork.c 02fork.c 03vfork.c 04wait.c 05waitpid.c 06system.c a.out
done
结果与ls -a
效果相同。
总结
进程的相关操作是Linux编程的重要环节。熟悉这些进程控制的api的使用,对我们初学Linux平台下的c语言编程大有帮助。本文重点介绍了,进程的创建常用的俩函数,以及进程的等待。以及system函数。之后将重点介绍一下exec族。