进程基础

main函数
int main(int argc, char **argv)

C程序一般是从main函数开始执行,当内核执行C程序时,在调用main函数前会先调用一个特殊的启动例程,可执行程序文件将此启动例程指定为程序的起始地址(由链接器设定),启动例程从内核取得命令行参数和环境变量,为调用main函数做好准备。

C程序的存储空间布局
C程序的内存布局.png

C程序一直由下列几部分组成:

  • 正文段 text
    由cpu执行的机器指令部分,通常正文段是只读可共享的。
  • 初始化数据段 data
    存放初始化的全局变量和静态变量
  • 未初始化数据段 bss
    存放未初始化的全局变量和静态变量,在程序开始执行之前,内核将此段中的数据初始化为0或者空指针。

  • 存放自动变量,向下生长,地址变小。

  • 动态存储分配,向上生长,地址变大

存放在磁盘上的只有正文段和初始化数据段。(size命令可以查看)

进程环境变量
环境表.png

每个程序都会有一张环境表,全局变量environ包括该指针数组的地址。环境表通常放在进程存储的顶部

extern char **environ;

环境字符串形式: name=value

#include <stdlib.h>

int putenv(char *str)
    函数返回值:成功返回0,出错返回非0

int setenv(const char* name, const char* value, int rewrite);
    函数返回值:成功返回0,出错返回非0

int unsetenv(const char* name);
    函数返回值:成功返回0,出错返回非0
  • 删除一个环境变量很简单,只要在环境表中找到相应环境字符串指针,将后续指针向前移动一个即可。
  • 如果修改一个环境变量:
    • 如果新的value长度小于或等于现有value长度,则只需复制即可
    • 如果新的value长度大于原长度。则必须调用malloc为新字符串分配空间
进程标识

每一个进程都有一个非负整型pid_t表示的唯一进程ID。ID为0通常是调度进程,ID为1通常是 init进程,在自举过程中,此进程在自举内核后启动一个UNIX系统。

#include <unistd.h>

pid_t getpid()
    返回值:调用进程的进程ID
pid_t getppid()
    返回值:调用进程的父进程ID

uid_t getuid()
    返回值:调用进程的实际用户ID
uid_t getuid()
    返回值:调用进程的有效用户ID

gid_t getgid()
    返回值:调用进程的实际组ID
gid_t getegid()
    返回值:调用进程的有效组ID
进程创建
#include <unistd.h>

pid_t fork(void)
    返回值:子进程返回0,父进程返回子进程ID,若出错,返回-1。

子进程是父进程的副本,子进程获得父进程的数据空间,堆和栈的副本。父子进程共享正文段。

  • 写时复制

  • fork失败有2个主要原因:

    • 系统中已经有太多的进程
    • 实际用户ID的进程数量超出了限制
  • fork有2个主要用法:

    • 父进程希望复制自己,使父进程和子进程执行不同的代码段。网络服务进程是常见的。
    • 一个进程要执行一个不同的程序。这对shell是常见的情况。子进程从fork返回之后立即调用exec。
  • 文件共享
    父进程所有的打开文件描述符都被复制到子进程中。父进程和子进程共享同一个文件偏移量。


    fork之后父子进程对打开文件的共享
进程终止
  • 有8种方式可以使进程正常终止

    • 从main返回
    • 调用exit - 调用_exit和_Exit
    • 最后一个线程从启动例程返回
    • 从最后一个线程调用pthread_exit
    • 调用abort
    • 接到一个信号
    • 最后一个线程对取消请求作出响应

    无论进程如何终止,最后都会执行内核中同一段代码,这段代码为xiangying

  • 退出函数

    #include <stdlib.h>  
    
    void exit(int status); 
    void _Exit(int status);  
    
    #include <unistd.h>  
    
    void _exit(int status);  
    

3个函数用于正常终止一个程序:

_exit和_Exit立即进入内核,_exit和_Exit其目的是为进程提供一个无需运行终止处理程序或信号处理程序而终止的方法。它们并不冲洗标准I/O流 。

exit则先执行一些清理处理(典型的比如执行标准I/O库的清理关闭操作,对于所有的流调用fclose函数)

  • 进程清理函数
    #include <stdlib.h>  
    
    int atexit(void *(func) void); 
        函数返回值:成功返回0,出错返回非0 
    

进程可以注册"终止处理函数",exit调用这些函数与他们注册时的顺序相反,同一函数若注册多次,也会被调用多次。

C程序如何清理和终止.jpg
回收进程资源

当一个进程正常或异常终止时,内核就向其父进程发送 SIGCHLD信号,父进程可以选择忽略(默认做法),也可以提供一个信号处理函数。

  • wait函数
    内核为每一个 终止子进程保存一定信息:包括进程ID,退出状态,以及CPU时间总量等信息。

    #include <sys/wait.h>
    
    pid_t wait(int *statloc);
        返回值:成功返回进程ID,出错返回0或-1
    

    如果一个进程有几个子进程,那么只要有一个子进程终止,wait就返回,在子进程终止前,调用者将会被阻塞。其错误原因:进程没有子进程,或者函数调用该被信号中断。

    函数参数statloc是一个指针,如果不为空,则进程的终止状态会存放在它所指向的地址单元内。我们可以通过一些列的宏,来查看这些信息。

    作用
    WIFEXITED(status) 若子进程正常终止,返回true
    WIFSIGNALED(status) 若子进程异常终止,返回true
    WIFSTOPPED(status)
    WIFCONTINUED(status)
  • waitpid函数

    #include <sys/wait.h>
    
    pid_t waitpid(pid_t pid, int *staloc, int options);
        返回值:成功返回进程ID,出错返回0或-1
    

    waitpid相较于wait区别如下:

    • 在一个子进程终止前,wait使其调用者阻塞,而waitpid有一选项,可使得调用者不阻塞。
    • wait只要有一个子进程终止,就立即返回,waitpid 有很多选项,可以控制它等待的进程。
    pid 作用
    pid == -1 等待任一子进程
    pid > 0 等待进程ID与pid相等的子进程
    pid == 0 等待组ID等于调用进程组ID的任一子进程
    pid < 0 等待组ID等于pid绝对值的任一子进程
    常量 说明
    WCONTINUED
    WNOHANG waitpid不阻塞
    WUNTRACED

    相对于wait函数:

    • waitpid可等待一个特定的进程
    • waitpid提供了一个wait的非阻塞版本。
    • waitpid通过 WCONTINUED和WUNTRACED支持作业控制
  • 僵尸进程
    一个已经终止,但其父进程尚未对其进行善后处理(获取子进程的有关信息,释放它仍然占用的资源)的进程。

  • 孤儿进程
    如果父进程在子进程之前终止,则它的子进程将会成为孤儿进程。此后,他们的父进程会变为init进程。

函数exec

在上面我们提到了fork的用法2:创建后调用exec以执行另一个程序

#include <unistd.h>

int execl(const char *pathname, const char* arg0, ... /* (char*)0 */);

int execv(const char* pathname, char*const argv[]);

int execle(const char* filename, const char* arg0, ... /* (char*)0 */)

int execve(const char* pathname, char* const argv[], char* const envp[]);

int execlp(const char* filename, const char* arg0, ... /* (char*)0 */)

int execvp(const char* filename, char* const argv[]);

int fexecve(int fd, char* const argv[], char* const envp[]);

    返回值:若出错,返回-1。若成功,不返回
7个exec.jpg
进程资源限制

每个进程都有一组资源限制,其中可以用以下函数查询和更改。

#include <sys/resource>

struct rlimit 
{
    rlim_t rlim_cur;    // soft limit
    rlim_t rlim_max;    // hard limit
};

int getrlimit(int resource, struct rlimit* rlptr);
    返回值:成功返回0,错误返回非0

int setrlimit(int resource, const struct rlimit* rlptr)
    返回值:成功返回0,错误返回非0

在更改资源限制时,必须遵循下列3条原则:

  1. 任何一个进程都可以提升软限制值,但软限制值必须小于或等于硬限制值。
  2. 任何一个进程都可以降低硬限制值,但硬限制值必须大于或等于软限制值。
  3. 只有超级用户进程可以提高硬限制值。

参数resource取下列值:

限制 作用
RLIMIT_NOFILE 每个进程能打开的做多文件数
RLIMIT_FSIZE 可以创建文件的最大字节长度
RLIMIT_NICE 影响进程调度优先级nice值可设置的最大值
RLIMIT_CORE core文件的最大字节数,值为0则不会创建core文件
RLIMIT_STACK 栈的最大字节长度
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 进程概念 进程是程序在计算机上执行一次的过程,也就是一个执行中的程序,进程是一个独立的逻辑控制流,独占处理器,相当...
    秋风弄影阅读 317评论 0 0
  • 进程基础 a.从程序到进程 1.内核将程序读入内存,为程序镜像分配内存空间。 2.内核为该进程分配进程标识符PID...
    allen_TZ阅读 470评论 2 1
  • 1.进程组织结构 1. task_struct ,thread_info 和内核栈 在内核中通常current宏获...
    FlyingReganMian阅读 1,182评论 0 1
  • 从一次元世界进入二次元世界,不只是思维的改变,而是世界观的改变。 程序与进程 如果程序是菜谱,进程就是厨师烹饪;如...
    jdzhangxin阅读 1,168评论 0 6
  • 概述: 本文主要讲解进程基础,更深入的认识有血有肉的进程,内容涉及进程控制块,信号,进程FD泄露等等。仅供参考,欢...
    LooperJing阅读 13,354评论 9 52