UNIX_C 高级编程<二>

     环境表的概念和使用

         基本概念:

              环境表本事就是环境变量的集合,每个进程拥有一张独立的环境表信息,用于记录该进程相关的环境信息   

              环境表本质就是一个以空指针结尾的字符指针数组,其中每个指针指向一个格式为“变量名=变量值”

         +的字符串,该字符指针数组的首地址保存在全局变量char **environ中,通过该全局变量可以访问所有环境变量;

 

     相关的基本操作函数

 

         #include<stdlib.h>

        char* buf = getenv(const char *name);

 

              返回:查找成功返回对应的变量值,没有查找到相同的环境变量名返回NULL;

              功能:主要用于根据参数指定的环境变量名去搜索整个环境表;

             

    

 

           #include <stdlib.h>

           int setenv(const char *name, const char*value, int overwrite);

 

              参 1:字符串形式的环境变量名

              参 2:字符串形式的环境变量值

              参 3:是否修改的标志0:不修改 !0:表示修改

 

              返回:成功:0  失败:-1

              功能:主要用于修改/增加一个环境变量,如果环境变量不存在则增加,如果环境变量已经存在

                       +则是否修改取决于第三个参数overwrite;

    

           int unsetenv(const char *name);

 

              功能:主要用于删除参数指定的环境变量,如果该环境变量不存在则函数调用成,函数表不发生改变

 

             

          intunsetenv(const char *name);

    

              返回:成功:0  失败:!0

              功能:主要用于增加/修改环境变量到环境表中,如果不存在则增加,如果存在则修改,其中参数的格式为:name=value;

 

         intclearenv(void); 


             

              返回:成功:0  失败:!0

              功能:清空环境表信息,并且将记录环境变量首地址的全局变量environ也置为空指针

 

    

main函数的原型

         intmain( int argc, char * argv[], char * envp[] )

        

              参 1:记录命令行参数的个数

              参 2:字符指针数组,用于记录每个命令行参数的地址信息

              参 3:用于记录当前进程的环境表信息

 

              注意:由于历史等原因,main函数中的第三个参数不一定被所有系统支持,因此建议使用全局变量environ来访问环境表

 

**   内存管理技术

 

         程序:存放在磁盘/硬盘上的可执行文件

         进程:运行在内存中的程序

    

         同一个程序可以同时对应多个进程

 

     进程中的内存划分

***

***     代码区(Text)     ——主要用于存放功能代码,函数指针指向该区域

***       (代码区)

***     只读常量区(Text) ——主要存放字符串常量,字面值,const修饰的已经初始化的全局变量

***                                             const修饰的已经初始化的static局部变量

***

***     全局区(Data)     ——用于存放已经初始化的全局变量和已经初始化的static局部变量

***     (数据区)

***     BSS段(Data)     ——用于存放没有初始花的全局变量和没有初始化的static局部变量

***                     ——该区域会在main函数执行之前自动清零

***

***     堆区(heap)       ——主要使用malloc()/calloc()/realloc()/free()函数所操作的内存区域

***                     ——该区域的内存空间由程序员手动申请和释放的

***

***     桟区(Stack)      ——主要存放非静态的局部变量

***                     ——该区域的内存空间由系统自动管理


        

const int num1 = 3; //桟区

         //strs指向栈区,strs本身在栈区

         charstrs[] = "hello";

 

         预留空间 —— 共享库/共享内存等信息

 

                                     --------->   预留空间    <-------       |当前环境表信息

      代码区--只读常量区--全局区--BSS段--堆区---堆区----------> 桟区---桟区----   |命令行参数信息

 低-------------------------------------------------------------------------------------------------->高

     0                                                            3G-1

        

 

                                         

     不同字符串存放形式的比较(重点)

       

        对于记录常量字符串的字符指针来说,指针指向的字符串内容不可以改变,但是指针的指向可以改变

       

        对于记录常量字符串的字符数组来说,指针指向的字符穿内容可以改变,但是指针的指向不可以改变

 

        对于记录动态内存的指针来说,指针指向的内容和指针的指向都可以改变;



 8     // &pc 表示pc本身的地址,桟区	
	 9     // pc 表示pc指向 只读常量区
	10     // 将字符串"hello"首地址放在pc的内存空间中
	11     char * pc = "hello";
	12 
	13     // 将字符串"hello"复制一份放到ps内存空间内
	14     // &ps 表示ps本身的地址 桟区
	15     // ps  表示ps的指向 桟区
	16     char  ps[] = "hello";
	17 
	18     printf("pc = %p, &pc = %p \n", pc, &pc);
	19     printf("ps = %p, &ps = %p \n", ps,  &ps);
	20 
	21     // 试图修改指针的指向
	22     pc = "1234";    // OK
	23     //ps = "1234";  // ERROR
	24 
	25     // 试图修改指针所指向的内容
	26     //strcpy(pc, "GooD");   //ERROR     
	27     strcpy(pc, "Good"); //OK
	28 
	29     printf("--------------------------------\n");
	30 
	31     pc = (char * ) malloc( sizeof(char) * 10 );
	32     strcpy(pc, "hello");
	33     char * pt = pc;
	34     // 试图修改指向指向 ok
	35     pc = "world";
	36     free(pt);
	37     pt = NULL;

虚拟内存管理技术

        

         在linux系统中,采用虚拟内存管理技术对内存管理,即每个进程都可以拥有0~4G-1的地址

     +空间(虚拟的,并不是真实存在的), 由操作系统负责建立虚拟地址到真实物理内存/文件

     +的映射,因此不同进程中的地址空间看起来是一样的,但是所对应的真实物理内存/文件是不一样的    

                  

         其中0~3G-1之间的地址空间叫做用户空间,3G~4G-1之间的地址空间叫做内核空间,用户程序

     +一般都运行在用户空间,不能直接访问内核空间,不过系统内核提供了相关的函数访问内核空间;

        

         内存地址的基本单位基本单位是字节,而内存映射的基本大为是内存页,目前主流的操作系统

     +内存页的大小是4kb(4096个字节)

 

段错误的由来

 

    试图操作没有操作权限的内存空间时可能引发段错误;

    试图使用没有经过映射的虚拟地址时可能引发段错误;   

 

使用malloc申请动态内存

 

     使用malloc申请动态内存时的注意事项:

    

         使用malloc函数申请动态内存时,除了申请参数指定的存储区空间之外,还可能申请额外的12个

         +字节(一般性原则),用于存放该动态内存的管理信息,比如:大小,是否空闲等信息;

 

         因此以后使用malloc申请的动态的内存时,切记不要进行越界赋值,以避免对动态内存管理信息

         +的破坏,从而避免错误的发生

    

     使用malloc申请动态内存的一般性原则:

        

         一般来说,当使用malloc函数申请比较小块的动态内存时,操作系统可能一次性映射33个内存页大

         +小的内存空间,用来提高效率;

              #include<unistd.h>

              #include<sys/types.h>

              getpid() —— 主要用于获取当前进程的编号

             

节点编号、进程的名称等;

 

         cat /proc/进程编号/mapss —— 表示查看指定进程的内存映射情况,查看结果包含以下6列:

地址范围、访问权限、偏移量、设备编号、i

     使用free函数释放动态内存的一般性原则:

 

         一般来说,使用malloc函数申请比较大块的动态内存时,系统可能会映射比33个内存页还多一些

         +的内存空间,而使用free函数释放动态内存时,释放多少则从映射的总量中减去多少,当所有的

         +动态内存释放完毕时,系统还会保留33个内存页,用来提高效率;              

                  

     内存管理相关函数:

 

         #include <unistd.h>

              int getpagesize(void)  —— 主要用于获取当前系统中一个内存页的大小并返回

 

              void *sbrk(intptr_tincrement);

 

                   功能:主要用于根据参数指定的来调整动态内存的大小,具体调整方式如果下:

 

                       increment >0:表示申请动态内存,成功返回申请到的新内存的起始地址;

                       increment = 0:表示获取当前所用动态内存的末尾地址

                       increment <0:表示释放动态内存,成功返回释放之前的末尾地址;

                  

                       无论increment的取值如何,失败都是返回(void *)-1;

 

                   注意:一般来说,使用sbrk函数申请比较小块的动态内存时操作系统可能会一次映射

                       +1个内存页大小的内存空间,当申请的动态内存超过1个内存页时,系统会再次映

                       +射1个内存页大小的内存空间,当所有的动态内存都释放时,系统不会保留映射

                       +的内存空间,因此相对malloc函数相比,效率比较低,但是比较节省内存空间;

                      

                       sbrk函数虽然能申请内存,也能释放内存,但是使用sbrk函数申请内存空间更加方便

 

              int brk( void * addr );

 

                   功能:主要用于根据参数指定的位置调整动态内存的大小,具体的调整方式如下:

                            当addr > 动态内存原来的末尾位置时,表示申请动态内存

                            当addr = 动态内存原来的末尾位置时,表示动态内存不变

                            当addr < 动态内存原来的末尾位置时,表示释放动态内存

                  

                   注意:虽然brk函数即能申请动态内存,也能释放动态内存,但是释放动态内存更加方便,

                       +因此一般情况下与sbrk函数搭配使用,sbrk函数申请内存,brk释放内存


s

         #include <sys/mman.h>  (暂时了解)

              void * addr=mmap(void*addr,size_t length,int prot,int flags,int fd,off_t offset);

 

                   参 1:映射的起始地址   实参给NULL表示由系统内核来指定起始位置

                   参 2:映射的大小      

                   参 3:映射的访问权限,不能和文件打开模式冲突

                                               PROT_EXEC—— 可执行

                                               PROT_READ—— 可读

                                               PROT_WRITE—— 可写

                                               PROT_NONE—— 不可访问

 

                   参 4:映射的操作标志

                                               MAP_SHARED    —— 共享的,映射区数据直接反应到文件

                                               MAP_PRIVATE   —— 似有的,映射区数据不会反应到文件

                   参 5:文件描述符

                   参 6:文件的偏移量

                  

                   返回:成功返回映射的其实地址,失败返回MAP_FAILED,本质就是(void *)-1;

                   功能:主要用于建立文件/设备到虚拟地址的映射;

 

              int munmap(void *addr, size_t length);

                  

                   参 1:映射的其实地址,mmap函数的返回值

                   参 2:映射的长度

             

                   功能:主要用于解除文件/设备到虚拟地址的映色

 

     内存管理函数之间的层次关系

           

         标准c语言 —— 使用malloc函数申请,free函数释放内存

              |       

         POSIX标准 —— 使用sbrk函数申请,brk函数释放

              |

                   操作系统  —— 使用mmap函数建立,munmap函数接触映射  


文件的管理

        

         在linux系统中,几乎把所有的一切都看做文件,包括目录,输入输出设备

 

         /dev/null —— 空设备文件

        

         eg:  cat /dev/null > a.txt

              清空a.txt文件里的原来的内容

    

文件IO操作(重点)

 

     open\read\write\lseek\close\——不带缓冲IO(每read或write一次都会调用内核中的一个系统调用)为了提高效率最好是自己定义缓冲区最佳给一个页(4096 byte)查看当前用户页大小函数( getpagesize() )

    

     每个进程都有一个文件描述符总表(in the system kernel)

     每个文件描述符总表对应多个文件描述符,每个文件描述符对应一个文件表

     文件描述符(非负整数):  标准输入:0

                            标准输出:1

                            标准错误:2

                               ......

                               OPEN_MAX(256)  范围:0 ~OPEN_MAX-1

 

     #include <fcntl.h>

     #include <sys/types.h>

     #include <sys/stat.h>

 

         int fd = open ( const char *path, int flag );               —— 打开或创建一个文件

         int fd = open ( const char * path, int flag, mode_t mode ); —— 打开或创建一个文件

         int creat(const char *pathname,mode_t mode);                —— 只创建不读写

 

参 2:        O_RDONLY —— 只读

O_WRONLY —— 只写

*    O_RDWR   —— 读写    三必选一

 

*    O_CREAT  —— 如果文件不存在则创建新文件,存在则打开

*    O_TRUNC —— O_CREAT+O_TRUNC,打开文件时自动清空文件

O_EXCL   —— O_CREAT+O_EXCT,创建文件,文件存在创建失败返回-1,不存在,则创建

**   O_APPEND —— 每次追加到文件末尾,用此方法打开则lseek位置偏移量失效

    

                   参 3: 只有在O_CREAT参数在创建新文件时有用 + 0664, 其它情况忽略

 

                   返回:   成功 —— 描述符  失败——-1

                   描述:   主要用于打开/创建一个文件/设备

 

     #include <unistd>

 

         ssize_t res = read (int fd, void * buf, size_t count);

        

                   返回:成功 ——读到的字节数,若已到达文件尾,返回0;失败 —— -1        

                   参 3: 为buf内存大小 sizeof(buf );

                      

         ssize_t res = write (int fd, const void * buf, size_t count)   

 

                   返回:成功 ——真实写入的字节数,什么都没写入,返回0;失败 —— -1      

                   参 3:为buf的真实使用大小strlen( buf );

 

         int close ( int fd );    成功:0 失败:-1

                  

         off_t currpos = lseek ( int fd, off_t offset, int whence );

        

                   成功:成功返回当前位置距离到文件开头位置的偏移量             SEEK_SET

                   失败:(off_t) -1                                            SEEK_CUR

                                                                               SEEK_END

                   注意:从SEEK_SET偏移-2个距离会报错

                         从SEEK_END向后偏移是可以的,当把文件的都写位置调整到SEEK_END之后时还是可以写入数据的,

只是中间有一块区域空闲,该现象叫做文件的空洞现象,该区域会被计算到文件的大小之中,但是没有有效数据,获取内容时得到的是'\0';

 

         获取一个文件大小的方式有两种:

              a.使用fseek调整到文件末尾,调用ftell函数

              b.使用lseek调整到文件末尾,此事函数返回值就是文件的大小




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值