首页 > 代码库 > 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 注册的函数。