进程ID为0的进程通常是调度进程,常常被称为交换进程swapper,该进程是内核的一部分,这并不执行任何磁盘上的程序,因此也被称为系统进程,进程ID为1是init进程,在自举过程结束时由内核调用,该进程的程序文件为/etc/init或者/sbin/init,此进程负责在自举内核后启动一个UNIX系统。init通常读取与系统相关的初始化文件/etc/rc*文件,或/etc/inittab文件,以及/etc/init.d中的文件。并将系统引导到一个状态(例如多用户)。init决不会终止,它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户运行,它是所有孤儿进程的父进程。
进程ID2是页守护进程pagedaemon,负责支持虚拟存储系统的分页操作。
一个现有进程可能通过调用fork创建一个新进程,新进程称为子进程,fork函数调用一次返回两次,再次返回的唯一区别是,子进程返回值是0,父进程的返回值是子进程ID。
子父进程继续执行fork调用之后的指令。子进程是父进程的副本,子获得父进程数据空间,堆和栈的副本。子父并不共享,因为子复制了父的这些存储空间。父子共享正文段。
//以下来自网络
对于exec系列函数
一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到
对于exec():
1、进程调用exec()后,将在同一块进程内存里用一个新程序来代替调用
exec()的那个进程,新程序代替当前进程映像,当前进程的“数据段”,
“堆栈段”和“代码段”背新程序改写。
2、新程序会保持调用exec()进程的ID不变。
3、调用exec()之前打开打开的描述字继续打开(好像有什么参数可以令打开
的描述字在新程序中关闭)
//以上来自网络
// proc/fork1.c 8-1 #include "apue.h" int glob = 6; /* external variable in initialized data */ char buf[] = "a write to stdout "; int main(void) { int var; /* automatic variable on the stack */ pid_t pid; var = 88; if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1) err_sys("write error"); printf("before fork "); /* we don't flush stdout */ /* fflush(stdout); */ /* if comments this line, the child will flush the stdout */ if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child, first flush buffer */ printf("hello, I am child process "); glob++; /* modify variables */ var++; } else { sleep(3); /* parent */ printf("hello, I am parent process "); } if (0 == pid) /* for the child process, variable pid is 0 */ { printf("hello, I am child process, variable pid is 0 "); } else /* for the parent process, variable pid is child pid */ { printf("hello, I am parent process, variable pid is child pid "); } printf("pid = %d, pid = %d, glob = %d, var = %d ", pid, getpid(), glob, var); return 0; }
all: shell1 shell2 fork1 shell1: shell1.c g++ -g -Wall shell1.c ../lib/libapue.a -I ../include -o shell1 shell2: shell2.c g++ -g -Wall shell2.c ../lib/libapue.a -I ../include -o shell2 fork1: fork1.c g++ -g -Wall fork1.c ../lib/libapue.a -I ../include -o fork1 clean: rm shell1 shell2 fork1
交互式的方式执行fork1,在子进程创建之前,before fork已经被父进程从缓冲区刷新,before fork已经被输出到终端设备(交互式是行缓冲),而采用重定向到磁盘文件的方式,这种情况是全缓冲的,也就是说,before fork还在缓冲区,这个缓冲区又被复制到了子进程,于是子进程缓冲区被刷新时,before fork在子进程终止时,被输出了出来。
在重定向父进程的标准输出时,子进程的标准输出同样被重定向,这点还是有疑惑
vfork用于创建一个新进程,该进程的目的是exec一个新进程,但是它不将父进程的地址空间完全复制到子程序,因为子会立即调用exec,或者exit,于是也就不会访问该地址空间,在调用exec之前,子在父进程的空间运行,且vfork保证子进程先运行,在子调用exec或exit之后,父才可能被调度运行。
// proc/vfork1.c 8-2 #include "apue.h" int glob = 6; /* external variable in initialized data */ int main(void) { int var; /* automatic variable on the stack */ pid_t pid; var = 88; printf("before vfork "); /* we don't flush stdio */ if ((pid = vfork()) < 0) { err_sys("vfork error"); } else if (pid == 0) { /* child */ // vfork ensure the child will go first, parent will not continue untill the child run function exec or exit, no matter how long the parent has to wait glob++; /* modify parent's variables */ var++; printf("I'm child, I am going to sleep 5 second "); sleep(5); printf("I'm child, I am going to invork exit function "); _exit(0); /* child terminates */ } /* * Parent continues here. */ printf("parent print: pid = %d, glob = %d, var = %d ", getpid(), glob, var); return 0; }
all: shell1 shell2 fork1 vfork1 shell1: shell1.c g++ -g -Wall shell1.c ../lib/libapue.a -I ../include -o shell1 shell2: shell2.c g++ -g -Wall shell2.c ../lib/libapue.a -I ../include -o shell2 fork1: fork1.c g++ -g -Wall fork1.c ../lib/libapue.a -I ../include -o fork1 vfork1: vfork1.c g++ -g -Wall vfork1.c ../lib/libapue.a -I ../include -o vfork1 clean: rm shell1 shell2 fork1 vfork1