7.2 main函数

1.C程序总是从main函数开始执行的,原型:int main(int argc,char *argv[]);
argc是命令行参数的个数
argc是指向参数的各个指针所构成的数组
2.内核执行C程序时,在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程作为程序的起始地址。启动例程从内核取得命令行参数和环境变量值,然后为按照上述方式调用main函数做好安排。(这是由连接编辑器设置的,而连接编辑器则由C编译器调用)
启动例程有点像这样子:
exit(main(argc, argv));

7.3 进程终止
有8中方式使进程终止,其中5种是正常终止,分别是:
1)从main返回
2)调用exit
3)调用_exit或Exit
4)最后一个线程从其启动例程返回
5)从最后一个线程调用pthread_exit
异常终止有三种:
1)调用abort
2)接到一个信号
3)最后一个线程对取消请求做出相应

1.退出函数
3个函数用来正常终止一个程序:_exit和_Exit立即进入内核,exit会先执行一些清理处理,然后返回内核。
这3个函数都带一个整型参数,称为终止状态(或退出状态,exit status)。可以通过shell检查进程终止的状态。
下面三种情况进程终止状态是未定义的:
a.调用这写函数时不带终止状态
b.main函数执行了一个无返回值的return语句
c.main没有声明返回类型为整型
特殊情况:若main的返回类型是整型,并且main执行到最后一条语句时返回,那么进程是终止状态是0.

2.atexit函数
这个函数可以用来登记函数进给进程。登记的函数将由exit自动调用。
按照ISO C的规定,一个进程可以登记多至32个函数。先登记的后调用,同一函数登记多次则会被调用多次。
原型:
#include<stdlib.h>
int atexit(void(fun*)(void));  // 调用时传一个函数指针就可以了。

7.4 命令行参数

7.5 环境表
每个程序都接收到一张环境表。跟参数列表一样,环境表也是一个字符指针数组。其中每个指针包含一个以null结束的C字符串的地址。
全局变量environ则包含了该指针数组的地址。
打印环境表:

extern char** environ;
void printEnv()
{
char **env = environ;
while(*env)
{
printf("%s\n", *env);
env++;
}
}

7.6 C程序的存储空间布局(这个是重点)
历史沿袭至今,C程序一直由下列几部分组成:
地址由低到高:
正文段(代码段):由CPU执行的机器指令部分。通常,正文段是可共享的。存放的有:代码、const全局变量、const静态变量、字符串字面值
数据段(已经初始化了):包含了程序中明确的赋初值的变量。例如:C程序任何函数之外的声明。
BSS段(未初始化数据段):未初始化的全局变量和静态变量
堆区:用来动态分配的内存,malloc出来的东西(由低向高扩散)
栈区:自动变量以及每次函数调用时所需保存的信息保存在此段中。(由高向低扩散)。主要有非静态的普通局部变量,函数参数,函数返回值,匿名变量。

命令行参数和环境变量:
可以用size查看正文段、数据段、bss段的长度(以字节为单位):

xcy@xcy-virtual-machine:~/test/unix$ sizea.out
text data bss dec hex filename
9ef a.out
xcy@xcy-virtual-machine:~/test/unix$

第4列表示十进制的3段总和,第5列表示十六进制的三段总和。

7.7 共享库

共享库使得可执行文件中不再需要包含公用的库函数,而只需要在所有进程都可引用的存储区中保存这种库例程的一个副本。
程序第一次执行或第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。
这减少了每个可执行文件的长度,但是增加了一些运行时间开销。这种时间开销发生在该程序第一次被执行时。
还有一个优点:可以用库函数的新版本代替老版本而不需要对使用该库的程序重新连接编辑。(这么一看有点像动态库)
在不同的系统中,程序可能使用不同的方法说明是否要使用共享库。
例如:

xcy@xcy-virtual-machine:~/test/unix$ gcc -static hello.c   // 阻止gcc使用共享库
xcy@xcy-virtual-machine:~/test/unix$ size a.out
text data bss dec hex filename
c3827 a.out
xcy@xcy-virtual-machine:~/test/unix$ gcc hello.c // gcc 默认使用共享库,可以看到正文和数据段的长度明显减小。
xcy@xcy-virtual-machine:~/test/unix$ size a.out
text data bss dec hex filename
9ef a.out
xcy@xcy-virtual-machine:~/test/unix$

7.8 存储空间分配

1)malloc:分配指定字节数的存储区。次存储区中的初始值不确定
2)calloc:为指定数量指定长度的对象分配存储空间。该空间中的每一位(bit)都初始化为0
3)realloc:增加或减少以前分配区长度。当增加长度时,可能需要将以前分配区的内容移到另一个足够大的区域,以便在尾端提供增加的存储区,而新增区域内的初始值是不确定的。
这三个分配函数所返回的指针一定是适当对齐的,使其可以用于任何数据对象。返回值都是void*

7.9 环境变量

环境变量字符串的形式是: name=value
UNIX 内核并不查看这些字符串,它们的解释完全取决于各个应用程序。
ISO C定义了一个函数getenv。可以用其取环境变量值。
还可以设置环境变量,比如改变环境变量、增加新的环境变量。

#include<stdlib.h>

int putenv(char *str);   //取形式为name=value的字符串,将其放到环境表中。若name已经存在,则会删除之前的定义。

int setenv(const char *name, const char*value, int rewrite); // 设置环境变量。若name存在,rewrite表示是否删除现有的定义。

int unsetenv(const char *name); // 删除name的定义,即使不存在也不算出错。

注意:环境表和环境字符串通常占用的是进程地址空间的顶部,所以它不能在向高地址扩展了;同时也不能移动在它之下的各栈帧,所以也不能向低地址方向扩展。
那么是如何实现上述操作的呢?(这个也需要理解)
)删除:比较简单,先在环境表中找到改指针,然后将所有后续指针都向环境表首部顺次移动一个位置。
)修改:分两种情况
a:若新value的长度小于等于现有value的长度,那么就直接复制到原来的空间中就好了
b:假如新value更长,就需要先malloc为新字符串分配空间,然后将新字符串复制到该空间中,接着使环境表中的针对name的指针指向新分配区。
)增加:先调用malloc为name=value字符串分配空间。接着:
a:如果是第一次增加一个新的name,就需要调用malloc为新的指针表分配空间。接着将原来的环境表分配到新的分配区,并将指向新name=value字符串的指针存放在改指针表的末尾,然后又存放一个null指针在最后。最后使environ指向新的指针表
b:如果不是第一次增加name,就知道已经用malloc在对中为环境表中分配了空间,就只要调用realloc,以分配比原空间多存放一个指针的空间,然后将指向新的name=value字符串的指针存放在该表尾,后面接一个空指针。

7.10 函数setjmp和longjmp
这两个函数可以实现跨越函数类型的跳转。
栈帧:每个被调用的函数在栈里都有自己的函数栈。每个函数栈就叫栈帧。(这个是我自己的理解)
这两个函数不是普通的C语言goto语句在一个函数内实施的跳转,而是在栈上调用若干调用帧,返回到当前函数调用路径上的某一函数。
详细的东西没有仔细看。有人看我再写这节的内容。

静态变量,程序在启动的时候,便为该变量分配了内存空间,程序中用extern和static关键标志,程序一开始执行的时候就已经存在了,但是不等于它们在整个程序中可用。
动态变量,也叫自动存储变量。c++把变量默认为自动存储。用static说明的局部变量只能在定义该变量的函数体中使用。不过与自动变量不同的是,static静态变量在第一次使用时进行初始化(默认初始值为0)。
函数退出时,系统保持该变量的值和存储空间。然后你下次调用这个函数时,static变量还是上次退出函数时的值。

7.11 函数getrlimit和setrlimit
每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改
      #include <sys/time.h>
       #include <sys/resource.h>
      int getrlimit(int resource, struct rlimit *rlim);
      int setrlimit(int resource, const struct rlimit *rlim);
在更改资源限制时,须遵循下列三条规则:
1)任何一个进程都可以将一个软限制值更改为小于或等于其硬限制值
2)任何一个进程都可降低其硬限制值,但是它必须大于等于其软限制值。这种降低对于普通用户而言是不可逆的。
3)只有超级用户进程才能提高硬限制值
下面的函数可以打印出各个限制,具体限制的定义就不列出来了:

#include<stdio.h>
#include<sys/resource.h>
#include"comm.h" // 自定义头文件,内容见下面
/*
#ifndef __COMM_H__
#define __COMM_H__
#include<errno.h>
#include<stdlib.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
#endif // __COMM_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) < )
ERR_EXIT("getrlimit");
printf("%-14s ", name);
if(limit.rlim_cur == RLIM_INFINITY)
{
printf("(infinite) ");
}
else
{
lim = limit.rlim_cur;
printf("%10lld ", lim);
}
if(limit.rlim_max == RLIM_INFINITY)
{
printf("(indinite) ");
}
else
{
lim = limit.rlim_max;
printf("%10lld", lim);
}
putchar((int)'\n');
}
int main()
{
#ifdef RLIMIT_AS
doit(RLIMIT_AS); // 进程总的可用存储空间的最大长度(字节)
#endif
doit(RLIMIT_CORE); // core文件的最大字节数。为0表示阻止创建core文件
doit(RLIMIT_CPU); // CPU时间的最大量值(秒)。超过此软限制时,向该进程发送SIGXCPU信号
doit(RLIMIT_DATA); // 数据段的最大字节长度
doit(RLIMIT_FSIZE); // 可用创建的文件的最大字节长度
#ifdef RLIMIT_MEMLOCK
doit(RLIMIT_MEMLOCK); // 一个进程使用mlock(2)能够锁定在存储空间中的最大字节长度
#endif
#ifdef RLIMIT_MSGQUEUE
doit(RLIMIT_MSGQUEUE); // 进程为POSIX消息队列可分配的最大存储字节数
#endif
#ifdef RLIMIT_NICE
doit(RLIMIT_NICE); // 影响进程的调度优先级
#endif
doit(RLIMIT_NOFILE); // 每个进程能打开的最多文件数
#ifdef RLIMIT_NPROC
doit(RLIMIT_NPROC); // 每个实际用户ID可拥有的最大子进程数
#endif
#ifdef RLIMIT_NPTS
doit(RLIMIT_NPTS); // 用户可同时打开的伪终端的最大数量
#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 // RLIMIT_AS的同义词
doit(RLIMIT_VMEM);
#endif
exit();
}

在虚拟机中运行:乌班图14.04,64位的结果如下:

xcy@xcy-virtual-machine:~/test/unix$ ./a.out
RLIMIT_AS (infinite) (indinite)
RLIMIT_CORE (infinite) (indinite)
RLIMIT_CPU (infinite) (indinite)
RLIMIT_DATA (infinite) (indinite)
RLIMIT_FSIZE (infinite) (indinite)
RLIMIT_MEMLOCK
RLIMIT_MSGQUEUE
RLIMIT_NICE
RLIMIT_NOFILE
RLIMIT_NPROC
RLIMIT_RSS (infinite) (indinite)
RLIMIT_SIGPENDING
RLIMIT_STACK (indinite)
xcy@xcy-virtual-machine:~/test/unix$

《UNIX环境高级编程》第七章进程环境的更多相关文章

  1. UNIX环境高级编程 第7章 进程环境

    本章涉及C/C++程序中main函数是如何被调用的.命令行参数如何传递给main函数.程序的内存空间布局.程序如何使用环境变量.程序如何终止退出. main函数 C程序或C++程序总是从main函数开 ...

  2. UNIX环境高级编程 第8章 进程控制

    本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...

  3. UNIX环境高级编程 第9章 进程关系

    在第8章学习了进程的控制原语,通过各种进程原语可以对进程进行控制,包括新建进程.执行新程序.终止进程等.在使用fork( )产生新进程后,就出现了进程父子进程的概念,这是进程间的关系.本章更加详细地说 ...

  4. 《UNIX环境高级编程》(APUE) 笔记第七章 - 进程环境

    7 - 进程环境 Github 地址 1. main 函数 C 程序总是从 main 函数 开始执行: int main(int argc, char *argv[]); \(argc\) 为命令行参 ...

  5. Unix编程第7章 进程环境

    准备雄心勃勃的看完APUE,但是总感觉看着看着就像进入一本字典,很多地方都是介绍函数的用法的,但是给出例子远不及函数介绍的多.而且这本书还是个大部头呢.第7章的讲的进程环境,进程是程序设计中一个比较重 ...

  6. UNIX环境高级编程 第13章 守护进程

    守护进程daemon是一种生存周期很长的进程.它们通常在系统引导时启动,在系统关闭时终止.守护进程是没有终端的,它们一直在后台运行. 守护进程的特征 在Linux系统中,可以通过命令 ps -efj ...

  7. UNIX环境高级编程 第16章 网络IPC:套接字

    上一章(15章)中介绍了UNIX系统所提供的多种经典进程间通信机制(IPC):管道PIPE.命名管道FIFO.消息队列Message Queue.信号量Semaphore.共享内存Shared Mem ...

  8. UNIX环境高级编程 第6章 系统数据文件和信息

    UNIX系统的正常运作需要用到大量与系统有关的数据文件,例如系统用户账号.用户密码.用户组等文件.出于历史原因,这些数据文件都是ASCII文本文件,并且使用标准I/O库函数来读取. 口令文件 /etc ...

  9. UNIX环境高级编程 第5章 标准I/O库

    本章是关于C语言标准I/O库的,之所以在UNIX类系统的编程中会介绍C语言标准库,主要是因为UNIX和C之间具有密不可分的关系.由于UNIX系统存在很多实现,而每个实现都有自己的标准I/O库,为了统一 ...

随机推荐

  1. 自动化运维工具——puppet详解(一)

    一.puppet 介绍 1.puppet是什么 puppet是一个IT基础设施自动化管理工具,它能够帮助系统管理员管理基础设施的整个生命周期: 供应(provisioning).配置(configur ...

  2. 2.python的文件类型、变量数值和字符串练习

    1.python的文件类型 .源代码 -python 源代码文件以"py"为扩展名,由python程序解释,不需要编译. 2.字节代码(编译的) -python源码文件经编译后生成 ...

  3. Golang中的信号处理

    信号类型 个平台的信号定义或许有些不同.下面列出了POSIX中定义的信号. Linux 使用34-64信号用作实时系统中. 命令 man 7 signal 提供了官方的信号介绍. 在POSIX.1-1 ...

  4. Python 点滴 IV

    [继承示意图] 类是实例的工厂, OOP就是在树中搜索属性,类事实上就是变量名与函数打成的包 . 每一个class语句会生成一个新的类对象 . 每次类调用时,就会生成一个新的实例对象 . 实例自己主动 ...

  5. SharePoint 2013 引发类型为“System.ArgumentException”的异常。 參数名: encodedValue

    SharePoint 2013 引发类型为"System.ArgumentException"的异常. 參数名: encodedValue 具体错误信息 说明: 运行当前 Web ...

  6. iOS 从应用程序跳转到评价界面

    1,跳转到App Store: NSString *str = [NSString stringWithFormat:@"http://itunes.apple.com/us/app/id% ...

  7. 关于Mac终端故障一直出现 [进程已完毕]

    终端已打开就出现以下信息.无法输入不论什么的命令 Last login: Mon Aug 18 10:00:36 on ttys000 [进程已完毕] 原因:不知谁改动了 终端->偏好设置-&g ...

  8. 巧用CAS解决数据一致性问题

    缘起:在高并发的分布式环境下,对于数据的查询与修改容易引发一致性问题,本文将分享一种非常简单但有效的优化方法. 一.业务场景 业务场景为,购买商品的过程要对余额进行查询与修改,大致的业务流程如下: ( ...

  9. 查询linux版本命令

    [环境] Ubuntu [本文命令记录] uname -a lsb_release cat /etc/issue cat /proc/version [截图效果] (1)uname -a (2)lsb ...

  10. Intellij IDEA 安装和配置jrebel进行项目的热部署

    Jrebel 先介绍一下jrebel,jrebel是可以热部署项目的一个工具,更改代码自动部署并不需要重启项目(在spring中的controller中,增加.修改方法都是可以进行热部署而不需要重启的 ...