getpid():获得子进程的pid
getppid():获得父进程的pid
执行可执行程序的时候,就会创建一个进程
一、创建子进程:
1、 fork函数:用于创建一个子进程(当前进程的子进程) ——<unistd.h>
pid_t fork(void)
一次fork调用:由一个进程变成两个进程------->两个进程各自对fork返回
返回2个值: 1)返回子进程的pid(>0) (父进程的返回)
2)返回0 (子进程的返回)
注:C语言中,一个函数返回多个值,需用到输出参数
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
pid_t pid;
char *message;
int n;
pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
if(pid == 0){
message = "child process!!!";
n = 6;
}else{
message = "parent process!!!";
n = 3;
}
for(; n > 0; n--){
printf("%s\n", message);
sleep(1);
}
return 0;
}
shell抢占:
注:shell也是一个进程,他也会抢占资源。
(1)pid是一直往上加,直到不能再加再往回找;但是文件描述符是每次都找可用的最小的fd
2、父子进程的执行内容
fork函数前的代码子进程不执行,子进程接着fork函数之后的代码执行
fork函数执行后函数会出现分支,一个进程分两个分支
3、循环创建子进程的架构
注:子进程与父进程权限一样,处于相同的地位去争夺CPU
循环创建5个进程:使用for循环
int main(void)
{
int i;
pid_t pid;
printf("ccccccccccccccccc\n");
for(i = 0; i < 5; i++)
{
pid = fork();
if(pid == -1)
{
perror("fork error");
exit(-1);
}else if(pid == 0){
break; //防止子进程再次创建子进程
}
}
if(i < 5)
{
sleep(i);
printf("%d child process, pid = %d \n", i+1, getpid());
}else{
sleep(i);
printf("parent process ppid = %d \n", getppid());
}
return 0;
}
特别注意:
代码执行分析:父进程和子进程处于相同的地位去争夺CPU,谁抢到CPU谁执行
1)父进程每次返回子进程的pid,因此大于零,每次都不执行循环内容,只是进行i++;循环结束后,执行下方的代码
2)子进程返回值为0,每次进入循环体执行后,break,执行下面的if语句
3)程序中加入的sleep函数为了控制进程执行的顺序,若去掉sleep,则进程结束顺序不定
4)在去掉所有的sleep函数后,在原有的进程集中,又加入shell进程来争夺资源,shell进程在等待./a.out进程结束抢占前台资源
二、获得用户id和组id
uid:
uid_t getuid(void):获得当前进程实际的uid
uid_t geteuid(void):获得当前进程有效的用户id
gid:
gid_t getgid(void):获得当前进程使用的用户组id
gid_t getegid(void):获得当前进程有效的用户组id
三、父子进程共享:原则:读时共享,写时复制
fork函数之前的代码,子进程不执行
fork函数执行之后:
父子进程:
相同之处:(全局变量、.data、.text)、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式
不同之处:进程ID、fork返回值、父进程ID、进程运行时间、定时器、未决信号集
注:父子进程间的全局变量是各有一份,各自独享
(全局变量、.data、.text)、栈、堆:父子进程读时共享同一块物理地址,写时复制
1、似乎,子进程复制了父进程0-3G用户空间内容,以及父进程的PCB,但pid不同。真的每fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份,然后再映射至物理内存吗?
当然不是!父子进程间遵循读时共享写时复制的原则。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销。
2、父子进程的共享内容:1、文件描述符
2、父子进程共享mmap建立的映射区