先来看下进程是什么?
1、首先进程是什么呢?
进程是一个动态的实体,是程序的一次执行过程。进程是操作系统资源分配的基本单位。
2、进程和程序有什么区别呢?
进程和程序的区别在于进程是动态的,程序是静态的,进程是运行中的程序,程序是一些保存在硬盘上的可执行的代码。
注:linux下可通过命令ps或pstree查看当前系统中的进程
linux操作系统中,每个进程都是通过唯一的进程ID标识的(此ID是一个非负数)。
在linux系统中,用于对进程进行控制的主要系统调用如下:
- fork:用于创建一个新进程。
- exit:用于中止进程。
- exec:用于执行一个应用程序。
- wait:将父进程挂起来,等待子进程终止。
- getpid:获取当前进程的进程ID
- nice:改变进程的优先级
5.进程状态
运行状态(R):程序正在运行
可中断等待状态(S):进程正在等待某个事件完成(如数据到达)。等待过程可被信号或定时器唤醒。
不可中断等待状态(D):进程等待某个事件完成,在等待过程中不可以被信号或定时器唤醒,必须等待直到等待的事件发生。
僵死状态(Z):进程已终止,但进程描述符依然存在,直到父进程调用wait()函数后释放。
停止状态(T):进程因为收到SIGSTOP、SIGSTP、SIGTOU、SIGTIN信号后停止运行或者该进程正在被跟踪(调试状态)。
创建进程有两种方式:
一是由操作系统创建,二是由父进程创建
系统调用fork或vfork函数可以创建一个新进程,但是vfork在创建进程时实际上还是调用了fork函数,所以说fork函数是创建一个新进程的唯一方法。
(1)fork函数
头文件及其函数原型:
一个进程,是包括代码、数据和分配给进程的资源,fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就两个进程可以完全做相同的事,但如果初始化参数或者传入的变量不同,两个进程也可以做不同的事
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的进程中,只有少数值与原来发的进程的值不同,相当于克隆了一个自己
fork调用的一个奇妙之处就是它仅仅被调用一次,却能能够返回两次,
成功调用fork函数后,当前进程实际上已经分裂为两个进程,一个是原来的父进程,另一个是刚刚创建的子进程。父子进程在调用fork函数的地方分开。
返回值:
fork函数有两个返回值,一个是父进程调用fork函数后返回刚刚创建的子进程的的ID;另一个是刚刚创建的子进程中fork函数的返回值,为0,当然,出现这两个返回值的前提是进程创建成功,否则返回-1,它们的返回值可以用来区分父子进程。
#include <unistd.h>
#include<sys/types.h>
#include <stdio.h>
int main ()
{
pid_t fpid; //fpid表示fork函数返回的值
int count=0;
fpid=fork();
if (fpid < 0)
printf("error in fork!\n");
else if (fpid == 0) {
printf("我是子进程, 我的进程id 为 %d\n",getpid());
printf("fork返回值:%d\n",fpid);
count++;
}
else {
printf("我是父进程, 我的进程id 为 %d\n",getpid());
printf("fork返回值:%d\n",fpid);
count++;
}
printf("统计结果是: %d\n",count);
return 0;
}
运行结果:
执行完fork后,进程1的变量为count=0,fpid!=0(父进程)。进程2的变量为count=0,fpid=0(子进程),这两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。可以说,我们就是通过fpid来识别和操作父子进程的。
这里还要了解两个函数
getpid
和getppid
.
getpid:返回当前进程id,getppid:返回父进程id
头文件及其函数原型:
#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void)
pid_t getppid(void)
(2)vfork函数
头文件及其函数原型:
fork与vfork的区别
1. fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段。
2. fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制.
返回值:
调用成功在父进程中返回子进程的进程号,在子进程中返回0
调用失败返回-1。
下面为代码示例:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
int globvar = 5;
int main(void)
{
pid_t pid;
int var = 1;
int i;
printf("fork is different with vfork\n");
//pid = fork();
pid = vfork();
switch(pid){
case 0:
i = 3;
while(i-- > 0)
{
printf("child process is running\n");
globvar++;
var++;
sleep(1);
}
printf("child's globvar = %dvar = %d\n",globvar,var);
_exit(0); //这块必须用_exit(0)或调用exec,父进程才会被调度运行,否则会出现未知错误
case -1:
perror("process creation failed\n");
exit(0);
default:
i = 5;
while(i-- > 0)
{
printf("parent process is running\n");
globvar++;
var++;
sleep(1);
}
printf("parent's globvar = %d,var = %d\n",globvar,var);
exit(0);
}
}
若注释掉第13行,即调用vfork,运行结果为:
若注释掉第14行,即调用fork,运行结果为:
从两个结果可以看出它们的区别,调用fork函数,子进程继承父进程的全局变量和局部变量。子进程中全局变量globvar和局部变量var的值均递增3,而父进程两者均递增5,这说明fork的子进程有自己独立的地址空间
,
再来看vfork,父进程中globvar和var最后均递增了8,这说明vfork的子进程共享父进程的地址空间,子进程修改变量对父进程是可见的。
注意:vfork() 保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程)之后父进程才可能被调度运行。如果子进程没有调用 exec, exit, 则会出现未知错误.
还是上面的程序(注释掉第13行),把26行_exit(0)改为break,则会出现以下结果,父进程中的局部变量var(本来应为9)现在却出现没有预料到的结果.
所以说这里一定要注意。