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 注册的函数。

 

0724------Linux基础----------进程1,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。