本来这个进程/线程/协程是在9月份看完了,可惜9月份堕落了很就,就推到了11月份,为什么不10月份呢?因为国庆要回家,总不能拿这么厚的书回家把,所以就选了一本nginx,比较薄的书,接下来一段时间内,我们专供进程/线程/协程、
1.1 main函数
我们从开始学c语言开始就一直写main函数,我们也知道c程序总是从main函数开始执行,main函数的原型是:
int main(int argc, char *argv[])
其中argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组。
说实话,我大学那时候写单片机的时候,一直都以为main函数的参数是void的,哎,直到后来学习了linux应用编程之后,才发现main函数原来还有这么多不知道的东西。
当内核执行C程序时,在调用main前先调用一个特殊的启动历程。可执行程序文件将此启动历程的起始地址–这是由链接器设置的,而链接器则由C编译器调用,启动历程从内核取到命令行参数和环境变量值,然后那上述的main函数传参。
1.2 进程终止
有8种方式使进程终止,其中5种为正常终止,他们是:
-
从main返回(return)
-
调用exit(先执行一些清理,然后返回内核)
-
调用_exit或_Exit
-
最后一个线程从其启动历程返回
-
从最后一个线程调用pthread_exit
异常终止有3种方式,他们是:
-
调用abort
-
接收到一个信号
-
最后一个线程对取消请求作出响应
1.2.1 退出状态
退出函数都是可以带上参数的,这个参数就是这个进程退出的状态
比如exit(0)或者return 0;进程返回的状态都是0;
1.2.2 终止处理程序
一个进程在退出的时候,是可以登记多至32个函数,这些函数将又exit自动调用。我们称这些函数为终止处理程序。并调用atexit函数来登记这些函数。
int atexit(void (*func)(void))
注意:只要调用了exit这个退出,才会执行终止处理程序,并清理。
1.3 环境表
每一个程序都接收到一张环境表,与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址:
extern char **environ;
可以用一张图来描述环境表,一个environ指向了指针数组,数组中存储了字符串指针
1.4 C程序的存储空间布局
说实话,上面的那些内容我都懂了,其实就是想看看这个空间布局,不过前面的就当过一遍。
C程序一直由下列几部分组成:
- 正文段。这是有CPU执行的机器指令部分。
- 初始化数据段。通常将此段称为数据段,它包含了程序中需明确地赋初值的变量。
- 未初始化数据段。通常将此段称为bss段,在程序开始执行之前,内核将此段中的数据初始化为0或空指针。
- 栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。
- 堆。通常在堆中进行动态存储分配。
下面 看看图
a.out中还有若干其他类型的段,如包含符号表的段、包含调试信息的段以及包含动态共享库连接表的段等。这些部分并不装载到进程执行的程序影像中。
可以用size命令读取程序中的各段信息:
des和hex分别以十进制和十六进制表示的3段总长度。
1.5 存储空间分配
我们经常用的3个用于存储空间动态分配的函数。
void *maloc(size_t size); //分配指定字节数的存储区
void *calloc(size_t nobj, size_t size); //为指定数量指定长度的对象分配存储空间
void *realloc(void *pt, size_t newsize); //增加或减少以前分配区的长度
void free(void *ptr); //释放ptr指向的存储空间
1.5.1 替代的存储空间分配程序
- libmalloc
- vmalloc
- quick-fit
- jemalloc
- TCMalloc
- 函数alloca
1.6 环境变量
环境变量的操作函数:
char *getenv(const cahr *name); //获取环境变量的值
int putenv(char *str); //将其放到环境表中
int setenv(const char *name, const char ^value, int rewrite); //设置环境变量,rewrite先删除在写
int unsetenv(const char *name); //删除name定义
1.6.1 删除环境变量
删除一个环境变量挺简单的,只要先在环境表中找到该指针,然后将所有后续指针都向环境表首部顺次移动一个位置,这样就可以把要删除的变量,删除掉。
1.6.2 修改环境变量
- 如果新value的长度少于或等于现有value的长度,则只需要将新字符串复制到原字符串所在的空间
- 如果新value的长度大于原长度,则必须malloc申请空间,然后将新字符串复制到新空间中,然后环境表中的指针这个环境的指针指向新分配区。
1.6.3 增加环境变量
- 如果是第一次增加一个新name,需要为新的指针表分配空间,(因为原来的空间已经固定好了),接着将原来环境表的值全部复制到新的指针表中,并将直线新的字符串的指针存放在该指针表的最后,并在表尾有增加一个NULL指针。最后使environ指向新指针表。
- 如果不是第一次增加name,则可以知道已经调用了malloc分配好了环境表,所以只需要调用realloc,以分配比源空间多存放一个指针的空间。然后把将这个新指针赋值到最后,最后再添加一个NULL。
1.7 进程资源
每一个进程都有一组资源限制,其中一些可以用下面两个函数修改或者查询
int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);
函数以后用到在看,有支持shell命令的:
root@ubuntu:~/net# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3741
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 3741
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
这一节,比较简单,就是熟悉一些进程环境。