进程控制
fork函数
函数说明:创建一个子进程。
函数原型:pid_t fork(void);
返回值:失败返回-1;成功返回:① 父进程返回子进程的ID(非负) ②子进程返回 0
pid_t类型表示进程ID,但为了表示-1,它是有符号整型。(0不是有效进程ID,init最小,为1)
注意返回值,不是fork函数能返回两个值,而是fork后,父子进程需【各自】返回一个。
循环创建n个子进程
一次fork函数调用可以创建一个子进程。那么创建N个子进程不能简单的以for(i = 0; i < n; i++) { fork() }来实现,因为产生子函数后,子函数会再次进入循环体调用fork函数,也就是子函数也会再创建自己的子函数,从而形成裂变,最终的子函数个数是(2^n)-1。
正确的创建n个子进程的写法如下:
#include <stdio.h>
#include <unistd.h>
#define CHILDNUM 5
int main(int argc, char **argv)
{
int i = 0;
for (i = 0; i < CHILDNUM; i++) {
/* 如果是子进程,即fork返回值是0,则直接退出循环,
* 避免裂变 */
if (fork() == 0)
break;
}
if (i == CHILDNUM) {
/* 父进程会进入此代码块,睡眠CHILDNUM秒,保证最后一个打印信息 */
sleep(CHILDNUM);
printf("I'm parent, pid = %d\n", getpid());
} else {
/* 子进程会进入此代码块,睡眠i秒再打印信息 */
sleep(i);
printf("I'm %dth child, pid = %d\n", i + 1, getpid());
}
return 0;
}
getpid函数
获取当前进程ID
pid_t getpid(void);
getppid函数
获取当前进程的父进程ID
pid_t getppid(void);
getuid函数
获取当前进程实际用户ID
uid_t getuid(void);
获取当前进程有效用户ID
uid_t geteuid(void);
getgid函数
获取当前进程使用用户组ID
gid_t getgid(void);
获取当前进程有效用户组ID
gid_t getegid(void);
进程共享
父子进程之间在fork后。有哪些相同,那些相异之处呢?
刚fork之后:
父子相同处: 全局变量、.data、.text、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式...
父子不同处: 1.进程ID 2.fork返回值 3.父进程ID 4.进程运行时间 5.闹钟(定时器) 6.未决信号集
似乎,子进程复制了父进程0-3G用户空间内容,以及父进程的PCB,但pid不同。真的每fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份,然后在映射至物理内存吗?
当然不是!父子进程间遵循读时共享写时复制的原则。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int var = 10;
int main()
{
pid_t pid = 0;
pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if (pid > 0) {
/* 父进程 */
sleep(1);
var = 20;
printf("I'm parent, pid = %d, parentID = %d, var = %d\n", getpid(), getppid(), var);
} else if (pid == 0) {
/* 子进程 */
var = 30;
printf("I'm child, pid = %d, parentID = %d, var = %d\n", getpid(), getppid(), var);
}
printf("var = %d\n", var);
return 0;
}
重点注意!躲避父子进程共享全局变量的知识误区!
父子进程共享:1. 文件描述符(打开文件的结构体) 2. mmap建立的映射区
特别的,fork之后父进程先执行还是子进程先执行不确定。取决于内核所使用的调度算法。
---------------
我是良许,世界500强外企 Linux 开发工程师,专业生产 Linux 干货。欢迎关注我的公众号「良许Linux」,回复「1024」获取最新最全的技术资料,回复「入群」进入高手如云技术交流群。