首页 > 代码库 > 0724------Linux基础----------进程1

0724------Linux基础----------进程1

1.进程的基本概念

  1.1操作系统有三大抽象概念

    a)进程:程序的执行过程;

    b)文件: IO;

    c)虚拟内存:可用的地址空间;

  1.2 进程在内核中是一种数据结构 task_struct(定义见/usr/src/linux-headers-3.8.0-29/include/linux中1240行起)。

  1.3 进程由 PCB,代码段以及数据段组成,其中PCB位于内核空间,也就是上述的tsak_struct结构。

  1.4 虚拟内存的前面一部分为内核空间(1G),后面是用户空间。

  1.5 进程的运行模式有用户态内核态,以 read 为例,如果接受1000个字节,那么需要进行系统调用,由内核去接收 1000个字节,然后再返回用户态运行 read 的时候,把数据拷贝到用户空间

  1.6 用户态到内核态有两种方式,一是系统调用,触发trap指令陷入内核,二是中断,二者的区别在于前者是自愿的,后者是被动的。

2.进程的状态

  2.1 进程的三种经典状态:

    a)就绪:准备完毕,随时等待调度;

    b)运行:占有CPU;

    c)阻塞:等待某一事件的发生。

  2.2 三种状态之间的转化

    a)就绪到运行:被CPU调度;

    b)运行到阻塞:执行了IO 等需要等待的系统调用,例如read;

    c)阻塞到就绪:等待的事件来临,例如read所等待的fd中有数据可读;

    d)运行到就绪:时间片到期或者被抢占

    e)这里不存在阻塞到运行:因为调度器总是从就绪队列里面挑选进程。

3.僵尸进程和孤儿进程

  3.1 什么是僵尸进程?子进程退出,而父进程没有对其进行回收。这里注意:僵尸进程占用的不是用户空间的资源,子进程运行过程中申请的资源已经全部被回收,占用的是内核中的某些结构,如 PCB,它主要是留给父进程做参考,以便于父进程获取子进程的运行状态。

  3.2 僵尸进程程序示例。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>/* * 僵尸进程 */int main(int argc, const char *argv[]){    pid_t pid;    if((pid = fork()) == 0){        printf("in child, pid = %d, parent = %d\n", getpid(), getppid());    }    else{        sleep(20);        printf("in parent, child = %d, pid = %d\n ", pid, getpid());    }    return 0;}

 

  3.3 孤儿进程:父进程先退出,子进程托管给init进程。

  3.4 孤儿进程示例。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>/** 孤儿进程*/int main(int argc, const char *argv[]){pid_t pid;if((pid = fork()) == 0){printf("in child, pid = %d, parent = %d\n", getpid(), getppid());sleep(10);printf("in child, pid = %d, parent = %d\n", getpid(), getppid());}else{sleep(1);printf("in parent, child = %d, pid = %d\n ", pid, getpid());}return 0;}

 

4.fork和vfork函数

  4.1 利用fork产生的父子进程,地址空间是独立的。因为在传统的UNIX模型中,fork将父进程的地址空间复制了一份给子进程。

  4.2 fork程序示例。这里父子进程不共享全局变量,二者是独立的。

#include <stdio.h>#include <stdlib.h>#include <string.h>#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)/* * 父子进程的地址空间相互独立 * */int g_val = 10;int main(int argc, const char *argv[]){    pid_t  pid;    pid = fork();    if(pid == -1){        ERR_EXIT("fork");    }    else if(pid == 0){        sleep(3);        printf("in child ,g_val = %d\n", g_val);    }    else{        g_val++;        printf("in parent, g_val = %d\n", g_val);    }    waitpid(-1, NULL, 0);    return 0;}

  4.3 vfork 在产生子进程的时候,没有复制地址空间而是与父进程共享。vfork的目的就是为了在子进程中实行exec替换。程序示例如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)/* * vfork */int g_val = 10;int main(int argc, const char *argv[]){    pid_t  pid;    pid = vfork();    if(pid == -1){        ERR_EXIT("fork");    }    else if(pid > 0){        sleep(3);        printf("in parent ,g_val = %d\n", g_val);    }    else{        g_val++;        printf("in child, g_val = %d\n", g_val);        exit(EXIT_SUCCESS); //若不加 子进程会一直在父进程空间中执行    }    return 0;}

5.atexit函数

  5.1 atexit 的作用是向系统注册一些函数,这些函数在程序退出的时候被调用,并且这些函数被调用的顺序与注册的顺序相反。程序示例如下。

#include <stdio.h>#include <stdlib.h>#include <string.h>void test1(){    printf("test1...\n");}void test2(){    printf("test2...\n");}void test3(){    printf("test3...\n");}int main(int argc, const char *argv[]){    atexit(test1);    atexit(test2);    atexit(test3);    printf("before return \n");    return 0;}  

  5.2 exit 和 _exit 的区别

    a)exit 会清空 IO 缓冲区,后者不会;

    b)exit 会处理通过 atexit 注册的函数。