`
阅读更多

C程序的内存分配图(APUE)


一个进程地址空间中包含的内容(也称为Core Image):

1.文本段(Text Segment,只读,程序的代码),为了节省空间,同一个程序的多个进程共享该段;

2.数据段(Data Segment),即全局变量段,细分为初始化的和未初始化的数据,APUE上没有提到静态变量,静态变量应该也是存放在这里

3.栈,所有的函数的局部变量,形参,函数调用的返回地址等等都保存在这里

4.堆,即用malloc,realloc,free函数来管理的动态内存

实际的程序,例如a.out这个二进制代码中还包含了其他的信息,例如,符号表(symbol table),Debugging信息,链接表(dynamic shared libraries)等等,程序中的这些信息在执行的时候不被调入内存的进程空间中。


Static修饰符在C语言中的作用(即静态全局/全局,静态局部/局部变量之间的区别)

把局部变量改变为静态变量后是改变了它的存储方式,那么此时静态局部变量应该存储在数据段(即改变了它的生存期)。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。

全局变量和静态全局变量,两者在存储方式上并无不同。区别在于当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
Static

1.      静态变量,分配在静态存储区,在数据段中。函数退出之后,变量值不变。

2.      作用域,全局的静态变量、静态函数只能在本文件中使用。(不同于一般全局变量)局部的静态变量同函数的局部变量


fork()函数创建子进程

当调用fork函数创建一个子进程的时,Core Image除了文本段之外的其他部分都被子进程复制(并且自此相互独立),父子进程分别从Fork处继续执行。因此程序中的全局变量也是不共享的。

fork的两个最典型的应用:

1.一个进程想要复制一个子进程,从而两个进程可以并发执行不同的代码段,例如网络服务器

2.一个进程想执行一个不同的程序,例如Shell,调用Fork之后再调用Exec


如果父进程先于子进程终止,那么该子进程会的父进程会被内核设置为1(即init Process),之后当子进程终止时,他的信息会自动被init清理

如果子进程先于父进程终止,那么该子进程会称为僵尸进程,即他在内存中的Image 已被清理,但是内核的进程控制表中仍然保留了诸如Exit Status,进程ID之类的信息,等待父进程调用Wait或者WaitPid清理


子进程从父进程继承已经打开的文件指针(一般这些内容在内核的进程控制表PCB中),二者指向同一文件,在网络服务器中,常用的一段代码如下

pid_t pid;

int listenfd,connfd;

listenfd = Socket();

Bind();

Listen()

for(;;){

    connfd = Accept();

if(  (pid = fork()) == 0) {

        close(listenfd);//子进程关闭监听Socket(但这并不会导致父进程的监听Socket也被关闭)

        doSomething(listenfd);

        Close(connfd);

         exit(0);

}

Close(connfd);//父进程关闭连接的Socket(但这并不会导致子进程的连接的Socket也被关闭)

原因是内核对打开的Socket 维护一个Reference Count,只有当Count值为零的时候才会真正关闭

如果父子进程需要共享变量(典型的一个应用如利用多进程实现的聊天室服务器,服务器端父进程负责监听,每来一个客户就创建一个子进程负责与之通信,为了能将客户端的信息发给其他客户,服务器端需要维护一个所有用户的列表,而该列表需要被这些进程共享)可以使用共享变量,或者也可以改用多线程实现,或者使用 Select函数进行I/O的多路复用。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics