Linux编程每周一讲-进程
进程可能是Linux世界下最有生命力的存在了。所谓进程就是活的程序,正在运行中的代码,它的英文名叫Process。当然后来还有了更轻量级的线程(Thread),但是它是存活在进程的体内的,不能独立于进程存在。所以,本文要讲的就是:Process,Linux下的进程。
话说天地初开之际,计算机还是一堆插着电的废铁,必须要有软件,人类才可能使用它。所以,程序猿和硬件工程师们一起创造了BIOS,也就是固化在硬件当中的软件(我们管它叫固件,Firmware)。当计算机通电后,由BIOS负责将计算机里面的各个硬件加载,运转起来,伟大的科学家们给这一过程非常形象地起了一个名字:Bootstrap。
这个词初见于 埃·拉斯伯和戈·毕尔格 这两位德国人的童话故事 《吹牛大王历险记》:在书中,男主角敏豪森男爵骑着马掉进了沼泽,但他天生神力,两腿夹着马,双手抓着自己的小辫子,把自已和马一起从沼泽里拉到了半空!
因此,你懂的,计算机的启动过程就比较类似于这样。至于为什么后来这个启动过程变成了叫做Bootstrap,即拉着自己的鞋跟,而不是拉着自己的小辫子,这就说不清了。历史上一个说法是,这个德国民间故事后来传到美国,就被再加工,变成了拉鞋跟了。。。
好吧,我们是在说计算机。当BIOS完成Bootstrap后,会把控制权交接给操作系统。此时Linux系统会执行Bootloader,比如GRUB或者LILO,让你选择要运行的Linux系统;对于没有安装Bootloader的Linux系统,则跳过选择这一步,总之接下来是开始Linux系统的加载过程。
此时,一个编号为0的进程产生了,它是所有后续进程的父。这个编号为0的进程会产生一个进程为1的进程,然后自我毁灭(准确来讲,是变成swapper进程)。这个进程为1的进程就是init,它是继而成为所有后续进程的父。
Linux下面的进程,就是这样父与子的层级关系,一个父进程可以有多个孩子(废话,否则init以下只有一条线,Linux就成单任务操作系统了);一个孩子只有一个父(嗯,两个父亲比较麻烦)。
以上是两个神级的进程,我们可以用ps命令看一下init:
% ps -ef | grep initUID PID PPID C STIME TTY TIME CMDroot 1 0 0 Dec04 ? 00:00:01 /sbin/init
/* exit() */#include <stdlib.h>#include <sys/types.h>/* * fork() */#include <unistd.h>/* * stderr * stdout * fprintf */#include <stdio.h>void err_sys(const char* fmt, ...);int main () {pid_t pid;if ((pid = fork()) < 0) { /* 创建新的进程 */err_sys("fork error");} else if (pid == 0) { /* fork向孩子进程返回0 *//* 所以只有孩子进程会执行到这里 */puts("child process here."); puts("child process will sleep for 10 seconds...");sleep(10);} else { /* fork向父进程返回孩子进程的PID *//* 所以只有父进程会执行到这里 */puts("parent process here.");printf("parent get the child pid: %d\n", pid);}/* 父亲孩子共用的代码部分 *//* 注意,虽然代码是共用的,但内存资源是独立的 *//* 父亲是访问不到孩子的变量,孩子也访问不到父亲的变量 *//* 所以说进程是很重的,资源会被复制。*/sleep(3);if (pid == 0) { /* child process */puts("child process exit.");} else { /* parent process */puts("parent process exit.");}/* share part of child and parent */return 0;}void err_sys(const char* fmt, ...) {/* man stdarg to see the instructions on va_list */va_list ap;fprintf(stderr, fmt, ap);exit(1);}cc process.c -o process.o
% ./process.oparent process here.parent get the child pid: 1650child process here.child process will sleep for 10 seconds...
% ps -ef | grep process.oUID PID PPID C STIME TTY TIME CMDweli 1649 1407 0 00:54 pts/1 00:00:00 ./process.oweli 1650 1649 0 00:54 pts/1 00:00:00 ./process.o
parent process exit.
% ps -ef | grep process.oUID PID PPID C STIME TTY TIME CMDweli 1650 1 0 00:54 pts/1 00:00:00 ./process.o
child process exit.
/* exit() */#include <stdlib.h>#include <sys/types.h>/* * fork() */#include <unistd.h>/* * stderr * stdout * fprintf */#include <stdio.h>void err_sys(const char* fmt, ...);int main () {pid_t pid;if ((pid = fork()) < 0) { /* create a process */err_sys("fork error");} else if (pid == 0) { /* child process *//* Child process exit immediately. * It will create a zombie process, * and waiting for parent to handle it. */exit (0); } else {/* Parent process sleep, so we can see *//* child process in zombie state. */fputs("parent process goes to sleep...", stderr);sleep (60); }return 0;}void err_sys(const char* fmt, ...) {/* man stdarg to see the instructions on va_list */va_list ap;fprintf(stderr, fmt, ap);exit(1);}cc zombie.c -o zombie.o
% ./zombie.o parent process goes to sleep...
% ps -ef | grep zombie.oUID PID PPID C STIME TTY TIME CMDweli 1683 1407 0 01:10 pts/1 00:00:00 ./zombie.oweli 1684 1683 0 01:10 pts/1 00:00:00 [zombie.o] <defunct>
lsof -p 1684
% lsof -p 1683COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEzombie.o 1683 weli cwd DIR 253,2 4096 131119 /home/weli/projs/learnczombie.o 1683 weli rtd DIR 253,1 4096 2 /zombie.o 1683 weli txt REG 253,2 5404 131329 /home/weli/projs/learnc/zombie.ozombie.o 1683 weli mem REG 253,1 157200 28423 /lib/ld-2.14.90.sozombie.o 1683 weli mem REG 253,1 1991260 28429 /lib/libc-2.14.90.sozombie.o 1683 weli 0u CHR 136,1 0t0 4 /dev/pts/1zombie.o 1683 weli 1u CHR 136,1 0t0 4 /dev/pts/1zombie.o 1683 weli 2u CHR 136,1 0t0 4 /dev/pts/1
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
/* exit() */#include <stdlib.h>#include <sys/types.h>/* * fork() */#include <unistd.h>/* * stderr * stdout * fprintf */#include <stdio.h>/* We'll catch SIGCHLD signal to know that child process has quit */#include <signal.h>void err_sys(const char* fmt, ...);static void child_quit(int);int main () {pid_t pid;int i;if ((pid = fork()) < 0) { /* create a process */err_sys("fork error");} else if (pid == 0) { /* child process *//* Child process will quit after 3 seconds. * It will create a zombie process, * and waiting for parent to handle it. */puts("child process start.");sleep(3);exit (0); } else {if (signal(SIGCHLD, child_quit) == SIG_ERR)err_sys("cannot register signal handler for SIGCHLD!");puts("signal handler registered for SIGCHLD.");/* Parent process will do some work now */for (i = 0; i < 10; i++) {sleep (1); printf("#%d: parent is doing work.\n", i);}}return 0;}static void child_quit(int signo) {wait(NULL);puts("child process quit.");}void err_sys(const char* fmt, ...) {/* man stdarg to see the instructions on va_list */va_list ap;fprintf(stderr, fmt, ap);exit(1);}cc zombie_fix.c -o zombie_fix.o
% ./zombie_fix.osignal handler registered for SIGCHLD.child process start.#0: parent is doing work.#1: parent is doing work.child process quit.#2: parent is doing work.#3: parent is doing work....
% ps -ef | grep zombie_fix.oweli 1750 1407 0 01:52 pts/1 00:00:00 ./zombie_fix.o
/* * This code is referenced from APUE 1st edition, program 8.2, with slight modifications. *//* * fprintf() */#include <stdio.h>/* * vfork() * _exit() * getpid() */#include <unistd.h>/* * exit() */#include <stdlib.h>int glob = 1; /* external variable in initialized data */void err_sys(const char* fmt, ...);int main() {int var; /* automatic variable on the stack */pid_t pid;var = 1;printf("before vfork\n");if ((pid = vfork()) < 0) err_sys("vfork error");else if (pid == 0) { /* child *//* * Because vfork makes child to share address space with parent, * the child process will increment the glob variable in parent space; */printf("modifying variables in parent address space.\n");glob++; var++;/* * Compared with exit(), _exit() won't flush io and close the stdout. Because we * are sharing address space with parent, we must use _exit() so in following * parent printf() call, the parent could output to stdout. If we use exit here * instead, the printf() in parent won't print anything. */_exit(0); }/* parent *//* * if we use exit() instead of _exit() in child process, printf won't output anything, * because stdout is already closed. */printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0);}void err_sys(const char* fmt, ...) {va_list ap;fprintf(stderr, fmt, ap);exit(1);}http://github.com/liweinan/learnc