文章目录
- 回顾进程
- 查看进程的方式
- fork
回顾进程
前面对进程进行了一些初步的认知,比如进程=可执行程序+内核数据结构PCB,再比如可以通过ps
命令搭配管道寻找进程,通过/proc
系统文件夹查看进程的信息,这些都是前面对进程建立起来的一些初步的认知
查看进程的方式
下面引入在函数中使用进程的函数,getpid()
和gitppid
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("process id:%d,parent process id:%d\n",getpid(),getppid());
sleep(1);
}
return 0;
}
这两个函数的功能,就是得到进程的id
和父进程的id
实现Makefile
cc=gcc
src=process.c
target=process
$(target):$(src)
gcc -o $@ $^
.PHONY:clean
clean:
rm -f $(target)
在启动程序前,为了便于观察进程的变化情况,我们可以借助ps
命令来观察,但是ps
命令不能做到实时观察目前我们的进程变换情况,因此使用一个脚本来自动的观察进程的情况
# 打印ps指令的题头和利用ps指令寻找关于process的进程信息,打印一次暂停1s,再使用####进行分割每次打印的信息
while :; do ps ajx |head -1 && ps ajx | grep process; sleep 1;echo "###################################"; done
执行程序和脚本
下面对这个程序进行多次执行
# 第一次可执行程序运行的结果
process id:11389,parent process id:7945
# 进程监视的结果
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
7945 11389 11389 7945 pts/0 11389 S+ 1003 0:00 ./process
# 第二次可执行程序运行的结果
process id:14823,parent process id:7945
# 进程监视的结果
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
7945 14823 14823 7945 pts/0 14823 S+ 1003 0:00 ./process
# 第三次可执行程序运行的结果
process id:15279,parent process id:7945
# 进程监视的结果
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
7945 15279 15279 7945 pts/0 15279 S+ 1003 0:00 ./process
输出结果如上,从中可以看出这个函数是可以打印出进程的信息的,和实际的信息也确实对应,但是有一个现象,父进程一直都是7945
,为什么不变呢?
理解系统接口调用
从这里其实可以更深层次的理解下图中关于系统调用接口的概念,我们使用的不管是getpid
还是getppid
,实际上都是要调用系统的接口,操作系统再帮我们进行一个搜索后把结果返还给用户,逻辑上是很简单的一个过程
那么现在有了函数的调用,用户也可以直接去得到进程的id
信息,下面进行第二种查看进程的方式,使用/proc
文件来查看
proc
文件夹,是一个动态的目录结构,里面存放的是所有存在的进程,而目录的名称就是以这个进程的id
来命名的
下面展示了/proc
文件夹下的各种进程内容,每一个文件夹其实都对应了一个进程,每一个进程文件夹下的内容其实就是每个进程的PCB,那么下面就要进入进程内,看这些进程内到底包含什么东西
[test@VM-16-11-centos process]$ ls /proc -l
total 0
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1
dr-xr-xr-x 9 root root 0 Sep 11 17:49 10
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1005
dr-xr-xr-x 9 test test 0 Sep 23 19:42 10082
dr-xr-xr-x 9 root root 0 Sep 11 17:49 106
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1070
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1072
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1075
dr-xr-xr-x 9 root root 0 Sep 23 19:45 10935
dr-xr-xr-x 9 root root 0 Sep 11 17:49 11
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1180
dr-xr-xr-x 9 root root 0 Sep 11 17:49 1188
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1195
dr-xr-xr-x 9 root root 0 Sep 11 17:49 12
dr-xr-xr-x 9 root root 0 Sep 11 17:49 1205
dr-xr-xr-x 9 root root 0 Sep 11 17:49 1206
dr-xr-xr-x 9 root root 0 Sep 11 17:49 13
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1318
dr-xr-xr-x 9 postfix postfix 0 Jul 15 22:07 1328
dr-xr-xr-x 9 root root 0 Jul 15 22:07 1370
dr-xr-xr-x 9 root root 0 Sep 11 17:49 14
dr-xr-xr-x 9 postfix postfix 0 Sep 23 19:55 14576
dr-xr-xr-x 9 root root 0 Sep 11 17:53 14808
dr-xr-xr-x 9 root root 0 Sep 11 17:53 14911
dr-xr-xr-x 9 root root 0 Aug 30 10:08 15264
dr-xr-xr-x 9 root root 0 Sep 11 17:49 1565
dr-xr-xr-x 9 root root 0 Sep 11 17:49 16
dr-xr-xr-x 9 root root 0 Sep 23 20:03 17216
dr-xr-xr-x 9 root root 0 Sep 23 20:05 17758
dr-xr-xr-x 9 root root 0 Sep 11 17:49 18
dr-xr-xr-x 9 ntp ntp 0 Jul 15 22:07 1800
dr-xr-xr-x 9 test test 0 Sep 23 20:09 18814
dr-xr-xr-x 9 root root 0 Sep 11 17:49 19
dr-xr-xr-x 9 root root 0 Sep 23 20:10 19001
dr-xr-xr-x 9 root root 0 Sep 23 20:12 19441
dr-xr-xr-x 9 root root 0 Sep 11 17:49 2
dr-xr-xr-x 9 root root 0 Sep 11 17:49 20
dr-xr-xr-x 9 root root 0 Aug 31 20:02 20043
dr-xr-xr-x 9 root root 0 Aug 31 20:02 20051
dr-xr-xr-x 9 root root 0 Aug 31 20:02 20052
dr-xr-xr-x 9 root root 0 Sep 23 20:15 20184
dr-xr-xr-x 9 test test 0 Sep 23 20:15 20221
dr-xr-xr-x 9 root root 0 Sep 11 17:49 203
dr-xr-xr-x 9 test test 0 Sep 12 12:11 20530
dr-xr-xr-x 9 test test 0 Sep 12 12:11 20543
dr-xr-xr-x 9 root root 0 Sep 11 17:49 21
dr-xr-xr-x 9 root root 0 Sep 11 17:49 22
dr-xr-xr-x 9 root root 0 Sep 11 17:49 23
dr-xr-xr-x 9 root root 0 Sep 11 17:49 24
dr-xr-xr-x 9 root root 0 Sep 11 17:49 25
dr-xr-xr-x 9 root root 0 Sep 11 17:49 253
dr-xr-xr-x 9 root root 0 Sep 11 17:49 26
dr-xr-xr-x 9 root root 0 Sep 11 17:49 262
dr-xr-xr-x 9 root root 0 Sep 11 17:49 267
dr-xr-xr-x 9 root root 0 Sep 11 17:49 268
dr-xr-xr-x 9 root root 0 Sep 11 17:49 269
dr-xr-xr-x 9 root root 0 Sep 11 17:49 27
dr-xr-xr-x 9 root root 0 Sep 11 17:49 272
dr-xr-xr-x 9 root root 0 Sep 11 17:49 277
dr-xr-xr-x 9 root root 0 Sep 11 17:49 28
dr-xr-xr-x 9 root root 0 Sep 11 17:49 29
dr-xr-xr-x 9 root root 0 Sep 11 17:49 292
dr-xr-xr-x 9 root root 0 Sep 11 17:49 293
dr-xr-xr-x 9 root root 0 Sep 22 18:22 29767
dr-xr-xr-x 9 root root 0 Sep 11 17:49 300
dr-xr-xr-x 9 root root 0 Sep 23 19:15 3061
dr-xr-xr-x 9 root root 0 Sep 21 20:36 3243
dr-xr-xr-x 9 root root 0 Sep 11 17:49 35
dr-xr-xr-x 9 root root 0 Sep 11 17:49 36
dr-xr-xr-x 9 root root 0 Sep 11 17:49 37
dr-xr-xr-x 9 root root 0 Sep 11 17:49 38
dr-xr-xr-x 9 root root 0 Jul 15 22:07 381
dr-xr-xr-x 9 root root 0 Sep 11 17:49 4
dr-xr-xr-x 9 root root 0 Jul 15 22:07 404
dr-xr-xr-x 9 root root 0 Jul 15 22:07 409
dr-xr-xr-x 9 root root 0 Sep 11 17:49 46
dr-xr-xr-x 9 root root 0 Sep 11 17:49 48
dr-xr-xr-x 9 root root 0 Sep 11 17:49 49
dr-xr-xr-x 9 root root 0 Sep 11 17:49 501
dr-xr-xr-x 9 root root 0 Sep 11 17:49 51
dr-xr-xr-x 9 root root 0 Jul 15 22:07 515
dr-xr-xr-x 9 root root 0 Sep 11 17:49 52
dr-xr-xr-x 9 root root 0 Jul 15 22:16 5449
dr-xr-xr-x 9 libstoragemgmt libstoragemgmt 0 Jul 15 22:07 556
dr-xr-xr-x 9 dbus dbus 0 Jul 15 22:07 561
dr-xr-xr-x 9 root root 0 Jul 15 22:07 566
dr-xr-xr-x 9 root root 0 Jul 15 22:07 567
dr-xr-xr-x 9 polkitd polkitd 0 Jul 15 22:07 568
dr-xr-xr-x 9 root root 0 Sep 11 17:49 6
dr-xr-xr-x 9 root root 0 Sep 11 17:49 612
dr-xr-xr-x 9 root root 0 Sep 11 17:49 613
dr-xr-xr-x 9 root root 0 Sep 11 17:49 614
dr-xr-xr-x 9 root root 0 Sep 11 17:49 615
dr-xr-xr-x 9 root root 0 Sep 11 17:49 616
dr-xr-xr-x 9 root root 0 Sep 11 17:49 621
dr-xr-xr-x 9 root root 0 Sep 11 17:49 65
dr-xr-xr-x 9 root root 0 Sep 11 17:49 7
dr-xr-xr-x 9 root root 0 Sep 23 19:34 7937
dr-xr-xr-x 9 test test 0 Sep 23 19:34 7944
dr-xr-xr-x 9 test test 0 Sep 23 19:34 7945
dr-xr-xr-x 9 root root 0 Sep 11 17:49 8
dr-xr-xr-x 9 root root 0 Sep 11 17:49 9
dr-xr-xr-x 2 root root 0 Sep 23 20:15 acpi
-r--r--r-- 1 root root 0 Sep 23 20:15 buddyinfo
dr-xr-xr-x 4 root root 0 Sep 23 20:15 bus
-r--r--r-- 1 root root 0 Sep 23 20:15 cgroups
-r--r--r-- 1 root root 0 Sep 23 20:15 cmdline
-r--r--r-- 1 root root 0 Sep 23 20:15 consoles
-r--r--r-- 1 root root 0 Sep 23 20:15 cpuinfo
-r--r--r-- 1 root root 0 Sep 23 20:15 crypto
-r--r--r-- 1 root root 0 Sep 23 20:15 devices
-r--r--r-- 1 root root 0 Sep 23 20:15 diskstats
-r--r--r-- 1 root root 0 Sep 23 20:15 dma
dr-xr-xr-x 3 root root 0 Sep 23 20:15 driver
-r--r--r-- 1 root root 0 Sep 23 20:15 execdomains
-r--r--r-- 1 root root 0 Sep 23 20:15 fb
-r--r--r-- 1 root root 0 Sep 23 20:15 filesystems
dr-xr-xr-x 5 root root 0 Sep 23 20:15 fs
-r--r--r-- 1 root root 0 Sep 23 20:15 interrupts
-r--r--r-- 1 root root 0 Sep 23 20:15 iomem
-r--r--r-- 1 root root 0 Sep 23 20:15 ioports
dr-xr-xr-x 25 root root 0 Sep 23 20:15 irq
-r--r--r-- 1 root root 0 Sep 23 20:15 kallsyms
-r-------- 1 root root 140737486266368 Sep 23 20:15 kcore
-r--r--r-- 1 root root 0 Sep 23 20:15 keys
-r--r--r-- 1 root root 0 Sep 23 20:15 key-users
-r-------- 1 root root 0 Sep 23 20:15 kmsg
-r-------- 1 root root 0 Sep 23 20:15 kpagecount
-r-------- 1 root root 0 Sep 23 20:15 kpageflags
-r--r--r-- 1 root root 0 Sep 23 20:15 loadavg
-r--r--r-- 1 root root 0 Sep 23 20:15 locks
-r--r--r-- 1 root root 0 Sep 23 20:15 mdstat
-r--r--r-- 1 root root 0 Sep 23 20:15 meminfo
-r--r--r-- 1 root root 0 Sep 23 20:15 misc
-r--r--r-- 1 root root 0 Sep 23 20:15 modules
lrwxrwxrwx 1 root root 11 Sep 23 20:15 mounts -> self/mounts
-rw-r--r-- 1 root root 0 Sep 23 20:15 mtrr
lrwxrwxrwx 1 root root 8 Sep 23 20:15 net -> self/net
-r-------- 1 root root 0 Sep 23 20:15 pagetypeinfo
-r--r--r-- 1 root root 0 Sep 23 20:15 partitions
-r--r--r-- 1 root root 0 Sep 23 20:15 sched_debug
-r--r--r-- 1 root root 0 Sep 23 20:15 schedstat
dr-xr-xr-x 3 root root 0 Sep 23 20:15 scsi
lrwxrwxrwx 1 root root 0 Jul 15 22:07 self -> 20221
-r-------- 1 root root 0 Sep 23 20:15 slabinfo
-r--r--r-- 1 root root 0 Sep 23 20:15 softirqs
-r--r--r-- 1 root root 0 Sep 23 20:15 stat
-r--r--r-- 1 root root 0 Jul 15 22:07 swaps
dr-xr-xr-x 1 root root 0 Jul 15 22:07 sys
--w------- 1 root root 0 Sep 23 20:15 sysrq-trigger
dr-xr-xr-x 2 root root 0 Sep 23 20:15 sysvipc
-r--r--r-- 1 root root 0 Sep 23 20:15 timer_list
-rw-r--r-- 1 root root 0 Sep 23 20:15 timer_stats
dr-xr-xr-x 4 root root 0 Sep 23 20:15 tty
-r--r--r-- 1 root root 0 Sep 23 20:15 uptime
-r--r--r-- 1 root root 0 Sep 23 20:15 version
-r-------- 1 root root 0 Sep 23 20:15 vmallocinfo
-r--r--r-- 1 root root 0 Sep 23 20:15 vmstat
dr-xr-xr-x 2 root root 0 Sep 23 20:15 xpmem
-r--r--r-- 1 root root 0 Sep 23 20:15 zoneinfo
对于产生的结果过多,不太好观察,因此这里将前面的程序启动,看进程中到底有什么文件和信息可以进行获取
# 启动程序
process id:18814,parent process id:7945
# 查看进程内容
[test@VM-16-11-centos process]$ ls /proc/18814 -l
total 0
dr-xr-xr-x 2 test test 0 Sep 23 20:10 attr
-rw-r--r-- 1 test test 0 Sep 23 20:10 autogroup
-r-------- 1 test test 0 Sep 23 20:10 auxv
-r--r--r-- 1 test test 0 Sep 23 20:10 cgroup
--w------- 1 test test 0 Sep 23 20:10 clear_refs
-r--r--r-- 1 test test 0 Sep 23 20:09 cmdline
-rw-r--r-- 1 test test 0 Sep 23 20:10 comm
-rw-r--r-- 1 test test 0 Sep 23 20:10 coredump_filter
-r--r--r-- 1 test test 0 Sep 23 20:10 cpuset
lrwxrwxrwx 1 test test 0 Sep 23 20:09 cwd -> /home/test/process
-r-------- 1 test test 0 Sep 23 20:09 environ
lrwxrwxrwx 1 test test 0 Sep 23 20:09 exe -> /home/test/process/process
dr-x------ 2 test test 0 Sep 23 20:09 fd
dr-x------ 2 test test 0 Sep 23 20:10 fdinfo
-rw-r--r-- 1 test test 0 Sep 23 20:10 gid_map
-r-------- 1 test test 0 Sep 23 20:10 io
-r--r--r-- 1 test test 0 Sep 23 20:10 limits
-rw-r--r-- 1 test test 0 Sep 23 20:10 loginuid
dr-x------ 2 test test 0 Sep 23 20:10 map_files
-r--r--r-- 1 test test 0 Sep 23 20:10 maps
-rw------- 1 test test 0 Sep 23 20:10 mem
-r--r--r-- 1 test test 0 Sep 23 20:10 mountinfo
-r--r--r-- 1 test test 0 Sep 23 20:10 mounts
-r-------- 1 test test 0 Sep 23 20:10 mountstats
dr-xr-xr-x 5 test test 0 Sep 23 20:10 net
dr-x--x--x 2 test test 0 Sep 23 20:10 ns
-r--r--r-- 1 test test 0 Sep 23 20:10 numa_maps
-rw-r--r-- 1 test test 0 Sep 23 20:10 oom_adj
-r--r--r-- 1 test test 0 Sep 23 20:10 oom_score
-rw-r--r-- 1 test test 0 Sep 23 20:10 oom_score_adj
-r--r--r-- 1 test test 0 Sep 23 20:10 pagemap
-r-------- 1 test test 0 Sep 23 20:10 patch_state
-r--r--r-- 1 test test 0 Sep 23 20:10 personality
-rw-r--r-- 1 test test 0 Sep 23 20:10 projid_map
lrwxrwxrwx 1 test test 0 Sep 23 20:09 root -> /
-rw-r--r-- 1 test test 0 Sep 23 20:10 sched
-r--r--r-- 1 test test 0 Sep 23 20:10 schedstat
-r--r--r-- 1 test test 0 Sep 23 20:10 sessionid
-rw-r--r-- 1 test test 0 Sep 23 20:10 setgroups
-r--r--r-- 1 test test 0 Sep 23 20:10 smaps
-r--r--r-- 1 test test 0 Sep 23 20:10 stack
-r--r--r-- 1 test test 0 Sep 23 20:09 stat
-r--r--r-- 1 test test 0 Sep 23 20:10 statm
-r--r--r-- 1 test test 0 Sep 23 20:09 status
-r--r--r-- 1 test test 0 Sep 23 20:10 syscall
dr-xr-xr-x 3 test test 0 Sep 23 20:10 task
-r--r--r-- 1 test test 0 Sep 23 20:10 timers
-rw-r--r-- 1 test test 0 Sep 23 20:10 uid_map
-r--r--r-- 1 test test 0 Sep 23 20:10 wchan
上面演示了查看进程中文件的信息的方法,针对上面的信息,选两个来进行总结
lrwxrwxrwx 1 test test 0 Sep 23 20:09 cwd -> /home/test/process
lrwxrwxrwx 1 test test 0 Sep 23 20:09 exe -> /home/test/process/process
这里挑选出了两个内容,一个是cwd
,一个是exe
,选这两个也是有原因的,这两个可以挖掘出可值得学习的点
cwd
在解决问题前要先知道什么是cwd
,它的全程叫做current working directory
,也就是所谓的当前工作目录,一般叫它叫当前目录,对于当前目录这个概念并不陌生,但什么是当前目录?
前面对于进程的定义中讲到,操作系统会创建出一个PCB
来管理进入内存中的可执行程序,那么可执行程序是如何被管理的?该如何理解管理的概念?一个进程,要能找到自己的可执行程序,这样才能被称为管理起来,因此,这就意味着每一个进程都要有自己的工作目录,而默认情况下,进程启动的时候所处的路径就是当前路径
既然这是默认,那么当前路径也是可以更改的,有一个函数叫做chdir()
函数,它的功能就是用以更变工作路径的,在Linux
下使用man
指令查看:
SYNOPSIS
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);
因此用实验来证明它的存在
对程序略做修改,让程序可以在工作目录下创建文件,可以使用C
语言的文件操作来实现
#include <stdio.h>
#include <unistd.h>
int main()
{
FILE* pf=fopen("test.txt","w");
if(pf==NULL)
{
perror("fopen fail!\n");
return 1;
}
fclose(pf);
return 0;
}
编译运行结果,此时在process
路径下就产生了一个test.txt
文件,这也说明默认的工作路径就是进程启动的时候所处的环境,此时再看proc
文件下的信息
[test@VM-16-11-centos process]$ ls /proc/3466 -l
total 0
lrwxrwxrwx 1 test test 0 Sep 23 21:17 cwd -> /home/test
lrwxrwxrwx 1 test test 0 Sep 23 21:17 exe -> /home/test/process/process
此时确实是被改变了,说明和前面是一样的
[test@VM-16-11-centos process]$ make
gcc -o process process.c
[test@VM-16-11-centos process]$ ./process
[test@VM-16-11-centos process]$ ll
total 20
-rw-rw-r-- 1 test test 106 Sep 23 19:39 Makefile
-rwxrwxr-x 1 test test 8464 Sep 23 20:47 process
-rw-rw-r-- 1 test test 203 Sep 23 20:45 process.c
-rw-rw-r-- 1 test test 0 Sep 23 20:47 test.txt
那么对于程序再略做修改
#include <stdio.h>
#include <unistd.h>
int main()
{
chdir("/home/test");
FILE* pf=fopen("test.txt","w");
if(pf==NULL)
{
perror("fopen fail!\n");
return 1;
}
fclose(pf);
return 0;
}
此时将工作目录改到了家目录的一个文件夹下,此时再看test.txt
的位置
[test@VM-16-11-centos ~]$ pwd
/home/test
[test@VM-16-11-centos ~]$ ll
-rw-rw-r-- 1 test test 0 Sep 23 20:50 test.txt
此时程序生成的文件就默认创建到了这个文件夹内,此时打开/proc
文件,再看进程的描述
exe
exe
表示的就是当前正在执行的可执行程序,如果把可执行程序从磁盘中删除
[test@VM-16-11-centos process]$ ll
total 20
-rw-rw-r-- 1 test test 106 Sep 23 19:39 Makefile
-rwxrwxr-x 1 test test 8520 Sep 23 19:46 process
-rw-rw-r-- 1 test test 187 Sep 23 19:46 process.c
[test@VM-16-11-centos process]$ rm process
[test@VM-16-11-centos process]$ ll
total 8
-rw-rw-r-- 1 test test 106 Sep 23 19:39 Makefile
-rw-rw-r-- 1 test test 187 Sep 23 19:46 process.c
但是程序依旧在运行,这是为什么?
这是因为,磁盘中的文件被删除了,其实和进程关系并不大,进程被运行前就已经被加载到内存中了,因此磁盘上文件的删除对于内存并不会造成决定性的影响,但是如果程序终止想要重新运行,可能就并不可以了,但是在proc
文件内,会为用户标出错误提示,说明在磁盘中的可执行程序已经被删掉了
lrwxrwxrwx 1 test test 0 Sep 23 20:09 cwd -> /home/test/process
lrwxrwxrwx 1 test test 0 Sep 23 20:09 exe -> /home/test/process/process (deleted)
那么有几个问题就随之产生了:
- 这个父进程是谁,为什么一直不变?
对于这个问题,可以使用ps
命令来查看,而结果是bash
,也就是命令行解释器
[test@VM-16-11-centos process]$ ps ajx |head -1 && ps ajx | grep 7945
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
7944 7945 7945 7945 pts/0 16488 Ss 1003 0:00 -bash
- 父进程的概念如何理解?
关于这个问题,后续和fork
一起总结
Linux
中创建进程的方式通常有两种:
- 命令行中直接启动进程
- 通过代码来创建进程
对于第一种很好理解,执行程序./process
其实就是启动进程,那第二种是什么?如何理解这个概念?就引出了本篇的核心,系统调用fork
启动进程,本质就是创建进程,一般都是通过父进程创建的
关于如何创建进程,下面用fork
来讲述
fork
首先要对fork
有一个初步认知,它可以给当前的进程添加一个子进程,它可以使用代码来创建进程,它是属于系统级别的调用:
- 当用户启动一个进程的时候,如何理解在行为?其实就是在系统重多增加一个进程,操作系统要管理的进程就多了一个
- 创建一个进程,就是要向系统中申请一些内存,保存当前进程的可执行程序并且要创建这个进程的
PCB
,也就是task_struct
对象,并且将这个对象添加到进程列表中
那么如何来观察现象?
对程序进行改变
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("Before fork:the pid is %d,the ppid is %d\n",getpid(),getppid());
fork();
printf("After fork:the pid is %d,the ppid is %d\n",getpid(),getppid());
return 0;
}
运行程序
# 运行程序
[test@VM-16-11-centos process]$ ./process
# 运行结果
Before fork:the pid is 7810,the ppid is 6523
After fork:the pid is 7810,the ppid is 6523
After fork:the pid is 7811,the ppid is 7810
现象很神奇,程序中只打印了一次After fork...
,但却输出了两次,并且第二次输出的结果的ppid就是第一次输出的pid
,这是不是证明第二次输出的这个7811
就是7810
的子进程?
答案是肯定的,并且fork
之后这两个进程都会执行它们自己的程序,由此展开关于fork
的理解
fork的具体描述
使用man指令查看fork
的帮助文档内的信息
NAME
fork - create a child process
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
从上面的表述中,可以获取到一些信息,fork
的功能就是创建一个子进程,fork
函数有两个返回值,给子进程返回0
,给父进程返回子进程的pid
为什么要使用fork?
在C
语言中,写的代码的执行逻辑都是从上向下执行(默认情况下),但是事实上在一些情况下这样的需求满足不了用户的需求,例如要边下载一个电影边播放它,那么这个时候就需要创建一个子进程来帮助实现这个功能,这是单进程所解决不了的,因此级有了fork
函数,有了这个函数可以让父进程和子进程做不一样的事,执行不一样的代码,通过fork
的返回值就能判断谁是子进程谁是父进程,就可以让它们实现不同的代码片段,执行不同的功能
fork做了些什么?
从表面上看,fork
函数在执行后,增加了一个进程,这个进程和原来的父进程会一起执行下去,分别在自己的进程下输出内容(以这个代码为例子),但是这都是表象,从深层次的角度来看,fork
的实现过程并不简单
既然是子进程,也属于进程,是进程就要创建程序的PCB
,那现在有了关于task_struct
,里面的信息在哪里?拷贝的内容是什么?答案是以父进程为模板,为子进程创建PCB
,而创建的PCB
指向的可执行文件并没有创建,因此这里实现的子进程没有它自己的代码和数据,只是和父进程共享代码和数据,用下图来描述上面的这一系列过程如下:
上图清晰的把前文进行了图示化,创建出的子进程也有自己的PCB
,内容来自于父进程的PCB
,直接拷贝而来,而它的可执行文件是共享的父进程的可执行文件
fork
之后,父子进程会执行同样的代码,fork
之前的代码父子进程也都有记录
fork之后会执行同样的代码
-
fork
之前的代码可以看到吗?可以看到,父子进程都有前面代码的数据 -
子进程为什么不从头开始执行?这是由于
pc/eip
执行fork
完毕后,eip
指向的是fork
后续的代码,eip
也会被子进程继承,因此直接执行的是fork
之后的代码
进程的独立性如何理解?
关于进程的独立性,可以从下面的角度入手,首先,进程独立性可以从每一个进程都有它自己的PCB
来看出,进程之间是不会被相互影响的,但是数据父子是会被修改的,那么可以采用写实拷贝的方法来将这个过程执行下去,进程的代码可以共享,但是进程的数据不可以,数据每一个进程只能拥有一份,不然对于一些全局变量来说,一个进程对于全局变量的改变会使得另外一个进程受到严重的影响,因此上面的图其实并不完全正确,应该这样改良
新创建的子进程的PCB
中,对于可执行文件的拥有应该分为两部分,代码片段和数据片段,对于代码片段可以进行共享,但是对于数据片段一定是每一个进程拥有一份独立的数据,这样才能保证进程的独立性
fork之后,父子进程谁先运行?
创建完成子进程,只是开始,系统中有其他进程,父进程和子进程,接下来谁先被执行要看谁先被调度,先被调度的就要先运行,那么有具体的规则吗?没有,这是由操作系统自己来决定的,由每个进程PCB
中的调度信息,比如说时间片优先级等等,和调度器算法共同决定谁优先被调用
如何理解fork?
从本质上来讲,fork
其实就是一个函数,那么它函数中的内容会有哪些?
- 找到父进程的
PCB
对象 malloc
一个PCB
用来放子进程- 用父进程的
PCB
来初始化子进程 - 让子进程的
PCB
指向父进程的代码和数据 - 子进程和父进程都加入调度队列开始排队
- …
简单来说,就是创建子进程,将子进程放到调度队列中运行
以上为对fork
的初步理解,后续会继续用它实现系统级别的代码