进程

进程环境=======

main函数

1、c程序总是main函数开始执行,main函数的原型是:

int main(int argc, char *argv[])
{}

其中,argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组,其实就是指针数组
2、命令行参数

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i;

    for(i = 0; i < argc; i++)
    {
        printf("argc = %d argv[%d] = %s\n", argc, i, argv[i]);
    }

    return 0;
}

root@ubuntu:/home/jianghu/share# ./app 
argc = 1 argv[0] = ./app
root@ubuntu:/home/jianghu/share# ./app abc efg
argc = 3 argv[0] = ./app
argc = 3 argv[1] = abc
argc = 3 argv[2] = efg

char *argv[]一个指针数组

进程终止

1、有8种方式使进程终止,其中5种为正常终止,它们是

(1)从main返回
(2)调用exit
(3)调用_exit或_Exit
(4)最后一个线程从其启动例程返回
(5)从最后一个线程调用pthread_exit
基本概括为一个进程正常终止或者进程中最后一个线程正常终止

异常终止有3中方式,它们是

(6)调用abort
(7)接到一个信号
(8)最后一个线程对取消请求做出响应
基本概括为一个进程异常终止或者进程中最后一个线程异常终止

2、退出函数
3个函数用于正常终止一个程序,_exit和_Exit立即进入内核,exit则先执行一些清理处理,然后返回内核

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

exit函数总是执行一个标准I/O库的清理关闭操作:对于所有打开流调用fclose函数,这就造成输出缓冲中的所有数据都被冲洗(写到文件上)
3个退出函数都带一个整型参数,称为终止状态(或退出状态),如果调用这些函数时不带终止状态,或main执行一个无返回值的return语句,或main没有声明返回类型为整型,则该进程的终止状态是未定义的,但是,若main的返回类型是整型,并且main执行到最后一条语句时返回,那么该进程的终止状态是0
main函数返回一个整型值与用该值调用exit是等价的,于是在main函数中exit(0)等于return(0)

#include <stdio.h>

main()
{
        printf("hello  world\n");
}

对该程序进行编译,然后运行,则可见到其终止码是随机的

7-2.png

注意,内核使程序执行的唯一方法是调用一个exec函数,进程自愿终止的唯一方法是显式或隐式(通过调用exit)调用_exit或_Exit,进程也可非自愿地由一个信号使其终止。

c程序的存储空间布局

1、c程序一直由下列几部分组成:正文段、初始化数据段、未初始化数据段、堆、栈
正文段:这是CPU执行的机器指令部分,通常,正文段是可共享的,正文段常常是只读的,以防止程序由于意外而修改其指令。
初始化数据段:通常将此段称为数据段,它包含了程序中需明确的赋初值的变量,例如 int maxcount = 99,使此变量以其初值存放在初始化数据段中
未初始化数据段:通常将此段称为bss段,在程序开始执行之前,内核将此段中的数据初始化为0或空指针,例如函数的声明 long sum[1000]将此变量存放在非初始化数据段中
栈:自动变量以及每次函数调用时所需保存的信息都保存在此段中。每次函数调用时,其返回地址以及调用者的环境信息(如某些机器寄存器的值)都存放在栈中。然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间。
堆:通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于未初始化数据段和栈之间。


7-6.png

未初始化数据段的内容并不存放在磁盘程序文件中,其原因是,内核在程序开始运行前将他们都设置为0,需要存放在磁盘程序文件中的段只有正文段和初始化数据段

存储空间分配

1、malloc,分配指定字节数的存储区,此存储区中的初始值不确定
2、calloc,为指定数量指定长度的对象分配存储空间。该空间中的每一位(bit)都初始化为0
3、realloc,增加或减少以前分配区的长度,当增加长度时,可能需要将以前分配区的内容移到另一个足够大的区域,以便在尾端提供增加的存储区,而新增区域内的初始值则不确定

#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
3个函数返回值:若成功,返回非空指针,若出错,返回NULL
这3个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象

void free(void *ptr);
函数free释放ptr指向的存储空间

详见目录c中《浅谈malloc,calloc,realloc函数之间的区别》

进程控制=======

1、进程控制,主要包括进程的创建、执行和终止
2、进程标识
每个进程都有一个非负整型表示的唯一进程ID,因为进程ID标识符总是唯一的。虽然是唯一的,但是进程ID是可复用的,当一个进程终止后,其进程ID就成为复用的候选者。
ID为0 的进程通常是调度进程

#include <unistd.h>
pid_t getpid(void)           //返回值:调用进程的进程ID
pid_t getppid(void)         //返回值:调用进程的父进程的ID

3、函数fork
作用:一个现有的进程可以调用fork函数创建一个新进程

#include <unistd.h>
pid_t fork(void)
返回值:子进程返回0,父进程返回子进程ID,如出错,返回-1

由fork创建的新进程被称为子进程(child process),fork函数被调用一次,但返回两次,两次返回的区别是子进程的返回值是0,而父进程的返回值则是新建子进程的进程ID。

将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID

fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID

子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获取父进程的数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父进程和子进程并不共享这些存储空间部分。父进程和子进程共享正文段。

由于在fork之后经常跟随者exec,所以现在的很多实现并不执行一个父进程数据段、堆和栈的完全副本。作为替代,使用了写时复制技术。这些区域由父进程和子进程共享,而且内核将他们的访问权限改变为只读。如果父进程和子进程中的任一个试图修改这些区域,则内核只为修改区域那块内存制作一个副本。
demo

#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    int a = 0, b =0;

    pid = fork();
    if(pid < 0)
    {
        printf("it is error\n");
    }
    else if(pid == 0)
    {
        a++;
        b++;
        printf("this is child process a = %d b = %d\n", a, b);
    }
    else
    {   
        a = 2;
        b = 2;
        printf("this is parent process a = %d b = %d\n", a, b);
    }

    return 0;
}

root@ubuntu:/home/jianghu/share# ./app 
this is parent process a = 2 b = 2
this is child process a = 1 b = 1

从中可以看到,子进程对变量所做的改变并不影响父进程中该变量的值
一般来说,在fork之后是父进程先执行还是子进程先执行时不确定的,这取决于内核所使用的调度算法。
文件共享(待补充)
4、函数vfork
vfork函数的调用序列和返回值与fork相同,但两者的语义不同。
vfork和fork区别
区别一、vfork函数用于创建一个新进程,而该新进程的目的是exec一个新程序。vfork与fork一样都是创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),于是也就不会引用该地址空间。不过在子进程调用exec或exit之前,它在父进程的空间中,如果子进程修改数据(除了用于存放vfork返回值的变量)、进行函数调用、或者没有调用exec或exit就返回都可能会带来未知的结果。
区别二、vfork保证子进程先运行,在它调用exec或exit之后父进程才能被调度运行,当子进程调用这两个函数中的任意一个时,父进程会恢复运行(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁)
demo

#include <stdio.h>
#include <unistd.h>
#if 1
int main(int argc, char *argv[])
{
    int a = 8;
    pid_t pid;

    pid = vfork();
    if(pid < 0)
    {
        printf("this is error\n");
    }
    else if(pid == 0)
    {
        a++;
        printf("this is child process a = %d\n", a);
        _exit(0);
    }
    else
    {
        printf("this is parent process a = %d\n", a);
    }
    
    return 0;
}
#else
int main(int argc, char *argv[])
{
    int a = 8;
    pid_t pid;

    pid = fork();
    if(pid < 0)
    {
        printf("this is error\n");
    }
    else if(pid == 0)
    {
        a++;
        printf("this is child process a = %d\n", a);
    }
    else
    {
        printf("this is parent process a = %d\n", a);
    }
    
    return 0;
}
#endif

root@ubuntu:/home/jianghu/share# ./app 
this is child process a = 9
this is parent process a = 9

当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号,因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用执行的函数(信号处理函数)。对于这种信号的系统默认动作是忽略它。

函数wait和waitpid

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

推荐阅读更多精彩内容

  • Linux 进程管理与程序开发 进程是Linux事务管理的基本单元,所有的进程均拥有自己独立的处理环境和系统资源,...
    JamesPeng阅读 2,405评论 1 14
  • 2016-02-02 进程控制 进程标识 每个进程都有一个非负整型的唯一的进程id,因为进程id表示服总是唯一的,...
    千里山南阅读 413评论 0 0
  • 今天是自由书写第十二天了,不知道第二十一天时会有什么改变,我期待着。不过,这几天我已经发现自己不再对妈妈...
    尚池阅读 216评论 2 1
  • 对联六要素 上篇讲了对联的发源和发展,以及它的作用,关于对联其他的像对联的种类,对联的修辞技巧,有关对联禁忌事...
    红的太阳雨阅读 4,015评论 0 2
  • 请恕我冒昧,又是一篇和我女朋友有关系的文章。这已经是第三篇了。 之所以写这篇文章,是因为前两天写有...
    30秒男人辰昱阅读 294评论 0 0