进程控制(一)

1. fork

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main (void)
{
    pid_t pid;
    int i = 3;
    pid = fork();
    switch(pid){
        case 0:
            while(i--){
                printf("A background process, PID:%d, ParentID:%d\n", getpid(), getppid());
            }
                sleep(1);
            break;

        case -1:
            perror("Process creation failed.\n");
            exit(1);

        default:
            printf("I am parent process, my pid is %d\n", getpid());
            exit(0);
    }

    return 0;
}

运行结果为:

test_result

  在ubuntu的图形界面中,收养孤儿进程的并非init进程,而是交给了其子进程,所以显示的父进程id不是1。可以将系统转到字符界面(alt + shift + F1),再运行程序,会发现其父进程id变为1,即init进程收养了该孤儿进程。回到图形界面用alt + shift + F7.

2. 孤儿进程与僵尸进程

  上面说到了孤儿进程,所以再来总结下孤儿进程僵尸进程区别
  孤儿进程: 父进程退出,而其子进程仍在运行,则这些子进程将成为孤儿进程。孤儿进程被init进程收养(进程号为1),并完成对状态收集工作。就像没有父母而被收养的孤儿。
  僵尸进程: 某子进程已退出,而父进程没有调用waitwaitpid来获取子进程的状态信息,所以子进程的进程描述符仍保存在系统中,占用系统资源。就像僵尸,已经died,仍要进行肢体活动。

  为什么会产生僵尸进程呢?

  unix为了保证父进程可以得到子进程结束时的状态信息,在每个进程退出的时候,内核释放该进程所有的资源(打开的文件,占用的内存等),但仍然为其保留一定的信息(进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。因此如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用。僵尸进程过多就会导致无可用进程号。(kill等命令只可以杀死正在运行的进程,并不能清除僵尸进程的信息)

参考资料: http://www.cnblogs.com/Anker/p/3271773.html

3. 创建守护进程

1) 基本步骤:
- 让进程在后台执行:fork一个子进程,然后是父进程退出;
- 调用setsid创建新对话期,摆脱控制终端等的影响,成为无终端的会话组长;
- 禁止进程重新打开终端:fork一个新的子进程,使父进程退出;
- 关闭不需要的文件描述符,避免浪费系统资源;
- 将当前目录改为根目录,防止当前文件系统不能被拆卸;;
- 将文件屏蔽字设为0;
- 处理SIGCHLD信号,设为SIG_IGN,使子进程结束时不会产生僵尸进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>

int init_daemon(void){
    int pid;
    int i;

    /*忽略终端I/O信号,STOP信号*/
    /*防止守护进程没有正常运行时,控制终端受干扰退出或挂起*/
    signal(SIGTTOU, SIG_IGN); //Background Group的进程尝试写Terminal时
    signal(SIGTTIN, SIG_IGN); //Background Group的进程尝试读取Terminal时
    signal(SIGTSTP, SIG_IGN); //Suspend Key,一般是Ctrl+Z, 发送给所有Foreground Group的进程
    signal(SIGHUP, SIG_IGN);  //发送给具有Terminal的Controlling Process,当terminal被disconnect时

    pid = fork();
    if(pid > 0)
        exit(0);  //结束父进程,成为后台进程
    else if(pid < 0)
        return -1;

    setsid();//新建进程组,脱离终端控制

    /*新建进程,退出父进程,不再是进程组长,使其无法打开终端*/
    pid = fork();
    if(pid > 0)
        exit(0);
    else if(pid < 0)
        return -1;

    /*关闭父进程无用文件描述符*/
    for(i = 0; i < NOFILE; close(i++))
        ;

    chdir("/");//改变工作目录

    umask(0);//将文件屏蔽字设为0

    signal(SIGCHLD, SIG_IGN);//忽略SIGCHLD信号
    return 0;
}
int main (void)
{
    time_t now;
    init_daemon();
    syslog(LOG_USER|LOG_INFO, "测试守护进程!\n");
    while(1){
        sleep(8);
        time(&now);
        syslog(LOG_USER|LOG_INFO, "系统时间: \t%s\t\t\n", ctime(&now));//需要先配置日志文件
    }

    return 0;
}

4. fork返回值

  一次调用,两次返回。
  成功fork后,当前进程分裂为两个进程,父进程子进程一次返回值为0,表示子进程在运行;另一次返回值大于0,表示父进程在运行,返回值即子进程的进程ID。进程创建失败则返回-1.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值