UNIX环境高级编程 进程环境

 

 

相关函数列表

//退出函数
#include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>
void _exit(int status);

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

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

//动态分配空间的函数
#include <stdlib.h>
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 <stdlib.h>
char *getenv(const char *name);

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


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


//每个进程都有一组资源限制,可以用下列函数设置和获取
#include <sys/resource.h>
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终端宽度
DATEMSKgetdate模板文件路径名
HOMEhome起始目录
LANG本地名
LC_ALL本地名
LC_COLLATE本地排序名
LC_CTYPE本地字符分类名
LC_MESSAGES本地消息名
LC_MONETARY本地货币编辑名
LC_NUMERIC本地数字编辑名
LC_TIME本地日期/时间格式名
LINES终端高度
LOGNAME登陆名
MSGVERBfmtmsg处理的消息组成部分
NLSPATH消息类模板序列
PATH搜索可执行文件的路径前缀列表
PWD当前工作目录的绝对路径名
SHELL用户首选的shell名
TERM终端类型
TMPDIR在其中创建临时文件的目录路径名
TZ时区信息

 

 

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

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

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

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

限制说明
RLIMIT_AS进程总的可用存储空间的最大长度(字节),这影响到sbrk函数和mmap函数
RLIMIT_COREcore文件的最大字节数,若其值为0则组织创建core文件
RLIMIT_CPUCPU时间的最大量值(秒),当超过此软限制时,向该进程发送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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
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 <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#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 <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#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 <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

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 <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#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 <stdio.h>
#include <sys/resource.h>
#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程序是如何启动和终止

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值