#include
#include
#include
#include
#include
int main()
{
char *s=NULL;
int n=0;//控制父子进程的次数
//调用fork
pid_t id=fork();
assert(id != -1);//fork的返回值,-1表示出错,所以加断言
if(id == 0)//子进程
{
s="child";
n=3;
}
else//父进程
{
s="parent";
n=7;;
}
//父子进程一起执行
int i=0;
for(;i<n;i++)
{
//n的地址=%d --- 会警告,但能运行 --- 因为将地址转成整形 可用%p,输出地址
//注意,在进程中,看到的是进程的逻辑地址,所以父进程和子进程地址显示一样
printf("s=%s,pid=%d,ppid=%d,n的地址=%d\n",s,getpid(),getppid(),&n);
sleep(1);
}
exit(0);
}
示例运行:
#include
#include
#include
#include
#include
int main()
{
char *s=NULL;
int n=0;//控制父子进程的次数
//调用fork
pid_t id=fork();
assert(id != -1);//fork的返回值,-1表示出错,所以加断言
if(id == 0)//子进程
{
s="child";
n=3;
}
else//父进程
{
s="parent";
n=7;;
}
//父子进程一起执行
int i=0;
for(;i<n;i++)
{
//n的地址=%d --- 会警告,但能运行 --- 因为将地址转成整形 可用%p,输出地址
//注意,在进程中,看到的是进程的逻辑地址,所以父进程和子进程地址显示一样
printf("s=%s,pid=%d,ppid=%d,n的地址=%d\n,n的值=%d",s,getpid(),getppid(),&n,n);
sleep(1);
}
exit(0);
}
32位系统下的寻址地址空间为2^32 = 4,294,967,296 ~~== 4G;
用户真正能使用的是内核下几个区域,大概为3G,内核占1G;
4G/4k — 物理能有 2^20 个页面,前提是32位系统;
#include
#include
#include
int main()
{
int i=0;
for(;i<2;i++)
{
fork();
printf("a\n");
}
exit(0);
}
运行结果为6个:
运行结果不一定完全一样,但都是输出六个a;
最终生成4个进程;
#include
#include
#include
int main()
{
printf("a\n");
fork();
exit(0);
}
#include
#include
#include
int main()
{
printf("a");
fork();
exit(0);
}
运行结果:
#include
#include
#include
int main()
{
int i=0;
for(;i<2;i++)
{
fork();
printf("a");
}
exit(0);
}
因为没有\n,所以第一次fork(),打印一个a,但没有打印出来,在缓冲区里;
所以后面fork(),会将缓冲区内本来就有的a保留。
#include
#include
#include
int main()
{
fork() || fork();
printf("a\n");
exit(0);
}
运行结果为3个;
#include
#include
#include
int main()
{
fork() && fork();
printf("a\n");
exit(0);
}
运行结果为3个:
1、进程实体已经被释放;
2、PID内部的exit_code = 0; //进程的退出码,将PID内部的退出码置零;
3、父进程获取退出码;
当子进程先于父进程结束,父进程没有获取子进程的退出码,此时子进程变成僵死进程
;
#include
#include
#include
#include
#include
int main()
{
char *s=NULL;
int n=0;//控制父子进程的次数
//调用fork
pid_t id=fork();
assert(id != -1);//fork的返回值,-1表示出错,所以加断言
if(id == 0)//子进程
{
s="child";
n=3;
}
else//父进程
{
s="parent";
n=7;
}
//父子进程一起执行
int i=0;
for(;i<n;i++)
{
printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
sleep(1);
}
exit(0);
}
测试运行:
./main & — //main为进程名字,'&'表示将进程放在后台运行;
要将进程放在后台运行;这样才能运行ps查看;否则ps不能在进程运行的时候同时运行查看。
ps — 默认显示与当前终端有关的进程信息;
ps -e — 显示系统中所有的进程信息;
ps -f — 显示更多的进程信息;
(子进程结束而父进程未结束的时候才有僵死进程)
比如子进程的n改为7,父进程的n改为3;
#include
#include
#include
#include
#include
int main()
{
char *s=NULL;
int n=0;//控制父子进程的次数
//调用fork
pid_t id=fork();
assert(id != -1);//fork的返回值,-1表示出错,所以加断言
if(id == 0)//子进程
{
s="child";
n=7;
}
else//父进程
{
s="parent";
n=3;
}
//父子进程一起执行
int i=0;
for(;i<n;i++)
{
printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
sleep(1);
}
exit(0);
}
注意: 子进程在父进程结束后,子进程的PPID发生改变。有些系统父进程结束后,子进程的ppid为1,这里为1460;当父进程结束后,子进程会变成“孤儿进程”,会被“收养”;
父进程先于子进程结束,子进程就会被收养,新的"父进程"就会获取退出码;(调用wait实现);
使用 wait:
man wait
pid_t wait(int *wstatus); //返回值是所获取的子进程的pid,参数是将获取的退出码写入这个整型变量中;
else//父进程
{
s="parent";
n=7;
int val;
wait(&val);
printf("val=%d\n",val);
}
#include
#include
#include
#include
#include
#include //wait的头文件
int main()
{
char *s=NULL;
int n=0;//控制父子进程的次数
//调用fork
pid_t id=fork();
assert(id != -1);//fork的返回值,-1表示出错,所以加断言
if(id == 0)//子进程
{
s="child";
n=3;
}
/*else//父进程
{
s="parent";
n=7;
}*/
else//父进程
{
s="parent";
n=7;
int val;
wait(&val);
printf("val=%d\n",val);
}
//父子进程一起执行
int i=0;
for(;i<n;i++)
{
printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
sleep(1);
}
exit(0);
}
运行结果:
没有僵死进程
注意:这个先运行子进程,子进程运行完后才运行父进程;
wait需要获取子程序的退出码,只有当子程序运行结束,才能获取退出码,否则会阻塞,所以先运行子进程;
总结:
其实两种处理僵死进程的方法本质都是一样的,都调用了wait,获取退出码。
但是两种方法又有区别,就是父进程调用wait会阻塞,等子进程执行完之后,父进程才会执行。
一般我们父进程调用wait是配合信号使用的。目前暂时理解为说明父进程调用wait可以避免僵死进程。