前言
#include <unistd.h>
pid_t fork(void);
由fork创建的新进程被称为子进程。fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值是子进程的进程ID。将子进程ID发回给父进程的理由是:因为一个进程的子进程可以有多哥,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID(进程ID 0总是由内核交换进程使用,所以一个进程ID不可能为0)。
子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段(代码段)。
问题一:什么是程序,什么是进程,有什么区别?
程序是静态的概念,gcc xxx.c -o pro 磁盘中生成pro文件,叫做程序。
进程是程序的一次运行活动,程序跑起来,系统中就多了一个进程。
问题二: 如何查看系统中有哪些进程?
a.使用ps指令查看,配合grep来查找程序中是否存在某一个进程。(ps -aux|grep xxx)
b.运用top指令,类似windows下的任务管理器
问题三:什么是进程标识符?
每个进程都有一个非负整数标识的唯一ID,叫做pid,类似身份证
pid=0:称为交换进程,作用—进程调度
pid=1:init进程,作用----系统初始化
编程调用getpid函数获取自身的进程标识符,getppid获取父进程的进程标识符。
问题四:什么叫父进程,什么叫子进程?
进程A创建了进程B,那么A叫做父进程,B叫做子进程,父进程是相对的概念,理解为人类中的父子关系。
问题五:C程序的存储空间是如何分配?(重点)
实列
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t pid;
pid_t pid2;
pid_t retpid;
pid=getpid();
printf("before fork: pid = %d\n",pid);
retpid=fork();
pid2=getpid();
printf("after fork:pid = %d\n",pid2);
if(pid==pid2)
{
printf("this is father print,retpid = %d\n",retpid);
}
else{
printf("this is child print,retpid=%d,child pid= %d\n",retpid,getpid());
}
return 0;
}
进程创建发生了什么?
数据段cp,共享代码段
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t pid;
int data=10;
printf("father: id=%d\n",getpid());
pid=fork();
if(pid>0)
{
printf("this is father print,pid = %d\n",getpid());
}
else if(pid==0){
printf("this is child print,child pid= %d\n",getpid());
data=data+100;
}
printf("data=%d\n",data);
return 0;
}
fork创建一个子进程的一般目的
1.一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的-------父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork/,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
2.一个进程要执行不同的程序。这对shell是常见的情况。在这种情况下,子进程返回后立即调用exec。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t pid;
int data=10;
while(1){
printf("please input a data\n");
scanf("%d",&data);
if(data==1){
pid=fork();
if(pid>0)
{
}
else if(pid==0){
while(1){
printf("do net request,pid=%d\n",getpid());
sleep(3);
}
}
}
else{
printf("wait, do nothing\n");
}
}
return 0;
}
vfork函数也可以创建进程,与fork的区别
关键区别一:vfork直接使用父进程存储空间,不拷贝。
关键区别二:vforl保证子进程先运行,当子进程调用exit退出后,父进程才执行。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt=0;
pid=vfork();
if(pid>0)
{
while(1){
printf("cnt=%d\n",cnt);
printf("this is father print,pid=%d\n",getpid());
sleep(1);
}
}
else if(pid==0){
while(1){
printf("this is chilid print ,pid=%d\n",getpid());
sleep(1);
cnt++;
if(cnt==3){
exit(0);
}
}
}
return 0;
}
进程退出
正常退出:1.main函数调用return 2.进程调用exit(),标准C库 3.进程调用_exit()或者_Exit(),属于系统调用
补充:1.进程最后一个线程返回 2.最后一个线程调用pthread_exit
父进程等待子进程退出
父进程等待子进程退出并收集子进程的退出状态(下一程序),子进程退出状态不被收集,变成僵尸进程(上一程序就是僵尸进程)。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt=0;
int status=10;
pid=fork();
if(pid>0)
{
wait(&status);
printf("child quit,child status= %d\n",WEXITSTATUS(status));
while(1){
printf("cnt=%d\n",cnt);
printf("this is father print,pid=%d\n",getpid());
sleep(1);
}
}
else if(pid==0){
while(1){
printf("this is chilid print ,pid=%d\n",getpid());
sleep(1);
cnt++;
if(cnt==5){
exit(3);
}
}
}
return 0;
}
waitpid的用法
wait使调用者阻塞,waitpid第三个参数,可以使调用者不阻塞。注意:此时的子进程是僵尸进程。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt=0;
int status=10;
pid=fork();
if(pid>0)
{
// wait(&status);
waitpid(pid,&status,WNOHANG);
printf("child quit,child status= %d\n",WEXITSTATUS(status));
while(1){
printf("cnt=%d\n",cnt);
printf("this is father print,pid=%d\n",getpid());
sleep(1);
}
}
else if(pid==0){
while(1){
printf("this is chilid print ,pid=%d\n",getpid());
sleep(1);
cnt++;
if(cnt==5){
exit(3);
}
}
}
return 0;
}
孤儿进程
父进程如果不等带子进程退出,在 子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程。Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt=0;
int status=10;
pid=fork();
if(pid>0)
{
printf("this is father print,pid=%d\n",getpid());
}
else if(pid==0){
while(1){
printf("this is chilid print ,pid=%d,my father pid=%d\n",getpid(),getppid());
sleep(1);
cnt++;
if(cnt==5){
exit(3);
}
}
}
return 0;
}