进程学习总结(一)

进程学习总结(一)

概述

   在计算机中,进程是正在执行的计算机程序的实例。它包含程序代码及其当前活动(即程序的状态)。一个进程可能由多个并行执行指令的执行线程组成。

C程序的组成

C程序⼀直由下列⼏部分组成:

正⽂段

     这是由C P U执⾏的机器指令部分。通常,正⽂段是可共享的,所以即使是经常执⾏的程序(如⽂本编辑程序、 C编译程序、 s h e l l等)在存储器中也只需有⼀个副本,另外,正⽂段常常是只读的,以防⽌程序由于意外事故⽽修改其⾃身的指令。

初始化数据段

     初始化数据段通常将此段称为数据段,它包含了程序中需赋初值的变量。全局变量就是初始化数据段。

     例如:int global = 88;

⾮初始化数据段

     ⾮初始化数据段通常称为b s s段(block started bysymbol(由符号开始的块))。全局变量的声明就是非初始化数据段。

     例如:long sum[1000] ;

此变量存放在⾮初始化数据段中。

     ⾃动变量以及每次函数调⽤时所需保存的信息都存放在此段中。每次函数调⽤时,其返回地址、以及调⽤者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调⽤的函数在栈上为其⾃动和临时变量分配存储空间。

     例如:C函数递归调⽤的返回的临时变量就是保存在栈里。

     动态存储分配的变量就是保存在堆上。

测试程序

初始程序

#include <stdio.h>

int main(){

       return 0;

}

size a.out

[root@bogon ~]# size a.out

  text       data      bss      dec      hex  filename

  1127      540        4     1671      687  a.out

其中dec = text + data + bss,单位为字节,10进制。hex为dec 的16进制。下同。

测试程序1

#include <stdio.h>

int a;

int a1;

int b1;

int b2;

int c1;

int main(){

       return 0;

}

[root@localhost ~]# size a.out

  text       data      bss      dec      hex  filename

  1127      540       28     1695      69f   a.out

bss段增加了24个字节,bss段以8个字节作为对齐字节。例如上面测试程序,增加一个变量bss段增加8个字节,再增加一个变量,bss段字节数不变,再增加一个字节则bss段再增加8个字节,以此类推。

测试程序2

#include <stdio.h>

int d1=1;

int d2=2;

int d3=4;

int d4=5;

int d5=6;

int main(){

       return 0;

}

[root@localhost ~]# size a.out

  text       data      bss      dec      hex  filename

  1127      560        8     1695      69f   a.out

data 段增加了20个字节,bss段增加了一个字节。在测试中发现,初始化的全局变量为单数时,bss字节数增加4个字节,当初始化的全局变量为双数时,bss段字节数在单数时的字节数减4个字节。

进程创建的相关函数

fork函数

      #include <unistd.h>

pid_tfork(void);

     ⼀个现存进程调⽤fork函数是UNIX内核创建⼀个新进程的唯⼀⽅法。

     该函数被调⽤⼀次,但返回两次。两次返回的区别是⼦进程的返回值是 0,⽽⽗进程的返回值则是新⼦进程的进程 ID。

     ⼦进程和⽗进程继续执⾏ fork之后的指令。

     ⼦进程是⽗进程的复制品。例如,⼦进程获得⽗进程数据空间、堆和栈的复制品。

父子进程继承的数据

⽗进程的性质由⼦进程继承:

• 实际⽤户I D、实际组I D、有效⽤户I D、有效组I D。

• 添加组I D。

• 进程组I D。

• 对话期I D。

• 控制终端。

• 设置-⽤户- I D标志和设置-组- I D标志。

• 当前⼯作⽬录。

• 根⽬录。

• ⽂件⽅式创建屏蔽字。

• 信号屏蔽和排列。

• 对任⼀打开⽂件描述符的在执⾏时关闭标志。

• 环境。

• 连接的共享存储段。

• 资源限制。

• 打开的文件描述符。

⽗、⼦进程之间的区别是:

• fork的返回值。

• 进程I D。

• 不同的⽗进程I D。

• ⼦进程的t m s _ u t i m e, t m s _ s t i m e , t m s _ c u t i m e以及t m s _ us t i m e设置为0。

• ⽗进程设置的锁,⼦进程不继承。

• ⼦进程的未决告警被清除。

• ⼦进程的未决信号集设置为空集。

测试程序1

#include<unistd.h>

#include<stdio.h>

int             globvar = 6;            /* external variable in initializeddata */

char    buf[] = "a write to stdout\n";

int

main(void)

{

        int             var;            /* automatic variable on the stack*/

        pid_t  pid;

        var = 88;

        if (write(STDOUT_FILENO, buf,sizeof(buf)-1) != sizeof(buf)-1)

                return -1;

        printf("before fork\n");        /* we don't flush stdout */

        if ((pid = fork()) < 0) {

                return -2;

        } else if (pid == 0) {          /* child */

                globvar++;                              /* modifyvariables */

                var++;

        } else {

                sleep(2);                               /* parent */

        }

        printf("pid = %ld, glob = %d, var= %d\n", (long)getpid(), globvar, var);

        return 0;

}

运行结果:

[root@localhost proc]# ./a.out

a write to stdout

before fork

pid = 6084, glob = 7, var = 89

pid = 6083, glob = 6, var = 88

[root@localhost proc]# ./a.out  > a.log

[root@localhost proc]# vi a.log

[root@localhost proc]# tail -f a.log

a write to stdout

before fork

pid = 6100, glob = 7, var = 89

before fork

pid = 6099, glob = 6, var = 88

程序说明:

     write函数是不带缓存的。因为在fork之前调⽤write,所以其数据写到标准输出⼀次。

     标准 I / O库是带缓存的。如果标准输出连到终端设备,则它是⾏缓存的,否则它是全缓存的。当以交互⽅式运⾏该程序时,只得到printf输出的⾏⼀次,其原因是标准输出缓存由新⾏符刷新。

     但是当将标准输出重新定向到⼀个⽂件时,却得到 print f输出⾏两次。其原因是,在 fork之前调⽤了printf⼀次,但当调⽤fork时,该⾏数据仍在缓存中,然后在⽗进程数据空间复制到⼦进程中时,该缓存数据也被复制到⼦进程中。于是那时⽗、⼦进程各⾃有了带该⾏内容的缓存。

     在 exit之前的第⼆个printf将其数据添加到现存的缓存中。当每个进程终⽌时,其缓存中的内容被写到相应⽂件中。

参考资料

[1].《Unix高级编程》

[2]. https://en.wikipedia.org/wiki/Process_(computing)


猜你喜欢

转载自blog.csdn.net/xiangguiwang/article/details/79859626
今日推荐