进程是程序的一次执行过程
程序:是静态的,是存储在外存上的可执行二进制文件
进程:动态的概念,是程序的一次执行过程,包括了进程的创建,调度,消亡,是存在于内存中的
进程是独立的,可以被CPU调度的任务
Linux中的调度机制:时间片轮询机制
操作系统会给每一个进程分配时间片,当时间片结束后,CPU资源会切走,当前进程需要等待下一次调度
进程在被调度的时候,系统会分配和释放各种资源(CPU资源,内存资源,进城调度块(PCB))
虚拟内存和物理内存之间的关系—>映射关系
以进程为单位分配和释放各种资源
操作系统会给每一个进程分配一个编号,这个编号就是进程号
fork函数创建的子进程,会克隆父进程用户空间的所有资源,以及PC寄存器的值。所以子进程不会运行执行过的fork函数以及fork函数以上的代码
(ps:PC寄存器中存储着下一次该运行到哪一行代码)
父子进程的虚拟地址空间不是同一块,但是由于子进程是从父进程拷贝过来的,所以fork完毕的一瞬间,父子进程的用户空间长得完全一致
父子进程映射的物理地址不是同一块空间
**写时拷贝:**当父子进程均不修改其中的内容的时候,此时映射的物理地址是同一块空间。若某个进程要修改其中的内容的时候,此时才会真正的申请一块新的物理地址空间给子进程的虚拟地址映射
_exit
功能:结束进程,销毁其在内存中的资源,且直接摧毁缓冲区,不会刷新缓冲区
原型:void _exit(int status)
参数:int status:可以传递进程推出状态值给其父进程,父进程可以通过wait/waitpid函数接收。可以传递任意整型
exit
功能:结束进程,销毁其在内存中的资源,会刷新缓冲区
原型:void exit(int status)
参数:int status:可以传递进程退出状态值给其父进程,父进程可以通过wait/ waitpid函数接收。可以传递任意整型
wait
功能:阻塞函数,阻塞等待任意子进程退出;回收退出的子进程的资源
原型:pid_t wait(int *wstatus)
参数:int *wstatus:接收子进程传递回来的退出状态值,若不想接收,则填NULL
返回值:>0,成功返回退出的子进程的PID号;= -1,函数运行失败,更新errno;
若没有子进程,则wait函数运行失败;
父进程只能回收子进程,无法回收孙子进程
若子进程退出,父进程没有回收子进程的资源,此时子进程会变成僵尸进程。
若没有子进程,则wait函数运行失败
子进程退出状态
wait(int *wstatus)中,int *wstatus指向的int类型参数,只有【8bit,15bit】用于存储子进程传递的退出状态值。返回为【0,255】
所以子进程只能传递256种状态
从wstatus中提取子进程退出状态值
int wstatus = -1;
pid_t wpid = wait(&wstatus);
printf("wpid = %d wstatus=%d\n", wpid, wstatus>>8);
判断子进程是否正常退出
WIFEXITED(wstatus) 若正常退出,则返回真。
正常退出:exit _exit 主调函数用return退出
waitpid
功能:阻塞等待指定子进程退出
原型:pid_t waitpid(pid_t pid, int *wstatus, int options)
参数:pid_t pid:
< -1:阻塞等待指定进程组下的任意一个子进程退出
-1:阻塞等待当前进程下的任意一个子进程退出;与wait函数的功能基本一致
0:阻塞等待当前进程组下的任意一个子进程退出>0
(>0:阻塞等待指定的子进程退出;子进程的pid号 == pid参数
int *wstatus:接收子进程传递回来的退出状态值,若不想接收,则填NULL
int options:0:阻塞方式运行,当指定的子进程没有退出的时候,该函数阻塞,直到指定子进程退出,解除阻塞;
WNOHANG:非阻塞方式运行,当指定的子进程没有退出,该函数不阻塞,立即返回;
返回值:成功,>0,成功回收到的子进程的pid号;=0,函数运行成功,但是此时子进程没有退出
函数运行失败,返回-1,更新errno
1.若指定的子进程不存在(没有子进程)的时候,函数运行失败;
2.子进程无法回收父进程的资源;
3.同级之间无法相互回收资源
父进程退出,子进程不退出,此时子进程被1号(int)进程收养,变成孤儿进程。孤儿进程会脱离终端控制,且运行在后端,不能用ctrl+c杀死后端进程,但是可以被kill -9杀死
#include
#include
#include
int main(int argc, const char *argv[])
{
//父进程退出 子进程不退出
pid_t cpid = fork();
if(cpid > 0)
{
}
else if(0 == cpid)
{
while(1)
{
printf("this is child %d %d\n", getppid(), getpid());
sleep(1);
}
}
else
{
perror("fork");
return -1;
}
return 0;
}
子进程退出,父进程不退出,且父进程没有给子进程收尸,此时子进程就变成僵尸进程
注意:
#include
#include
#include
int main(int argc, const char *argv[])
{
//子进程退出,父进程不退出
pid_t cpid = fork();
if(0 == cpid)
{
}
else if(cpid > 0)
{
while(1)
{
printf("this is parent %d %d\n", getpid(), cpid);
sleep(1);
}
}
else
{
perror("fork");
return -1;
}
return 0;
}
又称为幽灵进程
1>创建孤儿进程:所有工作都在子进程中执行,从形式上脱离终端控制
fork(),退出父进程
2>创建新的会话组:使子进程完全独立出来,防止兄弟进程对其有影响
setsid(),函数
3>修改当前孤儿进程的运行目录为不可卸载的文件系统:例如根目录,/tmp
防止运行目录被删除后,导致进程崩溃
4>重设文件权限掩码:umask(0),一般清零
5>关闭所有文件描述符,从父进程继承过来的文件秒数符不会用到,浪费资源
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
//创建孤儿进程
pid_t cpid = fork();
if(0 == cpid)
{
//创建新的会话
pid_t sid = setsid();
printf("sid = %d\n", sid);
//修改运行目录为不可卸载的文件目录下
chdir("/");
//清空文件权限掩码
umask(0);
//关闭所有文件描述符
for(int i = 0; i<getdtablesize(); i++)
close(i);
while(1)
{
//守护进程运行的周期性代码
sleep(1);
}
}
return 0;
}
文件IO函数实现,拷贝文件。子进程先拷贝后半部分,父进程再拷贝前半部分。
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd_r = open("./1.png", O_RDONLY);
if(fd_r < 0)
{
ERR_MSG("open");
return -1;
}
int fd_w = open("copy.png", O_WRONLY|O_CREAT|O_TRUNC, 0644);
if(fd_w < 0)
{
ERR_MSG("open");
return -1;
}
//计算文件大小
off_t size = lseek(fd_r, 0, SEEK_END);
pid_t cpid = fork();
if(cpid > 0)
{
sleep(4);
//父进程拷贝前半部分
//将偏移量修改到0
lseek(fd_r, 0, SEEK_SET);
lseek(fd_w, 0, SEEK_SET);
char c = 0;
for(int i=0; i<size/2; i++)
{
read(fd_r, &c, 1);
write(fd_w, &c, 1);
}
printf("前半部分拷贝完毕\n");
}
else if(0 == cpid)
{
//子进程拷贝后半部分
//将偏移量修改到size/2
lseek(fd_r, size/2, SEEK_SET);
lseek(fd_w, size/2, SEEK_SET);
char c = 0;
for(int i=size/2; i<size; i++)
{
read(fd_r, &c, 1);
write(fd_w, &c, 1);
}
printf("后半部分拷贝完毕\n");
}
else
{
ERR_MSG("fork");
return -1;
}
close(fd_r);
close(fd_w);
return 0;
}
打印时钟在终端上,若终端输入quit,结束时钟
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
pid_t cpid = fork();
if(cpid > 0)
{
time_t t;
struct tm *info = NULL;
while(1)
{
if(waitpid(-1,NULL,WNOHANG) > 0)
break;
t=time(NULL);
info=localtime(&t);
printf("%d-%02d-%02d %02d:%02d:%02d\r",info->tm_year+1900,info->tm_mon+1,\
info->tm_mday,info->tm_hour,info->tm_min,info->tm_sec);
fflush(stdout);
sleep(1);
}
}
else if(cpid == 0)
{
char buf[128]="";
while(1)
{
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strcmp(buf,"quit") == 0)
exit(0);
sleep(1);
}
}
return 0;
}