创建并运行新进程

fork(), execl(), exit(), getpid(), getppid()

fork(): 当前进程(父进程)创建一个新的进程(子进程),创建后的父、子进程继续执行fork()调用点后面的程序,当然也可以使用比如execl() 来替换后续要执行的程序。

execl(): 替换当前进程后续要执行的程序(从execl()调用点后续的程序被替换,也就是说原来在execl()调用点后面的程序不再执行,而去执行execl参数中指定的程序)。

exit(): 终止一个进程,将进程占用的所有资源(内存,文件描述符)交还内核,由其进行再次分配。

getpid(): 获取当前进程PID

getppid(): 获取父进程PID

/*
 * 获取当前进程pid: int getpid();
 * 获取父进程pid: int getppid();
 * 执行新的程序: int execl(char *path, char *arg, ...);
 * 创建子进程: int fork();
 * 终止进程: void exit(int status);
 */

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

int main(void) {
    long pid;
    pid = fork();

    /* fork执行错误 */
    if(pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);        
    }


    /* parent */
    if(pid > 0) {
        int pret;

        printf("pid: %d, child_pid: %d\n", getpid(), pid);
        pret = execl("/usr/bin/sleep","sleep", "100", NULL);
        if(pret == -1) {
            perror("parent execl");
            exit(EXIT_FAILURE);
        }        
    }

    /* child */
    if(pid == 0) {
        int cret;

        printf("pid: %d, parent_pid: %d\n", getpid(), getppid());
        cret = execl("/usr/bin/sleep","sleep", "100", NULL);
        if(cret == -1) {
            perror("child execl");
            exit(EXIT_FAILURE);
        } 
    }     
}

输出

    pid: 22190, child_pid: 22191
    pid: 22191, parent_pid: 22190

execl, execlp, execle, execv, execvp

/*
 * int execl(char *path, char *arg, ...);
 * int execlp(char *file, char *arg, ...);
 * int execle(char *path, char *arg, ...);
 * int execv(char *path, char *argv[]);
 * int execvp(char *file, char *argv[]);
 */

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

int main(void) {
    int pret;
    char *args[] = {"sleep", "100", NULL};
    pret = execvp("sleep",args);
    if(pret == -1) {
        perror("parent execl");
        exit(EXIT_FAILURE);
    }
}

wait(), waitpid() 等待子进程退出

wait(), waitpid()用来让父进程获取子进程退出的信息

wait(): 获取任意一个子进程退出信息
waitpid(): 获取特定子进程退出信息

wait

/*
 * int wait(int *status);
 */

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

int main(void) {
    int status;
    int pid, wpid;

    pid = fork();

    if(pid == 0) {
        execl("/usr/bin/sleep", "sleep", "10", NULL);
    }

    if(pid > 0) {
        printf("pid: %d, child_pid: %d\n", pid, getpid());

        wpid = wait(&status);
        printf("wpid = %d\n", wpid);

        if(WIFEXITED(status)) {
            printf("Normal exit, status = %d\n", WEXITSTATUS(status));
        }

        if(WIFSIGNALED(status)) {
            printf("killed by singnal = %d\n", WTERMSIG(status));
        }

        if(WIFSTOPPED(status)) {
            printf("stopped by singnal = %d\n", WSTOPSIG(status));
        }
    }

    return 0;
}

pid_t waitpid(pid_t pid, int wstatus, int options);

/*
 * int waitpid(int pid, int *status, int options);
 */

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

int main(void) {
    int pid;

    printf("start process\n");
    pid = fork();
    if(pid == 0) {
        printf("child process ...\n");
        execl("/usr/bin/bash", "bash", NULL);
        return 1;
    }

    waitpid(pid, NULL, 0);
    printf("process stopped\n");
}

clone

clone()fork() 类似,都是用来创建新进程,不同之处在于:

forck()创建的子进程仍然继续运行调用点后面的程序

clone() 创建的子进程不再执行调用点后面的程序,转而去执行参数中func指定的函数

int clone(int (*func)(void *), void *child_stack, int flags)

示例

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

#define STACK_SIZE (1024 * 1024)

static char child_stack[STACK_SIZE];

int child_process() {
    printf("start child process ...\n");
    execl("/usr/bin/sleep", "sleep", "100", NULL);
    return 1;
}

int main(void) {
    int pid;
    printf("start process\n");
    pid = clone(child_process, child_stack + STACK_SIZE, SIGCHLD, NULL);
    waitpid(pid, NULL, 0);
    printf("process stopped\n");
}

setns(), unshare()

#define _GNU_SOURCE #include <sched.h>

setns(int fd, int nstype);

将当前进程加入到指定的namespace中,fd为进程描述符或路径

#define _GNU_SOURCE

#include <sched.h>

unshare(int flags)

退出当前Namespace,并加入新创建的Namespace

flags:

  • CLONE_FILES

  • CLONE_FS

  • CLONE_NEWCGROUP

  • CLONE_NEWIPC

  • CLONE_NEWPID

  • CLONE_NEWNET

  • CLONE_NEWNS

  • CLONE_NEWUSER

  • CLONE_NEWUTS


会话和进程组

image.png
用户登录:

a. bash进程创建会话和进程组,bing分配一个终端与该会话建立连接

终端产生的信号:

a. 发送信号给前台进程组中的所有进程

网络异常断开:

a. 发送SIGHUP信号给前台进程,分配给会话的tty仍然保持连接

退出登录

a. 发送SIGHUP信号给前台进程
b. 终端与session断开连接
/*
 * 创建新会话:int setsid();
 */

#include <unistd.h>

int main(void) {
    int sid;
    sid = setsid();

    if(sid == -1) {
        perror("setsid");
    }
    execl("/usr/bin/sleep", "sleep", "100", NULL);
}
$ ps -o tty,sid,pgid,ppid,pid,args -eH |less

TT          SID   PGID   PPID    PID COMMAND
pts/0     45181  45181  45180  45181         -bash
pts/0     45181  49838  45181  49838           sudo ./a.out
?         49840  49840  49838  49840             sleep 100
/*
 * 创建新进程组:int setpgid(int pid, int pgid);
     将pid的进程组设置成pgid, pid为0时设置当前进程
 * 获取新进程组: getpgid(int pid)
 */

#include <unistd.h>

int main(void) {
    int pgid;
    int pid;
    pgid = getpgid(0);

    pid = fork();

    if(pid == 0) {
        setpgid(0, pgid);
        execl("/usr/bin/sleep", "sleep", "100", NULL);
    }

    if(pid > 0) {
        execl("/usr/bin/ping", "ping", "www.baidu.com", NULL);
    }
    return 0;
}
/tmp/tmplbo7xu6h.c: In function ‘main’:
/tmp/tmplbo7xu6h.c:12:5: warning: implicit declaration of function ‘getpgid’ [-Wimplicit-function-declaration]
     pgid = getpgid(0);
     ^


PING www.a.shifen.com (220.181.38.150) 56(84) bytes of data.
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=1 ttl=52 time=4.84 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=2 ttl=52 time=4.49 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=3 ttl=52 time=4.51 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=4 ttl=52 time=4.62 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=5 ttl=52 time=4.43 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=6 ttl=52 time=4.46 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=7 ttl=52 time=4.52 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=8 ttl=52 time=4.49 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=9 ttl=52 time=4.51 ms

守护进程

成为守护的步骤:

a. 调用fork()创建新进程
b. 结束父进程,使新进程脱离父进程,使init成为其父进程
c. 调用setsid()创建新的会话,脱离原来的会话
d. 调用chdir()将其工作目录修改为"/",脱离原来的工作目录
e. 关闭所有文件描述符
f. 打开文件描述符0, 1, 2, 并重定向到/dev/null
/*
 * 成为守护进程的过程
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>

int main(void) {
    int pid, i;

    pid = fork();
    if(pid == -1)
        return -1;
    else if(pid != 0)
        exit(EXIT_SUCCESS);

    /* create new session */
    if(setsid() == -1)
        return -1;

    /* set the working directory to the root directory */
    if(chdir("/") == -1)
        return -1;

    /* close all open files*/
    for(i=0; i<NR_OPEN; i++)
        close(i);

    /* redirect fd's 0, 1, 2, to /dve/null */
    open("/dev/null", O_RDWR); /* stdin */
    dup(0);                    /* stdout */
    dup(0);                    /* stderror */

    /* do its daemon thing */
    return 0;
}
/*
 * 直接调用 int daemon(int nochdir, int noclose); 创建守护进程,避免繁琐的创建过程
    nochdir为0时,修改工作目录为根目录
    noclose为0时,关闭所有文件描述符
 */ 

进程调度

image.png 普通进程与实时进程的调度策略完全不同, 优先级系统也不同:

a. 普通进程的调度策略是雨露均沾,不会因为优先级低而被饿死,优先级高低影响的是时间片的长短。优先级采用的是动态优先级nice[-20, 19]
b. 普通进程:拥有root权限的可以提高自己优先级,即降低nice值,非root用户只能降低自己优先级。
c. 普通进程默认nice值为0
d. 实时进程采用的是霸占模式,只有高优先级的进程运行完才轮得到低优先级的进程,当高优先级的进程进入就绪态,将毫不留情的抢占低优先级的CPU时间。

普通进程优先级

/*
 * 普通进程修改nice值: int nice(int inc);
 */

#include <unistd.h>
#include <stdio.h>

int main(void) {
    int ret;

    errno = 0;
    ret = nice(10);
    if(ret == -1 && errno != 0)
        perror("nice");
    else
        printf("nice value is %d\n", ret);
}
/*
 * 修改进程、进程组、用户优先级: int setpriority(int which, int who, int prio);
     which:
         PRIO_PROCESS
         PRIO_PGRP
         PRIO_USER
     who:
         0时:当前进程、进程组、用户
 * 获取优先级: int getpriority(int which, int who);
 */

#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>

int main() {
    int nice, ret;
    nice = getpriority(PRIO_PGRP, 0);
    printf("nice value is %d\n", nice);

    ret = setpriority(PRIO_PGRP, 0, 10);
    if(ret == -1)
        perror("setpriority");

    nice = getpriority(PRIO_PGRP, 0);
    printf("nice value is %d\n", nice);
}
/* 
 * 处理器亲和力
 * void CPU_SET(unsigned long cpu, cpu_set_t *set); 将set结构体中的#cpu置为1
 * void CPU_CLR(unsigned long cpu, cpu_set_t *set); 将set结构体中的#cpu置为0
 * int CPU_ISSET(unsigned long cpu, cpu_set_t *set); 判断#cpu是否为1, 是返回1, 否0
 * void CPU_ZERO(cpu_set_t *set); 将set结构体中的cpu全部置为0
 * int sched_setaffinity(pid_t pid, size_t setsize, const cpu_set_t *set) 获取进程pid可绑定的cpu存储于set中
 * int sched_setaffinity(pid_t pid, size_t setsize, cpu_set_t *set) 将set中的cpu与进程绑定
 */

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>

int main(void) {
    cpu_set_t set;
    int ret, i;
    CPU_ZERO(&set);
    ret = sched_getaffinity(0, sizeof(cpu_set_t), &set);
    if(ret == -1)
        perror("sched_getaffinity");

    for(i=0; i<CPU_SETSIZE; i++) {
        int cpu;
        cpu = CPU_ISSET(i, &set);
        printf("cpu=%i is %s\n", i, cpu?"set":"unset");
    }

    /* 设定亲和力*/
    CPU_ZERO(&set);
    CPU_SET(0, &set);
    CPU_CLR(1, &set);
    ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);
    if(ret == -1)
        perror("sched_setaffinity"); 

    for(i=0;i<CPU_SETSIZE; i++){
        int cpu;

        cpu = CPU_ISSET(i, &set);
        printf("cpu=%i is %s\n", i, cpu?"set":"unset");
    } 
}

实时进程

/*
 * int sched_getscheduler(int pid); 获取pid进程的调度策略
 */

#include <sched.h>
#include <stdio.h>

int main() {
    int police;

    /* 当前进程调度策略 */
    police = sched_getscheduler(0);

    switch(police) {
        case SCHED_OTHER:
            printf("Policy is normal\n");
            break;
        case SCHED_FIFO:
            printf("Policy is FIFO\n");
            break;
        case SCHED_RR:
            printf("Policy is Round-Robin\n");
            break;
        case -1:
            perror("sched_getscheduler");
            break;
        default:
            printf("Unknown Policy\n");        
    }
}
Policy is normal
/*
 * int sched_setscheduler(int pid, int policy, const struct sched_param *sp); 
 * 修改pid进程的调度策略
 */

#include <sched.h>
#include <stdio.h>

int main() {
    int police, ret;
    struct sched_param sp;

    /* 静态优先级 */
    sp.sched_priority = 2;

    /* 设定当前进程调度策略为 Round Robin */
    ret = sched_setscheduler(0, SCHED_RR, &sp);

    if(ret == -1) {
        perror("sched_setscheduler");
        return 1;
    }

    /* 当前进程调度策略 */
    police = sched_getscheduler(0);

    switch(police) {
        case SCHED_OTHER:
            printf("Policy is normal\n");
            break;
        case SCHED_FIFO:
            printf("Policy is FIFO\n");
            break;
        case SCHED_RR:
            printf("Policy is Round-Robin\n");
            break;
        case -1:
            perror("sched_getscheduler");
            break;
        default:
            printf("Unknown Policy\n");        
    }
}
sched_setscheduler: Operation not permitted
[C kernel] Executable exited with code 1
/*
 * int shced_getparam(int pid, struct sched_param *sp);
 * 获取pid进程的调度参数
 * int shced_setparam(int pid, const struct sched_param *sp);
 * 设定pid进程的调度参数
 */

#include <sched.h>
#include <stdio.h>

int main() {
    int ret;
    struct sched_param sp;

    /* 获取当前进程调度参数 */
    ret = sched_getparam(0, &sp);
    if(ret == -1) {
        perror("sched_getparam");
        return 1;
    }

    printf("Priority is: %d", sp.sched_priority);
}
Priority is: 0
/*
 * int sched_get_priority_min(int policy);
 * int sched_get_priority_max(int policy);
 * 获取调度策略的优先级范围
 */

#include <sched.h>
#include <stdio.h>

int main(void) {
    int priority_range[2];

    /* 获取Round-Robin调度策略的最小优先级 */
    priority_range[0] = sched_get_priority_min(SCHED_RR);
    if(priority_range[0] == -1) {
        perror("sched_get_priority_min");
        return 1;
    }

    /* 获取Round-Robin调度策略的最大优先级 */
    priority_range[1] = sched_get_priority_max(SCHED_RR);
    if(priority_range[1] == -1) {
        perror("sched_get_priority_max");
        return 1;
    }

    printf("SCHED_RR priority range is: %d - %d", priority_range[0], priority_range[1]);
}
SCHED_RR priority range is: 1 - 99

进程资源限制

Linux内核对进程所能获取的资源进行了限制,比如打开文件数目,内存页数,未处理的信号等。

/*
 * int getrlimit(int resource, struct rlimit *rlim);
 * 获取资源限制
 */

#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit rlim;
    int ret;

    /* 获取当前进程NOFILE限制 */
    ret = getrlimit(RLIMIT_NOFILE, &rlim);
    if(ret == -1) {
        perror("getrlimit");
        return 1;
    }

    printf("RLIMIT_NOFILE limits: soft=%d hard=%d\n",
          rlim.rlim_cur, rlim.rlim_max);
}
RLIMIT_NOFILE limits: soft=65535 hard=65535
/*
 * int setrlimit(int resource, const struct rlimit *rlim);
 * 设定资源限制
 */

#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit rlim;
    int ret;

    /* 设定core文件最大为32MB */
    rlim.rlim_cur = 32 * 1024 * 1024;
    rlim.rlim_max = RLIM_INFINITY;
    ret = setrlimit(RLIMIT_CORE, &rlim);
    if(ret == -1) {
        perror("setrlimit");
        return 1;
    }
}