[linux实验] fork详解 孤儿进程

pid_t fork(void)
#include <unistd.h>
fork()会产生一个新的子进程。其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码、组代码、环境变量、已打开的文件代码、工作目录和资源限制等。由于这些继承的信息是复制而
来,并非指相同的内存空间,因此子进程对这些变量的修改和父进程并不会同步。此 外,子进程不会继承父进程的文件锁定和未处理的信号。
注意,Linux 不保证子进程会比父进程先执行或晚执行,因此编写程序时要留意死锁或竞争条件的发生。
返回值,如果 fork()调用成功则在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中则返回 0。如果 fork() 失败则直接返回-1,失败原因存于 errno 中。

fork函数详解
有以下代码

#include <stdio.h>
#include <unistd.h>
int main(){
	int x=100, p;
	printf("x=%d\n", x);
	p=fork();
	if (p){
		x=200;
		printf("parent x=%d\n", x);
		printf("this is parent, pid=%d\n", getpid());
		//sleep(1);
	}else{
		x=300;
		printf("child x=%d\n", x);
		printf("this is child, pid=%d\n", getpid());
		printf("my parent pid is %d\n", getppid());
	}
	return 0;
}

gcc -S test.c得到汇编代码
可以看到fork 的call指令后面有一个mov指令
在这里插入图片描述
要知道赋值操作是从右向左,也就是对于p=fork(),先调用fork后再把返回值给p
所以子进程和父进程都会有p的赋值操作。但是两个进程中p的值是不一样的,这就有了fork函数的双返回值的效果

fork函数双返回值详解
fork()是父进程委托操作系统创建子进程。
在单cpu、单核、单指令流水(某一时刻只能有一个进程在运行)的情况下,
父进程调用fork时要把自己变为阻塞态,在这之前先保存现场。
进而让OS创建子进程,这时要对子进程初始化运行现场。
把子进程的ax初始化为0,同时会把父进程的ax修改为子进程的pid。
所以在子进程和父进程的mov [ebp-12], ax的时候分别把0和子进程pid送给相应位置。


pid_t getpid(void)
#include<unistd.h>
getpid()用来取得目前进程的进程识别码。
返回值:目前进程的进程识别码。

pid_t getppid(void)
#include<unistd.h>
getppid()用来取得目前进程的父进程识别码。
返回值:目前进程的父进程识别码。


对于以上程序,有以下运行结果
在这里插入图片描述
当把sleep的注释去掉后,运行结果就会变成这样
在这里插入图片描述
原因是当父进程执行完毕后就会被os回收,这时它的子进程还没有执行完。对于这个子进程来说,它的父亲没有了,所以他就变成了孤儿进程,这时就会被系统的孤儿院进程收养,这里收养他的孤儿院进程就是1242.
如果父进程等待到子进程结束之后在被回收,那么在子进程中找它父进程的pid就是当前父进程的pid。


pid_t wait(int *status)
#include <sys/types.h>
#include <sys/wait.h>
wait() 会暂停目前进程的执行,直到有信号来到或子进程结束。如果在调用
wait()时子进程已经结束,则 wait()会立即返回子进程的结束状态值。子进程的结束状 态值会由参数 status 返回,而子进程的进程识别码也会一块返回。如果不在意结束状态 值,则参数 status 也可以设置成 NULL。该函数若执行成功,则返回子进程识别码
(pid),否则返回-1,失败原因存于 errno 中。

当一个进程结束时,会产生一个终止状态字,然后内核发出一个SIGCHLD信号通知父进程。
wait最常用的作用是让一个进程等待另一个进程,最常见的是父进程等待自己的子进程

pid_t waitpid(pid_t pid, int *status, int options);
#include <sys/types.h>
#include <sys/wait.h>

pid>0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

参数status如果不是一个空指针,则终止进程的终止状态就存放在status所指向的单元。
参数status如果是一个空指针,则表示父进程不关心子进程的终止状态

option
WNOHANG 若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
WUNTRACED 返回终止子进程信息和因信号停止的子进程信息
WCONTINUED 返回收到SIGCONT信号而恢复执行的已停止子进程状态信息

返回值:
成功 成功结束运行的子进程的进程号
失败 返回-1
WNOHANG 没有子进程退出返回0


编写程序
利用 fork()产生两个子进程,首先显示一下两个子进程及父进程的进程标识符;然后让父进程显示 1-26 个数字,子进程 1 显示 26 个大写字母,子进程 2 显示 26 个小写字母。让大小写字母及数字是夹杂交错输出的。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){
	pid_t pid, pid1;
    pid = fork();
    if (pid<0){
        perror("fork failed\n");
        exit(-1);
    }else if (pid==0){	//child_1
    	printf("child1 pid is %d\n", getpid());
		int i;
		for (i=0; i<26; i++){
			sleep(1);
			printf("%c\n", 'A'+i);
		}
	}else{	//parent (pid>0)
		pid1=fork();
		if (pid1<0){
			perror("fork failed\n");
			exit(-1);
		}else if (pid1==0){	//child_2
			printf("child2 pid is %d\n", getpid());
			int i;
			for (i=0; i<26; i++){
				sleep(1);
				printf("%c\n", 'a'+i);
			}
		}else{	//parent pid>0 && pid1>0
			printf("parent pid is %d\n", getpid());
			int i;
			for (i=1; i<=26; i++){
				sleep(1);
				printf("%d\n", i);
			}
		}	
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42172261/article/details/106106813