fork

创建一个子进程

头文件:
    #include <unistd.h>
原型:
    pid_t fork(void);
返回值:
    成功:    >0    在父进程中,返回创建的子进程的PID;
              0     在子进程中,返回0;
    失败,    -1    在父进程中,返回-1,没有子进程被创建,更新errno;
例程:
    pid_t pid = fork();
    if(pid > 0)//父进程
    {
        printf("this is parent %d %d\n", getpid(), pid);
        printf("pid = %d %d\n", pid, __LINE__);
    }
    else if(0 == pid)//子进程
    {
        printf("this is child %d %d\n", getppid(), getpid());
        printf("pid = %d %d\n", pid, __LINE__);
    }
    else if(pid < 0)//创建失败
    {
        perror("fork");
        return -1;
    }

getpid

获取当前进程的PID

头文件:
    #include <sys/types.h>
    #include <unistd.h>
原型:
    pid_t getpid(void);
返回值:
    获取当前进程的PID;
例程:
    pid_t pid = getpid();

getppid

获取当前进程的父进程PID

头文件:
    #include <sys/types.h>
    #include <unistd.h>
原型:
    pid_t getppid(void);
返回值:
    获取当前进程的父进程PID;
例程:
    pid_t ppid = getppid();

_exit

终止进程,清除进程使用的内存空间,销毁其在内核中的各种数据,但不会刷新缓冲区

头文件:
    #include <unistd.h>
原型:
    void _exit(int status);
参数:
    int status:可以利用这个参数传递进程终止的状态,可以输入任意整形。这个参数会被 wait/waitpid 函数接收。
例程:
    _exit(1);

exit

终止进程,并刷新缓冲区

头文件:
    #include <stdlib.h>
原型:
    void exit(int status);
参数:
    int status:可以利用这个参数传递进程终止的状态,可以输入任意整形。这个参数会被 wait/waitpid 函数接收。
例程:
    exit(1);

wait

阻塞等待子进程退出,并回收子进程的资源;如果没有子进程,父进程不等待,直接退出

头文件:
    #include <sys/types.h>
    #include <sys/wait.h>
原型:
    pid_t wait(int *status);
参数:
    int *status:获取子进程退出的状态;接收exit/_exit中的status传递值。如果不想接收,填NULL;
返回值:
    成功,返回退出的子进程的pid;
    失败,返回-1,更新errno;
例程:
    //不接收子进程退出的状态值
    pid_t c_pid = wait(NULL);
    printf("回收子进程资源成功 %d\n", c_pid);
    //接收子进程退出的状态值
    int status; 
    pid_t c_pid = wait(&status);
    printf("回收子进程资源成功 %d\n", c_pid);
    printf("status=%d\n", status);

waitpid

等待指定进程退出,并回收指定进程的资源

头文件:
    #include <sys/types.h>
    #include <sys/wait.h>
原型:
    pid_t waitpid(pid_t pid, int *status, int options);
参数:
    pid_t pid:
        < -1    回收指定进程组下的任意子进程,指定进程组的id = pid的绝对值。例如PGID=1976,则pid填-1976;
          -1    等待当前进程下的任意子进程。例如:回收变为僵尸进程的子进程;
          0     回收当前进程组下的任意子进程;
        > 0     回收指定进程。
    int *status:获取子进程退出的状态;接收exit/_exit中的status传递值。如果不想接收,填NULL;
    int options:
        0,阻塞等待,如果指定的进程没有退出,则当前进程阻塞等待。
        WNOHANG:非阻塞,就算指定进程没有退出,当前进程不等待,继续执行后面的代码;
返回值:
    成功,阻塞方式,返回被回收进程的pid.
        非阻塞方式,如果指定进程退出,则返回被回收的进程的pid;如果指定进程没有退出,则返回0;
    失败,返回-1,更新errno; 
例程:
    //不接收子进程退出的状态值
    pid_t c_pid = waitpid(pid,NULL,0);
    printf("回收子进程资源成功 %d\n", c_pid);
    //接收子进程退出的状态值
    int status; 
    pid_t c_pid = wait(pid,&status,0);
    printf("回收子进程资源成功 %d\n", c_pid);
    printf("status=%d\n", status);

WEXITSTATUS

从wait(&s)到提取exit(status)中status的值

pid_t wait(int*s); 中s存储的是一个32位的整形,其中[8,15]总共8bit存放的是exit(status)中的status;

用宏函数的方式返回子进程终止函数exit(int status)中的status值

例程:
    int status; 
    wait(&status);
    printf("status=%d\n", status);
    int ret = WEXITSTATUS(status);
    printf("ret = %d\n", ret);

WIFEXITED

判断子进程是否正常退出

  • 返回1,代表子进程正常退出;调用函数exit或return退出的都是正常退出
  • 返回0,代表子进程异常退出;由于信号而退出的是异常退出,例如kill -9 pid
例程:
    int status; 
    wait(&status);
    printf("status=%d\n", status);
    int res = WIFEXITED(status);
    if(res)
    {
        printf("正常退出\n");
    }
    else 
    {
        printf("异常退出\n");
    }

例程

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void child_func();
int main(int argc, const char *argv[])
{
    pid_t pid = fork();
    if(pid > 0)
    {
        //父进程
        printf("parent.....\n");
        sleep(1);
        int status; 
        pid_t c_pid = wait(&status);
        printf("回收子进程资源成功 %d\n", c_pid);
        printf("status=%d\n", status);
        int ret = WEXITSTATUS(status);
        printf("ret = %d\n", ret);
        int res = WIFEXITED(status);
        if(res)
        {
            printf("正常退出\n");
        }
        else 
        {
            printf("异常退出\n");
        }
        while(1)
            sleep(1);
    }
    else if(0 == pid)
    {
        //子进程
        child_func();
        printf("child end\n");
    }
    else
    {
        perror("fork");
        return -1;
    }
    printf("准备退出了\n");
    return 0;
}
void child_func()
{
    int i=0;
    while(1)
    {
        printf("child....\n");
        sleep(1);
        i++;
        if(i==1)
        {
            exit(-1);
            //exit(0);
            //exit(1);
        }
    }
}

结果

//exit(-1);时结果
parent.....
child....
回收子进程资源成功 6747
status=65280
ret = 255
正常退出

//exit(0);时结果
parent.....
child....
回收子进程资源成功 6756
status=0
ret = 0
正常退出

//exit(1);时结果
parent.....
child....
回收子进程资源成功 6763
status=256
ret = 1
正常退出

守护进程的创建

1、创建孤儿进程

2、创建新的会话,脱离父进程的控制

3、修改运行目录

4、修改文件权限掩码,一般保留文件原有权限

5、关闭所有文件描述符

setsid

头文件:  
    #include <unistd.h>
原型:
    pid_t setsid(void);
返回值:
    成功,返回新的会话id;
    失败,返回-1,更新errno;

1.创建新的会话,成为会话组组长;
2.创建新的进程组,成为进程组组长;
3.脱离父进程的控制。

chdir

头文件:   
    #include <unistd.h>
原型:
    int chdir(const char *path);
参数:
    char *path:指定修改后的运行目录,如"/";
返回值:
    成功,返回0;
    失败,返回-1;

例程

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
    //1、创建孤儿进程
    pid_t pid = fork();
    if(pid > 0)
    {
    }
    else if(pid == 0)
    {
        //子进程
        //2、创建新的会话,脱离父进程的控制
        pid_t sid = setsid();
        printf("sid = %d\n", sid);
        //3、修改运行目录
        chdir("/");
        //从这一步往后的程序,当前进程运行在根目录下
        //4、修改文件权限掩码
        umask(0);
        //5、关闭所有文件描述符
        int i = 0;
        for(i=0; i<1024; i++)
        {
            close(i);
        }
        while(1)
        {
            //程序功能
            sleep(1);
        }
    }
    else
    {
        perror("fork");
        return -1;
    }
    return 0;
}

应用例程

拷贝一张图片,是先父进程拷贝前半部分,后子进程拷贝后半部分。

提示:用sleep(2);

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{\
        perror(msg);\
        fprintf(stderr, "%d %s %s\n", __LINE__, __func__, __FILE__);\
}while(0)
int main(int argc, const char *argv[])
{
    int fd_r = open("./1.png", O_RDONLY);
    if(fd_r < 0)
    {
        ERR_LOG("open");
        return -1;
    }
    int fd_w = open("2.png", O_WRONLY|O_CREAT|O_TRUNC, 0664);
    if(fd_w < 0)
    {
        ERR_LOG("open");
        return -1;
    }
    int size = lseek(fd_r, 0, SEEK_END);     //计算文件大小
    lseek(fd_r, 0, SEEK_SET);             //偏移回开头位置
    pid_t pid = fork();
    if(pid > 0)
    {
        //父进程
        int i = 0;
        char c = 0;
        int res = -1;
        for(i=0; i<size/2; i++)
        {
            res = read(fd_r, &c, 1);
            if(-1 == res)
            {
                ERR_LOG("read");
                exit(1);
            }
            //写入到2.png中 fd_w
            if(write(fd_w, &c, 1) < 0)
            {
                ERR_LOG("write");
                exit(1);
            }
        }
        printf("前半部分拷贝完毕\n");
        wait(NULL);
        printf("子进程退出\n");
    }
    else if(0 == pid)
    {
        //子进程
        sleep(2);
        lseek(fd_r, size/2, SEEK_SET);
        lseek(fd_w, size/2, SEEK_SET);
        int i =0, res = -1;
        char c = 0;
        for(;;)
        {
            res = read(fd_r, &c, 1);
            if(-1 == res)
            {
                ERR_LOG("read");
                exit(1);
            }
            else if(0 == res)
            {
                break;
            }
            //写入到2.png中 fd_w
            if(write(fd_w, &c, 1) < 0)
            {
                ERR_LOG("write");
                exit(1);
            }
        }
        printf("后半部分拷贝完毕\n");
    }
    else
    {
        ERR_LOG("fork");
        return -1;
    }
    close(fd_r);
    close(fd_w);
    return 0;
}
Last modification:2021 年 04 月 08 日 22 : 52 : 40