Linux系统与进程有关的系统调用

本文主要展示了如下系统调用的使用说明,并做了演示程序。

  • getpid
  • getppid
  • fork
  • waitpid
  • exec

文章目录

一、进程相关的系统调用

1.查询进程的ID

  • getpid返回当前进程的pid,getppid则返回当前进程父进程的pid。

  • pid_t实质上是int类型

  • 头文件以及函数声明如下:

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

pid_t getpid(void);
pid_t getppid(void);

2.创建进程

  • 存在两个返回值。
    • 在父进程中返回值大于0,返回子进程ID
    • 在子进程中返回值等于零
  • 如果创建失败,返回-1。
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
  • 父进程与子进程运行在分开的内存地址空间中,对数据的写操作不会互相影响。

Memory writes, file mappings (mmap(2)), and unmappings (munmap(2)) performed by one of the processes do not affect the other.

3.进程资源回收

这两个系统调用可以在子进程结束后回收子进程占用的全部资源,防止僵尸进程的产生。

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

pid_t wait(int *wstatus);
  • 调用wait函数的进程会被挂起(阻塞),直到其任意一个子进程结束。
  • 当子进程结束时,该函数会回收子进程所占的资源(PCB等),并返回被回收的子进程的id。将被回收子进程的状态通过指针参数返回。
  • 如果没有运行的子进程,则函数立刻返回-1.

对于waitpid函数,使用更为灵活

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

pid_t waitpid(pid_t pid, int *wstatus, int options);
  • 参数pid可以指定要回收的子进程的进程号id
    • pid = -1: 表示要回收任意子进程
    • pid = 0: 回收当前进程同一进程组中的任意一个进程
    • pid > 0: 回收进程号为pid的子进程
  • 参数options可以选择在回收时是否阻塞当前进程:
    • 0: 阻塞(等价于wait)
    • WNOHANG:不会阻塞,立即返回。

4.进程退出

#include <stdlib.h>

void exit(int status);

二、 exec()函数族

exec()函数族会去在当前进程中调用另一个进程,执行新的进程的指令。

#include <unistd.h>
extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
  • 调用失败才会有返回值,返回-1。

The exec() functions return only if an error has occurred. The return value is -1, and errno is set to indicate the error.

  • 调用成功会使用新进程的指令覆盖当前进程,故当前进程剩余代码不执行。
  • 因此一般是在子进程中使用。先使用fork创建子进程,在子进程中使用exec()函数族函数,从而使得子进程执行我们想要执行的可执行文件。
  • excel()会执行path指定的可执行文件,但是excelp则会到环境变量下寻找file指定的可执行文件文件名。

二、示例程序

1.示例程序1

  • 下面的程序,当前进程会创建五个子进程(注意break语句,是为了防止子进程创建新的进程)。
  • 每一个子进程都会输出自身与父进程的进程号ip,之后进程结束。
  • 当前进程在创建完五个子进程后,会进入死循环不断输出自身的进程号id。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    
    pid_t pid;
    for(int i = 0; i < 5; i++){
        pid = fork();
        if(pid == 0){
            break;
        }
        sleep(1);
    }
    if(pid > 0){
        while(1){
            printf("父进程,pid:%d\n", getpid());
            sleep(1);
        }
    }
    else{
        printf("子进程,pid:%d, ppid:%d\n", getpid(), getppid());
    }

    return 0;
}
  • 可以预想到,子进程执行结束,其资源被释放,但是PCB等信息还保存着,于是五个子进程成为僵尸进程。占用进程号等系统资源。
  • 程序输出如下图:
    Linux系统与进程有关的系统调用
  • 我们使用 ps -aux 命令,查看当前系统中的进程。

Linux系统与进程有关的系统调用

  • 可以发现,创建的五个进程均成为僵尸进程。
  • 于是启发我们,需要让主进程手动回收子进程资源。

2.示例程序2

  • 示例程序1的基础上,增加了waitpid系统调用,可以主动回收子进程。
  • 在子进程执行的函数中加入了Sleep,为了让每一个子进程sleep的时间不同,使用进程号作为sleep的参数。
  • 当子进程sleep结束后,父进程会使用不带阻塞的watpid系统调用回收资源并打印消息。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
    pid_t pid;
    for(int i = 0; i < 5; i++){
        pid = fork();
        if(pid == 0){
            break;
        }
        sleep(1);
    }
    if(pid > 0){
        while(1){
            printf("父进程,pid:%d\n", getpid());
            int ret = waitpid(-1, NULL, WNOHANG);
            if(ret > 0)
                printf("回收了进程%d\n", ret);
            sleep(1);
        }
    }
    else{
        printf("子进程,pid:%d, ppid:%d\n", getpid(), getppid());
        sleep(getpid() % 10 + 3);
        exit(0);
    }
    return 0;
}
  • 最终的结果如下图:
    Linux系统与进程有关的系统调用
  • 可以看出,waitpid是不会阻塞当前进程的。
上一篇:11、Redis的配置文件


下一篇:MySQL提示:The server quit without updating PID file问题的解决办法