UNIX环境高级编程 进程环境

 

 

相关函数列表

//退出函数
#include 
void exit(int status);
void _Exit(int status);
#include 
void _exit(int status);

//按照ISO C的规定,一个进程可以登记多至32个函数,这些函数将由exit自动
//调用。我们称这些函数为终止处理程序(exit handler),并调用atexit函数来
//登记这些函数,这个函数起始就是注册退出钩子函数,退出程序时执行一些自定义的函数
//执行的顺序和注册的顺序正好相关,相同的函数可以注册多次
#include 
int atexit(void (*func)(void));

//环境表
int main(int argc, char *argv[], char *envp[]);

//动态分配空间的函数
#include 
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
void free(void *ptr);


//获取环境变量值
#include 
char *getenv(const char *name);

//putenv设置name=value
//setenv将name设置为value,如果rewrite非0则先删除之前的定义否则不删
//unsetenv删除name定义
#include 
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);


//C语言中goto是不能跨越函数的,而执行这种功能是由setjmp和longjmp完成
#include 
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);


//每个进程都有一组资源限制,可以用下列函数设置和获取
#include 
int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);

//rlimit结构体如下
struct rlimit {
    rlim_t rlim_cur;    //soft limit
    rlim_t rlim_max;    //hard limit
};

 

 

进程终止,有8种方式,其中5钟是正常终止

1) 从main返回

2) 调用exit

3) 调用_exit或_Exit

4) 最后一个现场从其启动列程返回

5) 从最后一个线程调用pthread_exit

异常终止

1) 调用abort

2) 接到一个信号

3) 最后一个线程对取消请求作出响应

 

1999 ISO C扩展要求编译器要求main必须声明为返回整型

当内核执行C程序时,在调用main前先调用一个特殊的启动例程。可执行文件将此例程指定为程序的起始地址--这是由连接编辑器设置的,而连接编辑器则由C编译器调用。启动例程从内核取得命令行参数和环境变量值,然后喂按上述方式调用main函数做好安排。

一个C程序是如何启动和终止的


 

函数sbrk()用来实现系统扩充(或缩小)进程的堆

但是大多数malloc和free的实现都不见效进程的存储空间,释放的空间可供以后再分配,但将它们保持在

malloc池中而不繁华给内核。

大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息--分配块的长度,指向

下一个分配块的指针等。这就意味着如果超过一个已分配区的尾端或者在已分配区起始位置之前进行写操作,则会改写另一块管理记录信息。这种类型的错误是在灾难性的单不会很快暴露出来。

 

 

替代的存储空间分配程序

1) libmalloc

2) vmalloc

3) quick-fit

4) jemalloc

5) TCMalloc

6) 函数alloca

 

Single UNIX Specification定义的环境变量

变量 说明
COLUMNS 终端宽度
DATEMSK getdate模板文件路径名
HOME home起始目录
LANG 本地名
LC_ALL 本地名
LC_COLLATE 本地排序名
LC_CTYPE 本地字符分类名
LC_MESSAGES 本地消息名
LC_MONETARY 本地货币编辑名
LC_NUMERIC 本地数字编辑名
LC_TIME 本地日期/时间格式名
LINES 终端高度
LOGNAME 登陆名
MSGVERB fmtmsg处理的消息组成部分
NLSPATH 消息类模板序列
PATH 搜索可执行文件的路径前缀列表
PWD 当前工作目录的绝对路径名
SHELL 用户首选的shell名
TERM 终端类型
TMPDIR 在其中创建临时文件的目录路径名
TZ 时区信息

 

 

在更改资源限制时,必须遵守下列三条规则(使用ulimit命令获取和设置)

1) 任何一个进程都可将一个软限制值更改为小于或等于其硬限制值

2) 任何一个进程都可降低其硬限制值,但它必须大于或等于其软限制值,这种降低对普通用户是不可逆的

3) 只有超级用户进程可以提高硬限制值

限制 说明
RLIMIT_AS 进程总的可用存储空间的最大长度(字节),这影响到sbrk函数和mmap函数
RLIMIT_CORE core文件的最大字节数,若其值为0则组织创建core文件
RLIMIT_CPU CPU时间的最大量值(秒),当超过此软限制时,向该进程发送SIGXCPU信号
RLIMIT_DATA 数据段的最大字节长度
RLIMIT_FSIZE 可以创建的文件的最大字节长度,当超过此软限制时,则向该进程发送SIGXFSZ信号
RLIMIT_MEMLOCK 一个进程使用mlock能够锁定在存储空间中的最大字节长度
RLIMIT_MSGQUEUE 进程为POSIX消息队列可分配的最大存储字节数
RLIMIT_NICE 为了影响进程的调度优先级,nice值可设置的最大限制
RLIMIT_NOFILE

每个进程能打开的最多文件数,更改此限制将影响到sysconf函数在参数

_SC_OPEN_MAX中的返回值

RLIMIT_NPROC

每个实际用户ID可拥有的最大子进程数,更改此限制将影响到sysconf函数在参数

_SC_CHILD_MAXZ中的返回值

RLIMIT_NPTS 用户可用时打开的伪终端的最大数量
RLIMIT_RSS

最大驻内存集字节长度(resident set size in bytes RSS)如果可用的物理存储器非常

少,则内核将从进程处取回超过RSS的部分

RLIMIT_SBSIZE 在任意给定时刻,一个用户可以占用的套接字缓冲区的最大长度(字节)
RLIMIT_SIGPENDING 一个进程可排队的信号最大数量,这个限制是sigqueue函数实施的
RLIMIT_STACK 栈的最大字节长度
RLIMIT_SWAP 用户可消耗的交换空间的最大字节数
RLIMIT_VMEM 这是RLIMIT_AS的同义词
RLIMIT_INFINITY 指定了一个无限量的限制

 

 

 

atexit函数使用过程

#include 
#include 
#include 
static void my_exit1(void) {
        printf("first exit handler\n");
}

static void my_exit2(void) {
        printf("second exit handler\n");
}

int main(int argc, char *argv[]) {
        if(atexit(my_exit2) != 0) {
                printf("can't register my_exit2");
        }
        if(atexit(my_exit1) != 0) {
                printf("can't register my_exit1");
        }
        if(atexit(my_exit1) != 0) {
                printf("can't register my_exit1");
        }
        printf("main is done\n");
        return 0;
}

 

 

gcc关闭共享库

gcc -static hello.c
关闭后可以发现目标文件明显比之前大了很多

 

 

 

假设一个函数程序如下

这个程序调用顺序是main --> do_line --> cmd_add --> get_token

如果在get_token()函数中发生了错误,函数需要跳回main中,此时我们不得不坚持返回值然后一个个返回

#include 
#include 
#include 
#define TOK_ADD 5
#define MAXLINE 1024
char *tok_ptr;

int get_token(void) {
        //fetch next token from line pointed to by tok_ptr
}

void cmd_add(void) {
        int token;
        token = get_token();
        //rest of processing for this command
}

void do_line(char *ptr) {
        int cmd;
        tok_ptr = ptr;
        while((cmd=get_token()) > 0) {
                switch(cmd) {
                        case TOK_ADD:
                            cmd_add();
                            break;
                }
        }
}


int main(int argc, char *argv[]) {
        char line[MAXLINE];
        if(fgets(line,MAXLINE,stdin) != NULL) {
                do_line(line);
        }

        return 0;
}

 

用setjmp和longjmp函数可以跳过函数返回

这里需要定义个全局的jmp_buf结构体,也就是setjmp()函数的env

longjmp可以对应多个setjmp()函数,通过第二个参数val区分,比如cmd_add对应1,而get_token对应2

这样就可以知道是哪个函数返回的了

#include 
#include 
#include 
#define TOK_ADD 5
#define MAXLINE 1024
char *tok_ptr;
jmp_buf jmpbuffer;

int get_token(void) {
        //fetch next token from line pointed to by tok_ptr
}

void cmd_add(void) {
        int token;
        token = get_token();
        if(token < 0) {
                longjmp(jmpbuffer,1);
        }
}

void do_line(char *ptr) {
        int cmd;
        tok_ptr = ptr;
        while((cmd=get_token()) > 0) {
                switch(cmd) {
                        case TOK_ADD:
                            cmd_add();
                            break;
                }
        }
}

int main(int argc, char *argv[]) {
        char line[MAXLINE];
        if(setjmp(jmpbuffer) != 0) {
                printf("setjmp error\r\n");
        }
        if(fgets(line,MAXLINE,stdin) != NULL) {
                do_line(line);
        }

        return 0;
}

 

 

执行跳转后各种类型的变量情况

自动,静态,寄存器,全局,易变 的变量是否会随着函数的回滚而回滚

目前测试是不会,所有变量都没有回滚,书中说到对于全部优化情况下,寄存器变量,易变变量会回滚

#include 
#include 
#include 

static jmp_buf jmpbuffer;
static int globval;

static void f2(void) {
        longjmp(jmpbuffer, 1);
}

static void f1(int i, int j, int k, int m) {
        printf("int f1()\n");
        printf("globval=%d, atuoval=%d, regival=%d, valaval=%d, statval=%d\n",globval,i,j,k,m);
}

int main(int argc, char *argv[]) {
        int autoval;
        register int regival;
        volatile int volaval;
        static int statval;

        globval = 1;
        autoval = 2;
        regival = 3;
        volaval = 4;
        statval = 5;

        if(setjmp(jmpbuffer) != 0) {
                printf("after jmp\n");
                printf("globval=%d, atuoval=%d, regival=%d, valaval=%d, statval=%d\n",globval,autoval,regival,volaval,statval);
                exit(0);
        }
        globval = 95;
        autoval = 96;
        regival = 97;
        volaval = 98;
        statval = 99;
        f1(autoval, regival, volaval, statval);
        exit(0);
}

 

 

自动变量需要注意返回值

这里的函数open_data返回值的内容是当前函数栈中的,所以返回了就没有了,应当定义成静态的或者通过

malloc函数动态申请的

#include 
#include 
#include 
#include 
#include 
#define BUFSIZE 1024
FILE *open_data(void) {
        FILE *fp;
        char databuf[BUFSIZE];
        if((fp=fopen("datafile","r")) == NULL) {
                return(NULL);
        }
        if(setvbuf(fp,databuf,_IOLBF,BUFSIZE) != 0) {
                return (NULL);
        }
        return fp;
}

int main(int argc, char *argv[]) {
        FILE *fp = open_data();
}

 

 

获取当前系统的各种软限制和硬限制

#include 
#include 
#define doit(name) pr_limits(#name, name);

static void pr_limits(char *name, int resource) {
        struct rlimit limit;
        unsigned long long lim;
        if(getrlimit(resource, &limit) < 0) {
                printf("getrlimit error for %s,",name);
        }
        printf("%-14s  ",name);

        if(limit.rlim_cur == RLIM_INFINITY) {
                printf(" (infinite) ");
        } else {
                printf("%10lld ",lim);
        }

        if(limit.rlim_max == RLIM_INFINITY) {
                printf(" (infinite) ");
        } else {
                printf("%10lld",lim);
        }
        putchar((int)'\n');
}

int main(int argc, char *argv[]) {
#ifdef RLIMIT_AS
        doit(RLIMIT_AS);
#endif

        doit(RLIMIT_CORE);
        doit(RLIMIT_CPU);
        doit(RLIMIT_DATA);
        doit(RLIMIT_FSIZE);

#ifdef  RLIMIT_MEMLOCK
        doit(RLIMIT_MEMLOCK);
#endif

#ifdef RLIMIT_MSGQUEUE
        doit(RLIMIT_MSGQUEUE);
#endif

#ifdef RLIMIT_NICE
        doit(RLIMIT_NICE);
#endif

        doit(RLIMIT_NOFILE);

#ifdef RLIMIT_nproc
        doit(RLIMIT_NPROC);
#endif

#ifdef RLIMIT_RSS
        doit(RLIMIT_RSS);
#endif

#ifdef RLIMIT_SBSIZE
        doit(RLIMIT_SBSIZE);
#endif

#ifdef RLIMIT_SIGPENDING
        doit(RLIMIT_SIGPENDING);
#endif

        doit(RLIMIT_STACK);

#ifdef RLIMIT_SWAP
        doit(RLIMIT_SWAP);
#endif

#ifdef RLIMIT_VMEM
        doit(RLIMIT_VMEM);
#endif
        return 0;
}

 

 

 

参考

Linux下C程序进程地址空间布局

gcc 编译共享库

c程序是如何启动和终止

 

 

 

 

 

 

你可能感兴趣的:(C和C++,学习笔记,shell,c/c++,操作系统)