Linux进程的理解:
1. 父子进程的关系
独立的两个进程
互为父子关系
2. 问题:
2.1. 父进程先结束?
子进程就依托根进程init:孤儿进程
孤儿进程没有任何问题(没有任何危害)
2.2. 子进程先结束?
子进程会成为僵尸进程。
僵尸进程不占用内存、cpu.但在进程任务树上的节点
僵尸进程会造成一个进程名额的浪费,所要处理僵尸进程
3. 僵尸进程使用wait回收
wait函数:pid_t wait(int * status);
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL。
僵尸进程回收例子:
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
if(fork()==0)
{
printf(“子进程:%d\n”,getpid());
sleep(5);
printf(“子进程退出\n”);
return 88;
}
else
{
printf(“父进程:%d\n”,getpid());
int status;
int pid=wait(&status);
printf(“父进程收回子进程%d,子进程返回码:%d”,pid,WEXITSTATUS(status));
sleep(20);
return 0;
}
}
4. 父进程怎么知道子进程退出?
子进程结束通常会向父进程发送一个信号:SIGCHLD
5. 父进程处理子进程退出信号
signal(intsig,void(*fun)(int));
向系统注册:只要sig信号发生,系统停止进程,并调用函数fun
当函数执行完毕,继续原来进程
5.1.实现处理函数
5.2.使用signal邦定信号与函数
僵尸进程回收例子:
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
void deal(int s)
{
int status;
int pid=wait(&status);
printf(“父进程收回子进程%d,子进程返回码:%d”,pid,WEXITSTATUS(status));
}
int main()
{
if(fork()==0)
{
printf(“子进程:%d\n”,getpid());
sleep(5);
printf(“子进程退出\n”);
return 88;
}
else
{
printf(“父进程:%d\n”,getpid());
signal(SIGCHLD,deal);
while(1)
{
sleep(1);
}
return 0;
}
}
注:sleep函数,当有信号传过来时,sleep不会堵塞,会跳到sleep函数的下一条语句执行。
6. 父子进程的资源访问
6.1.内存资源
6.2.文件资源
例子:
#include<stdio.h>
#include<unistd.h>
void main()
{
inta=20;
if(fork())
{
printf(“父进程:%d\n”,a);
}
else
{
printf(“子进程:%d\n”,a);
}
}
运行结构:
子进程:20
父进程:20
例子说明:子进程克隆了父进程的整个内存区域
列子2:
#include<stdio.h>
#include<unistd.h>
void main()
{
inta=20;
if(fork())
{
printf(“父进程:%d\n”,a);
a=99;
}
else
{
printf(“子进程:%d\n”,a);
sleep(3);
printf(“子进程:%d\n”,a);
}
}
运行结果:
子进程:20
父进程:20
子进程:20
列子说明:但内存区域指向不同的物理空间尽管克隆,但内存独立. 不能相互访问
例子3:
#include<stdio.h>
#include<unistd.h>
#include<sys/mman.h>
void main()
{
int*a=mmap(0,4,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,0,0);
if(fork())
{
*a=20;
printf(“父进程:%d\n”,a);
*a=99;
}
else
{
printf(“子进程:%d\n”,a);
sleep(3);
printf(“子进程:%d\n”,a);
}
}
运行结果:
子进程:20
父进程:20
子进程:99
但,当把MAP_SHARED改成MAP_PRIVATE时:
运行结果:
子进程:20
父进程:20
子进程:20
例子说明:
映射内存:
MAP_SHARED:映射到同一物理内存
MAP_PRIVATE:映射到不同的物理内存.
实现父子进程的数据交换可以使用内存映射的方式
使用sbrk方式是可以映射到同一块物理内存的
例子3:
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
void main()
{
int fd=open(“text.txt”,O_RDWR);
if(fork())
{
char buf[1024]={0};
lseek(fd,0,SEEK_SET);
read(fd,buf,1024);
printf(“父进程:%s”,buf);
close(fd);
}
else
{
char buf[1024]={0};
read(fd,buf,1024);
printf(“父进程:%s”,buf);
close(fd);
}
}
运行结构:子进程和父进程都能读取到文件的内容,父进程中用lseek是因为程序先执行子进程,子进程读取完后位置指针指向文件的末尾,影响了父进程,父进程不能读取到内容。
列子说明:两个进程之间,文件描述符号指向的是同一个文件内核对象.
注:文件描述符要关闭两次,子进程克隆的文件描述符也得关闭