进程创建
1.进程号获取
pid_t getpid(void); 返回当前进程的进程号
pid_t getppid(void); 返回当前的进程的父进程
2.fork()函数创建子进程
fork:目的是在当前进程下创建一个子进程.
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次. 它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程.在子进程中fork函数返回0,在父进程中,fork返回新创建子进程的进程ID大于0。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
0 表示的是子进程的进程号,那么这个fork是父进程执行的结果
0 表示的是,这是由子进程执行的结果
-1 父进程创建子进程失败,此时不会有子进程创建
2.1 父进程和子进程执行顺序?是否会阻塞?
执行的顺序是随机的,不会阻塞
2.2 进程数据共享么?
父进程再调用fork之后,会把父进程的进程空间(fork之前的东西变量什么的)拷贝到子进程中,但fork之后父子进程创建的变量,如下面代码中的变量d,地址就不会是一样的.由于这种拷贝是完全拷贝,所以父子进程之间是不共享的。意思就是,虽然地址一样,但是各自修改,都不会影响另一方,大家地址空间独立的,即便地址一样又能怎样.
fork之前的变量,加不加static,无所谓,只是换了个进程空间的data段区域,还是会拷贝.
附注:查阅资料,后来的操作系统优化,通过写时复制技术,在子进程需要复制资源(比如子进程执行写入动作更改父进程内存空间)时才复制,否则创建子进程时先不复制。
2.3 子进程何时执行?
子进程是在调用fork函数之后才存在的,在fork之前的语句只有父进程执行,父进程fork之后,产生子进程,fork父子进程同时执行。
2.4 子进程{}之后的代码是谁的?
之后的代码是父子进程都会执行的。
fork之后筛选之前的语句,也是父子进程共同拥有的.
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int c = 3;
int main(int argc, char const *argv[])
{
int a = 1;
static int b = 2;
printf("before fork:a = %d\n", a);
printf("**********************************\n");
pid_t pid = fork(); //创建了一个子进程
int d = 5;
printf("pid号=%d\n",pid); //运行结果会是两个: //我pid=4392 //我pid=0
printf("fork_pid = %d, pid=%d, ppid=%d,hello world\n", pid, getpid(), getppid());
printf("**********************************\n");
//根据fork的返回值,进行父子进程筛选
if(pid < 0) {
perror("fork");
return -1;
} else if(pid > 0) { //父进程
sleep(5);
a = 11;
//static int d = 40;
printf("父进程:pid = %d, ppid = %d\n", getpid(), getppid());
printf("父进程:a = %d, b = %d, c = %d \n", a, b, c);
printf("d = %d,地址=%p\n", d,&d);
printf("a 地址 %p\n",&a );//fork之后,abc变量本身的地址都是一样的.
}else if(0 == pid) { //子进程
//sleep(1);
a = 10;
b = 20;
c = 30;
d = 50;
int d = 43;
//static int d = 400;
printf("子进程:pid = %d, ppid = %d\n", getpid(), getppid());
printf("子进程:a = %d, b = %d, c = %d\n", a, b, c);
printf("d = %d,地址=%p\n", d,&d);
printf("a 地址 %p\n",&a );fork之后,abc变量本身的地址都是一样的.
}
printf("hello world pid = %d\n", getpid());
printf("\n");
return 0;
}
执行结果:
3. vfork()函数创建子进程
vfork函数并不会向fork那样把父进程的地址空间拷贝到子进程
3.1 父子进程执行顺序?
vfork产生子进程,子进程就在父进程的地址空间运行,所以子进程会阻塞父进程.看清这个所以的关系,因为子进程在父进程地址空间上运行,所以肯定会阻塞父进程.为什么就所以的那么理所应当,因为如果同时在同一块地方上撒欢,那不就乱套了吗,这就是所谓的并发.
3.2 父子进程何时执行?
子进程运行在vfork之后.
exit之后父进程才可能被调度运行。
附注:查阅资料,在fork中子进程与父进程的执行顺序不确定,而在vfork中,子进程先运行,父进程挂起,直到子进程调用了exec或者exit函数,父进程才被执行。
3.3 子进程{}之后的代码是谁的?
如果子进程在{}已经退出,之后的代码就属于父进程的.
3.4 进程数据共享么?
由于子进程是在父进程的地址空间运行,那么数据会共享.但是对于非静态(自动)的局部变量,在vfork之前可以共享,在vfork之后不能共享(因为子进程结束后,会释放栈区空间,即便在子进程修改了vfork之后的自动变量,子进程结束时也会清掉,父进程执行时候,又重新压一遍vfork之后的自动变量).子进程和父进程共享数据段,全局变量和静态的局部变量是完全共享的,静态局部变量在编译的时候就已经存在了(这里要记住static作用有三,修饰函数,全局变量,局部变量).
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
int a = 1;
pid_t pid = vfork();
int b = 5;//data
static int c = 6;
if(pid < 0) {
perror("vfork error");
exit(-1);
}else if(0 == pid) { //子进程
sleep(1);
a = 10;
b = 50;
c = 80; ///int b = 20;
printf("子进程:a = %d b = %d c = %d\n", a,b,c);
exit(0); //千万不要忘,fork也要养成该习惯
}else if(pid > 0) { //父进程
printf("父进程:a = %d b = %d c = %d\n", a,b,c);//父进程不能访问b,作用域
}
printf("hello world pid = %d\n", getpid());
return 0;
}
运行结果:
子进程:a = 10 b = 50 c = 80
父进程:a = 10 b = 5 c = 80
hello world pid = 7358
如果在vfork中,子进程没有exit()退出,就会显示以下结果的错误:
-
子进程有exit时的运行结果:
子进程:a = 10 c = 50 d = 80
父进程:a = 10 c = 5 d = 80
hello world pid = 3899 -
无exit时的运行结果:
子进程:a = 10 c = 50 d = 80
hello world pid = 3908
父进程:a = 32765 c = 5 d = 80
hello world pid = 3907
a.out: cxa_atexit.c💯 __new_exitfn: Assertion `l != ((void *)0)’ failed.
已放弃 (核心已转储)
一定要谨记住: vfork子进程退出一定要exit!