跳转至

进程

  1. 一个终端等于一个进程
  2. 一个程序经常同时启动多个进程以并行多个任务。这就引入进程间通讯的概念
  3. 只要系统内存足够,理论上可用运行无数个进程

查看进程

  • PID:ProcessID,16 位正整数,默认 2~32768
  • 1 为特殊进程 init,负责管理其他进程
  • PID 自动按顺序分配

父进程

  • 父进程的进程号(PID)即为子进程的父进程号(PPID)
  • 用户可以通过调用 getppid() 函数来获得当前进程的父进程号
# 查看当前在系统上运行的所有进程(包括其他用户的)
ps -aux

# 以直观的方式查看父子进程的关系,没有的话 sudo apt install psmisc
pstree

进程与程序

  • 程序是静态文件,或称可执行文件
  • 进程是动态实体,是程序执行的具体实例。它包括了运行环境、CPU、外设等
  • 一个程序可用实例化很多个进程
  • 每个进程都有单独的地址空间

进程状态

20241027215329

进程创建

  1. system()
  2. 模拟调用 shell 终端运行一个程序
  3. 阻塞的,需要立刻返回在指令后面加 & 运行,ls &
  4. 返回结果不可预料
  5. fork()

20241027224031

exec 族函数

如果父子进程都一样的话,子进程能干的父进程也能干。为了让子进程做点不一样的事,exec 系列函数就诞生了。

执行完后,原进程除了进程号外,其他内容(内存空间、数据段、代码段、环境变量等)都被替换掉

脱胎换骨、覆盖的意思

int main(void)
{
    int err;

    printf("this is a execl function test demo!\n\n");

    // 必须以 NULL 结尾,效果等同于 ls -la
    err = execl("/bin/ls", "ls", "-la", NULL);

    // 因为替换了,所以一般不会返回,除非发生错误
    if (err < 0) {
        printf("execl fail!\n\n");
    }

    // 由于 exec 是替换,因此 Done 不会被执行
    printf("Done!\n\n");

    return 0;
}

exec 其他函数原型

int execl(const char *path, const char *arg, ...)

int execlp(const char *file, const char *arg, ...)

int execle(const char *path, const char *arg, ..., char *const envp[])

int execv(const char *path, char *const argv[])

int execvp(const char *file, char *const argv[])

int execve(const char *path, char *const argv[], char *const envp[])

进程终止

正常终止: 1. 从 main 返回 2. 调用 exit() 终止 3. 调用 _exit() 终止

20241027231500

异常终止: 1. 调用 abort() 终止 2. 系统信号终止

进程等待

当调用 exit() 后,进程变成了僵尸进程(无内存空间、没有可执行代码、不能被调度)。 无论如何,父进程都要回收这个僵尸进程。 调用 wait() 或者 waitpid() 函数其实就是将这些僵尸进程回收, 释放僵尸进程占有的内存空间,并且了解一下进程终止的状态信息。

fork & wait 需要成对出现

函数原型:

pid_t wait(int *wstatus);

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

代码示例:

int main(void)
{
    pid_t pid, child_pid;
    int status;

    pid = fork(); //(1)

    if (pid < 0)
    {
        printf("Error fork\n");
    }
    /*子进程*/
    else if (pid == 0)
    { //(2)

        printf("I am a child process!, my pid is %d!\n\n", getpid());

        /*子进程暂停 3s*/
        sleep(3);

        printf("I am about to quit the process!\n\n");

        /*子进程正常退出*/
        exit(0); //(3)
    }
    /*父进程*/
    else
    { //(4)

        /*调用 wait,父进程阻塞*/
        child_pid = wait(&status); //(5)

        /*若发现子进程退出,打印出相应情况*/
        if (child_pid == pid)
        {
            printf("Get exit child process id: %d\n", child_pid);
            printf("Get child exit status: %d\n\n", status);
        }
        else
        {
            printf("Some error occured.\n\n");
        }

        exit(0);
    }

    return 0;
}