操作系统之进程

    stdin    标准输入    0
    stdout    标准输出    1
    stderr    标准错误    2

进程

1.程序
    数据结构+算法=程序
    程序是存放在磁盘中的可执行文件。
    程序是静态的(指令的集合)
    
2.进程
    运行起来的程序称为进程    
    进程是独立的活动单位,是程序执行和资源管理的最小单位
    一个程序可以对应多个进程(即多次打开运行同一个软件,例如QQ飞车开小号刷任务)
    每个进程从启动开始就都有4G虚拟内存
    
3.进程号    
    每个Linux进程都有一个唯一的数字标识符,称为进程ID(PID)
    本质上就是一个非负的整数
    实例:
    ps -ef    //以全格式查看系统中所有的进程
    
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Feb04 ?        00:00:02 /sbin/init

    UID        用户名
    PID        进程号
    PPID    父进程的进程号
    C        占用CPU的百分比
    STIME    启动时间
    TTY        终端号(?表示没有控件终端)
    TIME    消耗CPU的时间
    CMD        进程名称
    
4.父子进程    
    如果进程A启动进程B,那么A称为父进程,B称为子进程
    Linux进程的启动顺序:
        进程0(系统进程)负责启动进程1(init进程)和2,其他所有的进程都1和2直接
        或间接启动,从而形成树形结构。
    获取父(PPID)子(PID)进程的进程号:
    #include <unistd.h>
    
    子进程: pid_t getpid(void);
    列如:printf("child process %d is running \n",getpid());
        //打印子进程的PID,表示进入子进程
    父进程:     pid_t getppid(void);
    列如:printf("parent process %d is running \n",getppid());
        //打印父进程的PPID,表示进入父进程
    
    ./a.out &    把a.out放到后台运行,不占用当前终端
    
5.    Linux进程地址空间布局
    程序的结构与进程的结构
    一个可执行程序包含三部分,用    size a.out    命令查看
   
   text       data        bss        dec        hex    filename
   1172        284          4       1460        5b4    a.out
    
    text    代码段:主要存放指令
    data    数据段:已初始化的全局或静态的变量
    bss    BSS段:未初始化的全局或静态变量,BSS段会在main()执行之前自动清0
    
    在执行程序时,系统会在内核中创建一个进程,
    为这个进程申请PCB(task_struct),用于管理整个进程的资源
    其中,mm_struct成员用来管理与当前进程相关的所有的内存资源
    
    32位平台下,【一个进程拥有4G虚拟地址空间】
        1.代码段、数据段、BSS段,这些直接从磁盘copy到内存
            其它代码段从0x08048000地址开始
        2.堆    通常在堆中进行动态内存分配    malloc系列
        3.栈    保存局部变量(包括函数参数),分配和回收都自动进行
        4.高地址1G空间供内核映射处理,用户程序不能直接访问
    
    readelf -a a.out 指令查看
    
6.    Linux虚拟内存
    在linux中,内存地址都是虚拟内存地址,不是物理内存
?    每个【进程一启动,就先天赋予0-4G的虚拟内存】,虚拟内存本质上是一个整数,
    这个整数通过内存映射对应一个物理内存的地址,但先天不对应任何的物理内存。
    虚拟内存自身存储不了任何数据,只有内存映射后才能存储数据,否则引发段错误
    
    0-3G是给用户使用的,称为用户空间
    3-4G是给内核使用的,称为内核空间
    
    用户空间的程序不能直接访问内核空间,但可以通过内核提供的系统函数进入内核
    空间。
    
进程使用步骤及函数:    
1.创建一个新进程
pid_t fork(void);
返回值:成功:给父进程返回创建的子进程的PID
                    给子进程返回0
            失败:返回-1,errno被设置    
例如:

pid_t pid = fork();
if(pid < 0)
{
    //创建失败
    perror("fork");
    exit(-1);
}    
else if(pid == 0)
{
    //子进程中
}    
else if(pid > 0)
{
    //父进程中
}    

说明:
    1.fork创建后,父子进程谁先执行不确定的,根据调度算法的不同,顺序可能有所不同。
    Linux自从2.6.x后,默认先执行父进程,但不能假设一直是这样的。
    
    2.使用fork创建子进程后,父子进程的执行方式:
        a.对于fork之前的代码,由父进程执行一次
        b.对于fork之后的代码,由父子进程各执行一次,即两次
        c.fork函数的返回值也是由父子进程各自返回一次
        
    3.fork之后,父子进程同时运行,
    如果子进程先结束,子进程会给父进程发一个信号,父进程负责回收子进程的资源。
    
    如果父进程先结束,子进程会变成孤儿进程,会认进程1(init)做新的父进程
    init进程称为孤儿院。
    
    如果子进程在发送信号时,出现了问题或父进程没有及时处理信号,子进程就会成为僵尸进程。    

2.终止进程
    #include <stdlib.h>
    void exit(int status);
        不会立即终止进程,可以调用atexit()注册过的函数之后,再结束
        要检查文件的打开情况,把文件的缓冲区中的内容写回文件,即清理IO缓冲
    例如:     exit(0);
    
        正常结束:
        1.在main函数中,执行return 0;
        2.调用exit(0)/_exit(0)
        
    非正常结束:
        在外力干涉下挂掉的,操作系统干掉的

/****执行注册过的函数之后,再结束进程***/
void func(void)
{
    printf("this is exit function\n");
}
int main()
{
    atexit(func);
    printf("this is main process\n");
    exit(0);
}
/*************
输出:this is main process
this is exit function    
*************/
    
    
/**********立即结束进程********/
void func(void)
{
    printf("this is exit function\n");
}
int main()
{
    atexit(func);
    printf("this is main process\n");
    _exit(0);
}
/************
输出:this is main process    
************/

int atexit(void (*function)(void));
    功能:用于注册参数指定的函数,该函数会在进程结束时被调用
    返回值:成功返回 0
            失败返回非0
    
    在main函数中,return与exit功能一样。
    return     用于函数返回
    exit    用于退出进程    
    
3.等待某进程先结束
    wait(&status)与waitpid(-1, &status, 0);等价
    #include <sys/types.h>
    #include <sys/wait.h>
    
    方法一: pid_t wait(int *status);
    用于挂起当前正在运行的进程,直到有一个子进程终止
    返回值:成功返回终止子进程的PID,失败返回-1 
    当参数不空时,会将获取到的状态信息存放到参数指定的空间,
       通过以下宏来获取相应信息
       WIFEXITED(status)--当正常结束时,返回true
       WEXITSTATUS(status)--获取子进程的退出状态
    例如:
 

int status;
pid_t res=wait(NULL);
if(res == -1)
{
   perror("wait");
   exit(-1);
}
if(WIFEXITED(status))//normal exit
//当正常结束时(即子进程结束),返回true
{
   printf("child pid = %d,normal exit,exit status = %d\n",res,WEXITSTATUS(status));
}
//挂起当前正在运行的进程,等待任意进程结束,并打印等待的结果【结束的进程的进程号或-1】
//若没有进程结束,则一直挂起

方法二:pid_t waitpid(pid_t pid, int *status, int options);
    用于按照【指定的方式】等待【指定的进程】状态发生改变
    pid:等待的进程号
        pid < -1     等待进程组ID为pid的绝对值的任意子进程(了解)
        pid == -1     等待任意子进程的结束(掌握)
        pid == 0        等待和当前进程组在同一个进程组的任意子进程(了解)
        pid > 0     等待进程号为pid的进程(掌握)
    *status:终止状态所存储的内存单元    
        如果不关心终止状态,可以将 statloc 参数设置为NULL。
        一般都设置为NULL;
        当&status不为 NULL 时,会将获取到的状态信息存放到参数指定的空间,
        通过以下宏来获取相应信息
        WIFEXITED(status)--当正常结束时,返回true
        WEXITSTATUS(status)--获取子进程的退出状态
        当&status为 NULL 时,【不关心终止状态】    
    options:等待的方式
        0            阻塞
        WNOHANG     如果没有子进程结束,立即返回
    
4.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 execvpe(const char *file, char *const argv[],
               char *const envp[]);
    
    exec函数说明:
    exec函数提供一个【在进程中启动另一个程序执行】的方法
    它可以【根据指定的文件或路径】【找到可执行文件】,并用它来【替换原调用进程的代码段、数据段、堆、栈】
    在【执行完成后】,原调用进程的内容,【除了进程PID号,其它全部被新的进程替换】。
    
    list
        ls -l a.txt NULL

    vector
        char *const argv[] = {"ls","-l","a.txt",NULL}; 

exec函数族:
    前4位    统一为exec
    第5位    l     参数传递以列表的方式,逐个列举    execl execlp execle
            v     参数传递以数组的形式            execv execvp execvpe
    第6位    e     可传递环境变量给新进程            execle    execvpe
            p     可执行文件查找方式为文件名        execlp    execvp    

    参数必须以NULL表示结束
实例:

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

int main()
{
    printf("hello world\n");
    execl("/bin/ls","ls","-l","a.out",NULL);
    printf("hello world 2\n");
    return 0;
}        
/************
输出结果:    
hello world
-rwxrwxrwx 1 root root 7330 Feb 10 20:36 a.out
************/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值