进程

进程

程序和进程

  1. 进程是程序运行的实例,程序是可执行的二进制代码文件,将文件加载到内存中运行就得到一个进程,同一个程序文件家在多次称为不同的进程
  2. 进程包括三个部分:代码段、数据段、堆栈段。
  • 代码段就是程序的代码数据,如果有多个进程运行同一个程序,则可以使用同一个代码段
  • 数据段存放程序的全局变量、常量和静态变量
  • 堆栈段中的栈用于函数调用,存放函数的参数、函数内部定义的局部变量。堆栈段还包括进程控制块(PCB)
  1. PCB处于进程核心堆栈的底部,PCB是进程存在的唯一标识
  2. 每个进程在系统中通过PID唯一的标识

进程的创建和结束

  1. 进程创建有两种方式:由操作系统创建、由父进程创建
  2. init()函数在内核态运行,是内核代码
  3. init进程是内核启动并运行的第一个用户进程,运行在用户态下
  4. init函数调用execve()从文件/etc/inittab中加载可执行程序init并执行,这个过程没有调用do_fork()

进程创建 fork()函数

1
2
3
4
5
#include <unistd.h>

pid_t fork(void);
//对于父进程,函数返回新创建的子进程的PID,对于子进程,创建成功则返回0
//创建失败返回-1
  1. 调用fork()函数的就是父进程,新创建的进程就是子进程
  2. 为新创建的子进程分配进程空间,将父进程的空间中的内容复制到子进程的进程空间,包括数据段和堆栈段,两者共用代码段
  3. getpid()获得当前进程的PID,getppid()获得父进程的PID

进程结束 exit()函数

1
2
3
4
5
#include <stdlib.h>

void exit(int status);
//status是一个整型状态值,表示进程的退出状态,保存在全局变量$?中
//$?保存main函数的返回值,或者程序运行中调用exit的status的值,或者异常出错的错误号
  1. exit()是一个函数,执行完成后将控制权交给系统,而return完成后将控制权交给调用函数
  2. exit是正常终止,abort是异常终止
  3. exit在stdlib.h中声明,_exit在unistd.h中声明
  4. exit参数为0表示进程正常终止,如果是其他值则表示执行过程中有错误发生
  5. _exit在执行后立即返回给内核,会关闭所有的文件描述符,清理内存以及其他一些内核清理函数,但是不会刷新流。exit()要先执行一些清除操作,然后才将控制权交给内核,是在_exit函数基础上的一个封装,会在调用_exit函数之前先刷新流数据,有利于将缓冲区的数据正确写入文件,如printf()函数就是一个使用缓冲IO的方式

僵尸进程

  1. 孤儿进程是指,父进程退出后,有子进程还在运行,这些子进程就是孤儿进程,这些进程将会被init进程(进程号PID=1)的进程收养
  2. 僵尸进程,是指进程使用fork创建紫禁城,如果子进程退出,而父进程没有调用wait或者waitpid获取子进程的状态信息,则子进程的进程描述符仍然保存在系统中,这种进程称为僵尸进程
  3. 孤儿进程是子进程未退出,父进程已退出。僵尸进程是子进程已退出,父进程未退出
  4. 调用wait函数,可以自动分析当前进程的某个子进程已经退出,wait可以回收子进程,销毁后返回,如果没有这样的子进程wait就会一直阻塞在这里,直到有一个出现
    1
    2
    3
    4
    5
    6
    7
    #include <sys/tyoes.h>
    #include <sys/wait.h>

    pid_t wait(int *status);
    //wait会暂时停止目前进程的执行明知道有信号或者子进程结束
    /*如果在调用wait时已经有子进程结束,则wait会立即返回子进程结束状态值,子进程结束状态值由参数status返回,子进程的进程识别码也会一起返回,如果不需要结束状态值,则参数status可以设为NULL*/
    //执行成功则返回子进程识别码PID,如果错误返回-1,并将错误原因存在errno
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main() {
pid_t pid=fork();

if(pid<0)
{
perror("fork error\n");
return 0;
}
else if(pid>0)
{
printf("Parent process\n");
int status=-1;
pid_t pr=wait(&status); //等待子进程?
if(WIFEXITED(status))
{
//sleep(10);
printf("the child process %d exit normally.\n", pr);
printf("the return code is %d.\n", WEXITSTATUS(status));
}
else
{
printf("the child process %d exit abnormally.\n", pr);
}
}
else if(pid==0)
{
printf("Sub-process, PID: %u, PPID: %u\n", getpid(), getppid());
exit(3); //WIFEXITSTATUS(status)返回5
}
return 0;
}

  1. waitpid是wait的封装,多出两个可由用户控制的参数pid和options
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <sys/types.h>
    #include <sys/wait.h>

    pid_t waitpid(pid_t pid, int *status, int options);
    //暂时停止目前进程的执行,直到有信号来到或子进程结束
    //pid是等待的子进程的识别码,pid<-1等待进程组识别码为pid绝对值的任何子进程
    //pid=-1等待任何子进程,相当于wait
    //pid=0等待进程组识别码与目前进程相同的任何子进程
    //pid>0等待任何子进程识别码为pid的子进程
    //options=WNOHANG及时没有子进程退出,立即返回
    //options=WUNTRACED,子进程进入暂停则马上返回,结束状态不予理会
    //正常返回时返回收集到的子进程的进程ID
    //如果设置了WNOHANG,调用中waitpid发现没有已退出的子进程可手机则返回0
    //如果调用中出错,则返回-1,errno被设置成相应的值指示错误
    //pid指示的子进程不存在,若进程存在,但不是调用进程的子进程,waitpid就会出错返回,errno被设置成ECHILD?

守护进程

  1. 守护进程是脱离终端并且在后台运行的进程
  2. 创建简单的守护进程:
  • 创建子进程,父进程退出
  • 在子进程中创建新会话,使用系统函数setsid创建一个新会话,并担任会话组组长setsid()
  • 改当前目录为根目录chdir()
  • 重设文件权限位掩码为0,用umask(0)
  • 关闭文件描述符
WhitneyLu wechat
Contact me by scanning my public WeChat QR code
0%