进程:运动起来的 程序。
程序是死的,只占用磁盘空间。
进程是活的,占用内存、CPU等系统资源。
1)进程是程序的一次动态执行。
2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
3)进程是具有独立功能的程序在其数据集合上运行的过程,他是系统调度和资源分配的一个独立单位。
每个进程在内核中都有一个进程控制块来维护进程的相关信息。(打开我们的用户管理器就可以看到每一个运行的进程的相关信息)
进程的五个状态:
调用函数:fork();
pid_t fork(void)
参数:void
返回值:fork函数返回父子进程标志;
子进程:返回值等于0;
父进程:返回值大于0;
调用失败:返回值等于-1;
获取父子进程的两个函数:
pid_t getpid() 获取当前进程id
pid_t getppid() 获取当前进程的父进程id
两个函数的返回值都是进程id
fork()函数的调用:
1)调用后存在两个进程,每个进程都会从fork()的返回值处继续执行,两个进程后各执行一次,调用之后,哪个进程先执行是无法确定的,由CPU的进程调度决定。
2)子进程的内存是对父进程的写时拷贝
//函数执行情况
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
printf("father pid is %d\n",getpid());
pid = fork();
if(pid > 0){
printf("this is father's pid print,pid is %d\n",getpid());
}else if(pid == 0){
printf("this is child's pid print,pid is %d\n",getpid());
}else if(pid == -1){
perror("fork error");
exit(1);
}
return 0;
}
//父子进程执行情况
#include
#include
#include
int main()
{
pid_t pid1;
pid_t pid2;
pid1 = getpid();//打印fork()函数之前的PID
printf("Now pid is:%d\n",getpid());
fork();//创建fork()函数,创建子进程,执行两次
pid2 = getpid();//获取fork()函数之后的pid
printf("after pid is:%d\n",getpid());
if(pid1 == pid2)//如果得到的PID和之前的PID相同,就是父进程在执行
{
printf("this is father's pid:%d\n",getpid());
}else{
//如果得到的PID和之前的PID不同,就是子进程在执行
printf("this is child's pid:%d\n",getpid());
}
return 0;
}
1)子进程直接使用父进程的存储空间,不拷贝。
2)vfork()保证,子进程先执行,等子进程调用_exit()或exec()后,将暂停执行,父进程才执行。
#include
#include
#include
#include
int main()
{
int cnt = 0;
pid_t pid;
pid = vfork();//vfork()创建进程
if(pid > 0){
while(1){
printf("this is father's pid print,pid is %d\n",getpid());
sleep(1);
cnt ++;
if(cnt == 6){
exit(0);
}
}
}
else if(pid == 0){
while(1){
printf("this is child's pid print,pid is %d\n",getpid());
sleep(1);
cnt ++;
if(cnt == 3){
exit(0);
}
}
}
return 0;
}
子进程先进行3次循环,然后在轮到父进程进行循环打印三次,然后退出程序:
在电脑上发出一个请求,我们的电脑就会根据每一个请求创建一个线程,同时又在等待新的请求。
#include
#include
#include
int main()
{
pid_t pid;
int data;
printf("please input a data:\n");
while(1){
scanf("%d",&data);
if(data == 1 || data == 6)
//当data等于1和6的时候就可创建进程
{
pid = fork();
if(pid > 0)
{
//fork()函数会对父进程和子进程各执行一次,所以将父进程空白化
}
if(pid == 0){ //子进程处理响应
while(1){
printf("yes,I get it,pid = %d\n",getpid());
sleep(3); //程序休眠3s防止刷屏
}
}
}
else{
printf("wait,do nothing.\n");
}
}
return 0;
}
1、exec族函数的作用
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序(父进程怕死,不希望调用新函数后自己就消失,所以用fork创建新的子进程,用exec杀死子进程来代替自己死)。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
(exec函数一旦调用就不能返回);
2、函数族
函数族:
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe;
#include
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
编写一个execl.c文件
#include
#include
#include
int main()
{
printf("This pro get system date:\n");
if(execl("/bin/date","date",NULL) == -1){ //打印时间
printf("execl failed\n");
}
return 0;
}
运行:
说明data在/bin/data目录下也是一个可以执行的文件。
编写一个execlp.c文件
//execlp.c
#include
#include
#include
//int execlp(const char *file, const char *arg, ...);
int main()
{
printf("before execl\n");
if(execlp("ls","ls","-l",NULL) == -1){
printf("execl failed\n");
}
printf("after execl\n");
return 0;
}
execl()与execlp()区别就在于execlp()会在环境变量中去找可执行文件,所以我们就相当于执行了一次 ls-l 命令
创建一个execvp.c文件
v指的是 应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。p指的是 从PATH环境变量中进行寻找可执行文件
//execvp.c
#include
#include
#include
//int execvp(const char *file, char *const argv[]);
int main()
{
printf("before execl\n");
char *argv[] = {"ls","-l",NULL};
if(execvp("ls",argv) == -1){
printf("execl failed\n");
}
return 0;
}