转载请注明出处:http://blog.youkuaiyun.com/muge0913/article/details/7315522
如果我们把计算机上的操作系统及各种各样的软件看成一系列的有机生命,而不是指令集,那么这就是一个进程的世界,在进程的世界中同样有“道德”和“法制法规”,窥探进程世界,看它的侠肝义胆,风雨江湖路~~~~~
linux支持多个进程同时进行,也就是我们常说的现代操作系统中的多道程序设计,所谓同时是linux系统调度各个进程分别占用cpu的时间。由于每个时间片的时间很小和宏观时间相比,给人的感觉是多个进程在运行。
注:总结下就是在微观是串行,在宏观上是并行。
为了提高程序的运行效率,程序往往分成多个部分组成,这也就是说的并发程序设计。并发程序中各进程是相互独立的,在必要的时候会通过相应的机制进行通信。若进程间要共享资源,为了避免出现冲突,常通过相应通信机制使它们轮流使用共享资源。在进程进行通信时,会出现一个进程等另一个进程完,才能继续运行的情况,这也需要进程间通信以了解对方的运行情况。有时进程间会出现互斥现象,这是会用到锁机制。在并发程序设计中,进程的创建和结束是由用户决定的。这也就出现了父进程和子进程概念。
进程的创建:
- #include <unistd.h>
- pid_t fork(void);
- pid_t vfork(void);
在这简述,fork创建的子进程是父进程的一个拷贝,但是和父进程使用不同的数据段和堆栈。vfork和fork基本相同但是vfork不会复制父进程的数据段,它们共享数据段。这是因为vfork常和exec函数使用去调用一个程序如ls命令,开启一个新的进程。vfork后父进程会等待子进程运行结束或调用了exit。fork后父进程和子进程的运行顺序是不确定的。
下面是体现它们性质的程序:
- #include <sys/types.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- main()
- {
- pid_tpid;
- char*pchar = "before fork";
- inttest_va = 1;
- if((pid= fork()) < 0 )
- {
- printf("forkerror!!\n");
- exit(1);
- }
- elseif(pid == 0)
- {
- printf("childprocess\n");
- pchar= "child pchr\n";
- printf("%s\n",pchar);
- test_va= 2;
- printf("%d\n",test_va);
- _exit(2);
- }
- else
- {
- printf("parentprocess\n");
- //sleep(1);
- printf("%s\n",pchar);
- printf("%d\n",test_va);
- }
- }
把上面的fork改为vfork即可
fork:
vfork:
进程等待
- #include <sys/types.h>
- #include <stdio.h>
- #include <sys/wait.h>
- void check_exit(int status);
- main()
- {
- pid_t pid;
- int status;
- if((pid = fork()) < 0)
- {
- printf("fork error!!\n");
- exit(0);
- }
- else if(pid == 0)
- {
- printf("child process exit\n");
- exit(0);
- }
- else
- {
- if(wait(&status) != pid)
- {
- printf("wait error!!");
- exit(0);
- }
- check_exit(status);
- }
- }
- void check_exit(int status)
- {
- if(WIFEXITED(status))
- printf("eixt\n");
- else if(WIFSIGNALED(status))
- printf("killed by signal\n");
- else if(WIFSTOPPED(status))
- printf("stopped by signal\n");
- else if(WIFCONTINUED(status))
- printf("continued");
- }
等待进程改变其状态。所有下面哪些调用都被用于等待子进程状态的改 变,获取状态已改变的子进程信息。状态改变可被认为是:1.子进程已终止。2.信号导致子进程停止执行。3.信号恢复子进程的执行。在子进程终止的情况 下,wait调用将允许系统释放与子进程关联的资源。如果不执行wait,终止了的子进程会停留在"zombie"状态。
如果发现子进程改变了状态,这些调用会立即返回。反之,调用会被阻塞直到子进程状态改变,或者由信号处理句柄所中断(假如系统调用没有通过sigaction的SA_RESTART标志重启动)。
wait 系统调用挂起当前执行中的进程,直到它的一个子进程终止。waitpid挂起当前进程的执行,直到指定的子进程状态发生变化。默认,waitpid只等待 终止状态的子进程,但这种行为可通过选项来改变。waitid系统调用对于等待哪个子进程状态改变提供了更精确的控制。
子进程已终 止,父进程尚未对其执行wait操作,子进程会转入“僵死”状态。内核为“僵死”状态的进程保留最少的信息量(进程标识,终止状态,资源使用信息),过后 父进程执行wait时可以获取子进程信息。只要僵死的进程不通过wait从系统中移去,它将会占据内核进程表中的一个栏位。如果进程表被填满,内核将不能 再产生新进程。如果父进程已终止,它的僵死子进程将由init进程收养,并自动执行wait将它们移去。
wait(等待子进程中断或结束)
- #include<sys/types.h>
- #include<sys/wait.h>
- pid_t wait (int * status);
函数说明
wait()会暂时停止目前进程的执行(挂起父进程),直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束,则 wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数 status 可以设成 NULL。如果调用wait的进程没有子进程则会调用失败,子进程的结束状态值请参考 waitpid( )
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。
linux中常用退出函数:
- #include<stdlib.h>
- voidexit(int status);
- intatexit(void (*function)(void))
- inton_exit(void (*function)(int,void *),void arg*)
- voidabort(void)
- #include<unistd.h>
- void_exit(int status)
- #include<assert.h>
- voidassert(int expression)
atexit:在其中注册的无参数函数在退出时调用。成功返回0失败返回-1,并影响errno
on_exit:在其中注册的有参数函数在退出时调用。成功返回0失败返回-1,并影响errno
assert是宏定义,检查是否出错,出错则退出。
abort发送SIGABRT消息结束当前进程。
exit和_exit函数都是用来终止进程的。当程序执行到exit或_exit时,系统无条件的停止剩下所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。但是,这两个函数是有区别的。
exit()函数与_exit()函数最大区别就在于exit()函数在调用do_exit之前要检查文件的打开情况,把文件缓冲区的内容写回文件。
由于Linux的标准函数库中,有一种被称作“缓冲I/O”的操作,其特征就是对应每一个打开的文件,在内存中都有一片缓冲区。每次读文件时,会连续的读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区读取;同样,每次写文件的时候也仅仅是写入内存的缓冲区,等满足了一定的条件(如达到了一定数量或遇到特定字符等),再将缓冲区中的内容一次性写入文件。这种技术大大增加了文件读写的速度,但也给编程代来了一点儿麻烦。比如有一些数据,认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时用_exit()函数直接将进程关闭,缓冲区的数据就会丢失。因此,要想保证数据的完整性,就一定要使用exit()函数。
exit的函数声明在stdlib.h头文件中。
_exit的函数声明在unistd.h头文件当中。
下面的实例比较了这两个函数的区别。printf函数就是使用缓冲I/O的方式,该函数在遇到“\n”换行符时自动的从缓冲区中将记录读出。实例就是利用这个性质进行比较的。
exit.c源码
- #include<stdlib.h>
- #include<stdio.h>
- intmain(void)
- {
- printf("Using exit...\n");
- printf("This is the content inbuffer");
- exit(0);
- }
输出信息:
Usingexit...
Thisis the content in buffer
- #include<unistd.h>
- #include<stdio.h>
- intmain(void)
- {
- printf("Using exit...\n");
- printf("This is the content inbuffer");
- _exit(0);
- }
则只输出:
Usingexit...
说明:在一个进程调用了exit之后,该进程并不会马上完全小时,而是留下一个称为僵尸进程(Zombie)的数据结构。僵尸进程是一种非常特殊的进程,它几乎已经放弃了所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其它进程收集,除此之外,僵尸进程不再占有任何内存空间。