进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区

fork函数讲解

  • 进程复制
    • fork基本使用
    • 简单分页 逻辑页 物理页 页表
    • fork的三个面试练习题
    • 缓冲区
    • 僵死进程
    • 孤儿进程
    • 写时拷贝
  • 进程替换
    • exexc 介绍
    • 简易命令解释器
      • strtok()函数讲解

进程复制

fork基本使用

进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第1张图片

父进程fork后,新的进程产生,新的进程就继续从fork往后的程序往下执行,新的进程不会继续执行fork,父进程中fork复制了该进程,然后fork的返回值是子进程的pid ,子进程中fork的返回值被强制为0,所以可以通过fork的返回值来判断

#include
#include
int main(){
	int n=0;
	char*s=NULL;
	pid_t id=fork();
	if(id==-1){
		printf("fork error\n");
		exit(1);
	}

	if(id==0){
		n=3;
		s="child";
	}
	else{
		n=7;
		s="parent";
	}
	for(int i=0;i<n;i++){
		printf("s=%s,pid=%d\n",s,getpid());
		sleep(1);
	}
	exit(0);
}   
            

进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第2张图片

for(int i=0;i<n;i++){
	printf("s=%s,pid=%d,ppid=%d,n=%d,&n=%p\n",s,getpid(),getppid(),n,&n);
	sleep(1);
}

进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第3张图片
解释一下,为啥在父进程和子进程中n的地址是相同的:fork一份产生子进程,n的位置,即距离起始位置的偏移量恰好和父进程相同,在父进程中n的那块空间存储7,子进程中那块空间存储3,两者互不影响。6

简单分页 逻辑页 物理页 页表

4K为一个页面
程序中看到的都是逻辑地址,实际上是偏移量,调试看到的都是逻辑地址,我们不知道物理地址,每次是映射到物理地址,一般是查表
进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第4张图片
内核用1G,

进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第5张图片

fork的三个面试练习题

#include
#include
#include
#include
int main(){
	fork()||fork();
	printf( "1\n");
}

第一个fork执行后,赋值一个进程,在第一个进程中,fork返回值大于0,在或||表达式中,第一个为真,后面就不执行了,直接返回真,子进程中,第一个fork返回值为0,在或||表达式中,第一个为假,后面继续执行,执行fork,这里没有写if语句,往后面执行就行,最终打印111
进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第6张图片
进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第7张图片

 int main()
 {
	for(int i = 0 ; i < 2; i++ )
	{
		fork();
		printf("a\n");
	}
	exit(0);
}

结果为aaaaaa
进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第8张图片
这个和上面的一个区别就是少了一个\n ,

 int main()
 {
	for(int i = 0 ; i < 2; i++ )
	{
		fork();
		printf("a");
	}
	exit(0);
}

缓冲区

缓冲区(可以想象成一个字符数组),使程序的开销变小,缓冲区满足三个条件,可以提交
1缓冲区满了,
2或者用户强制刷新(用’\n’),
3程序结束

exit(0);退出进程,
1刷缓冲区,2调用_exit(0)退出进程

fflush(stdout);//stdin stdout stderr
该函数可以和\n达到同样的效果,\n可以刷新屏幕,但是文件不行,文件刷新就只能用fflush(stdout)

fork复制进程的时候,连缓冲区也要复制一份,所以a是8个
进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区_第9张图片

僵死进程

子进程先结束,但是父进程没有调用wait获取子进程的退出码,会导致子进程的进程控制块结构体一直在占用id
处理僵死进程
调用wait,把父进程阻塞住了,得到子进程的退出码为3
让父进程死掉,变成孤儿进程,让其他进程接管子进程,

#include
#include
#include
#include
#include
int main(){
	int n=0;
	char*s=NULL;
	pid_t id=fork();
	if(id==-1){
		printf("fork error\n");
		exit(1);
	}

	if(id==0){
		n=3;
		s="child";
	}
	else{
		n=7;
		s="parent";
		int val=0;
		pid_t child_pid=wait(&val);
		//printf("child_pid=%d,val=%d\n",child_pid,val>>8);
		if(WIFEXITED(val)){//是否正常结束
			printf("child_pid=%d,val=%d\n",child_pid,WEXITSTATUS(val));//去除退出码
	}
	for(int i=0;i<n;i++){
		printf("s=%s,pid=%d\n",s,getpid());
		sleep(1);
	}
	exit(3);
}   

孤儿进程

子进程的父进程结束,该进程就变成了孤儿进程,系统会在给该进程找一个父进程,会自动获取该子进程的退出码

写时拷贝

fork时,先不着急复制,先共享,等到需要修改该页面数据的时候,再复制该页面一份,延迟拷贝

进程替换

exexc 介绍

每一个新的进程产生都是,先复制,再替换
把当前进程替换成另一个进程

没有返回值,替换成功,如果有返回值,说明替换失败,

int execl(const char* path, const char * arg,(char*)0...);

*path:新替换的程序的路径名称
*arg :传给新程序主函数的第一个参数,一般为程序的名字
*arg 后面是剩余参数列表,参数个数可变,必须以空指针作为最后一个参数

int execlp(const char* file, const char * arg,...);

会在bin路径下寻找,能找到就能启动,找不到就失败

简易命令解释器

strtok()函数讲解

char*strtok(char*str,const char*delim);

str是原本的字符串,这里不能是常量,因为会修改,实现机理是将每个分隔符换成’\0’,返回每个字符串的首地址,delim是分隔符,是个常量
strtok里面有个静态偏移量指针,第二次调用时,将NULL传入,就可以通过静态偏移量指针找到目前的位置,继续分隔,当到末尾时,s就返回为空

获取用户的id,返回值为0就是管理员

uid_t getuid(void)

通过uid我们可以获取用户的很多详细信息,都在passwd的结构体里面,

struct passwd*ptr=getpwuid(uid);

用ptr指向这个结构体,可以访问结构体所有的属性,

你可能感兴趣的:(Linux,linux,android,运维)