上篇大概总结了Linux中关于文件的一些知识,这篇就总结一下Linux中关于进程部分的知识要点。
一. 创建进程
创建进程可以使用
fork
或vfork
函数,两者的区别在于,fork创建的子进程会复制父进程的属性和资源,子进程对资源做出修改,父进程不可见,即两者拥有彼此独立的内存空间。而vfork创建的进程,父子进程共享资源和变量,子进程对资源做出修改,也会影响父进程,即两者共享同一内存空间,而且子进程执行完毕才会执行父进程。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
int global = 1;
int i = 0;
pid_t pid;
pid = fork();
/*pid = vfork();*/
if (pid == -1){
perror("fork");
exit(1);
}else if (pid == 0){
i = 3;
while (i > 0){
printf("child process\n");
global++;
i--;
sleep(1);
}
printf("In child process global=%d\n", global);
}else {
i = 5;
while (i > 0){
printf("parent process\n");
global++;
i--;
sleep(1);
}
printf("In parent process global=%d\n", global);
}
exit(0);
}
程序执行结果:
fork方法:输出父子进程的顺序是任意的,而且变量等资源都是独立的,如下图:
vfork方法:先执行子进程,再执行父进程,父子进程共享一个变量,如下图:
二. 在循环中创建进程
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
int i = 0;
pid_t pid;
for (i = 0; i < 2; i++){
pid = fork();
if (pid == -1){
perror("fork");
exit(1);
}else if (pid == 0){
printf("Child process pid=%d, parent process pid=%d\n", getpid(), getppid());
}else {
printf("Parent process pid=%d, parent process pid=%d\n", getpid(), getppid());
wait(NULL); //不加wait,若父进程先结束,则子进程就会成为孤儿进程,成为孤儿进程会被init进程回收,此时子进程的父进程为init进程
}
}
exit(0);
}
在循环中创建进程,每生成一个新进程都会产生两个进程
三. 孤儿进程
所谓孤儿进程就是,父进程先结束,子进程后结束,子进程之后被init进程领养,此后子进程的父进程就是init进程。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
int i = 0;
pid_t pid;
pid = fork();
if (pid == -1){
perror("fork");
exit(1);
}else if (pid == 0){
while (i < 5){
printf("Child process pid=%d, parent process pid=%d\n", getpid(), getppid());
i++;
sleep(1);
}
exit(0);
}else {
printf("Parent process pid=%d, parent process pid=%d\n", getpid(), getppid());
}
exit(0);
}
可以看到产生子进程的父进程的pid一直是1,说明此时子进程为孤儿进程。可以在父进程中调用wait函数来等待子进程结束后,在退出父进程。
三. 僵尸进程
僵尸进程就是子进程先结束,然后如果他死后一些资源无人回收就会变成僵尸进程,僵尸进程是占用系统资源的无用进程,虽然他死后也会释放内存空间,但他的一些属性还是存在的,比如进程号,这在系统中是很宝贵的资源,如果一直占着不放,会影响其他进程产生新进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main() {
pid_t pid;
if((pid=fork())==-1) perror("fork");
else if(pid==0) {
printf("child process %d will become a zombie.\n",getpid());
exit(0);
}else{
sleep(2);
exit(0);
}
}
在上面的代码中由于父进程暂停两秒,所以子进程先结束,而且在父进程中,并没有去回收资源,所以产生了僵尸进程。一般我们是要避免僵尸进程产生的,所以一定要在父进程中调用wait函数等待子进程结束,来回收资源。
四. 创建守护进程
守护进程就是运行在后台没有控制终端与之相连的进程。创建代码如下:
int creat_daemon(){
pid_t pid;
pid = fork();
if(pid == -1) {
perror("fork");
}
if(pid > 0 ) {
exit(0);
}
if(setsid() == -1) {
perror("setsid");
}
pid = fork();
if(pid == -1) {
perror("fork");
}
if(pid > 0 ) {
exit(0);
}
chdir("/");
int i;
for( i = 0; i < NOFILE; i++) {
close(i);
}
umask(0);
signal(SIGCHLD, SIG_IGN);
return 0;
}
守护进程创建分为如下几步:
(1)创建进程,使父进程退出,然后创建一个新的进程组,使子进程成为进程组组长,setsid()
就是使子进程成为进程组组长,使之脱离终端;
(2)再创建一个新的进程,为了保证当前进程不是进程组组长,同时使该进程无法打开控制终端,所以使进程再次退出;
(3)使用close()
关闭文件描述符;
(4)使用umask(0)
将文件屏蔽字设置为0;
(5)处理SIGCHLD
信号。