linux ---- 进程 --- 1

本文介绍了Linux中的进程控制块(PCB)及fork和exec函数的工作原理,并详细解释了如何通过环境变量与进程交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 概述
-------------------------------------------------------------
每个进程在内核中都有一个进程控制块,也就是PCB,它是用来维护进程的相关信息。Linux内核中,进程控制块是用task_struct这个结构来表现的。以下是这个结构所能表达的一些信息:

  • 进程ID:在C语言中就是用pid_t这个来表示,其实就是一个非负整数。
  • 进程的状态:  运行  --- 挂起 ---  停止 --- 僵尸  
  • 需要保存和恢复的寄存器: 
  • 描述虚拟地址空间的信息
  • 描述控制终端的信息
  • 当前工作目录
  • umask掩码
  • 文件描述符表:包含很多指向文件结构的指针
  • 和信号相关的信息:
  • 用户ID和组ID
  • 控制终端、Session和进程组
  • 进程可以使用的资源上线
  • ....
本章主要讲解的是fork以及exec函数。
其中fork的作用是根据一个现有的进程复制出一个新的进程,原来的进程称为负进程,新的进程称为子进程。
像shell, 为什么敲一个命令就能在这个shell上运行一个新的程序呢? 是因为shell 在读取了用户输入的命令后,会调用fork复制出一个新的Shell进程,新的shell 又调用exec执行新的程序。
一个例子:
在shell下,通过CD命令可以改变shell进程当前目录,输入ls命令来打印当前目录下的内容,ls其实是列自己当前目录,而不是shell 程序自己的当前目录,只不过ls当前目录是从shell PCB中拷贝来的。

2. 环境变量
-----------------------------------------------
exec系统调用执行新的程序的时候,会把命令行参数和环境变量传递给main函数,它们在整个进程地址空间中的位置如下图所示:
linux <wbr>---- <wbr>进程 <wbr> <wbr>--- <wbr>1

环境变量和命令行参数类似,都是一组字符串,如下图所示:
linux <wbr>---- <wbr>进程 <wbr> <wbr>--- <wbr>1


libc用environ这个全局变量指向环境变量表,这个变量没有包含在任何的头文件中,因此要使用这个变量,必须要加extern, 

例如:
-----------------------------------------------
#include <stdio.h>
int main(void)
{
          extern char **environ;
        int i;
        for(i=0; environ[i]!=NULL; i++)
        printf("%s\n", environ[i]);
        return 0;
}
-----------------------------------------------
结果为:
$ ./a.out
SSH_AGENT_PID=5717
SHELL=/bin/bash
DESKTOP_STARTUP_ID=
TERM=xterm...
 
下面介绍一下一些比较重要的环境变量:
PATH    可执行文件的搜索路径
SHELL    当前SHELL, 它的值通常是/bin/bash
TERM      当前终端类型,在图形界面下,它的值通常是xterm, 其决定了一些程序输出显示方式。
LANG      语言和locale, 决定了字符编码,时间,货币等信息
HOME        当前用户主目录,很多程序都需要在主目录下保存配置文件,使得每个用户在运行该程序的时候运行该 
              程序的时候都有自己的一套配置。
 
getenv --- 获取环境变量值
用environ指针可以查看所有环境变量字符串,但是不够方便,如果给出name要在环境变量表中查找它对应的value,可以用getenv函数。
--------------------------------------
#include <stdlib.h>
char *getenv(const char *name);
 
这个返回值返回value的指针, 若未找到就返回NULL
 
setenv --- 修改环境变量 
  int setenv(const char *name, const char *value, int rewrite);
 
unsetenv --- 删除环境变量
void unsetenv (const char ) 
 
3. 进程控制
  1> fork函数
  其过程如下:
   
linux <wbr>---- <wbr>进程 <wbr> <wbr>--- <wbr>1

执行结果:
$ ./a.out This is the child
This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the child
$ This is the child
This is the child

  1. 父进程初始化
  2. 父进程调用fork, 这是一个系统调用,因此进入内核
  3. 内核根据父进程, 复制出一个子进程,子进程和父进程一模一样(代码/数据/PCB信息)
  4. 现在有两个一模一样的进程都调用了fork函数进入内核等待返回,实际上fork值被调用了一次,此外系统还有别的进程等待返回,是父进程先返回还是子进程先返回,还是两个进程都等待,这都不一定,这取决于内核的调度。
  5. 当某一时刻父进程被执行了,从内核返回后, 保存在变量pid中的值是子进程的id, 是一个大于0的整数,因此执行下面的else分支,然后进入for循环,打印for循环中的内容
  6. 如果某个时刻子进程被执行了, 从内核返回后, 保存在 pid中的值是0, 因此执行下面的if pid == 0 的分支,然后执行for循环, fork函数把父进程的的数据复制一份给子进程, 但此后二者互不影响。
  7. 父进程没打印一条,就睡眠一秒,在这一秒里,子进程可能会被执行,同样,子进程没打印一条,也会睡眠一秒,这样子进程和父进程会交互的进行打印...
  8. 这个程序是在shell下执行的,因此shell是父进程的父进程。父进程运行时,shell就在等待的状态,当父进程终止的时候,shell认为命令执行结束了,而是打印shell提示符,而事实上,子进程这个时候还没有结束,所以子进程的打印消息打印在了shell提示符后了。
fork函数的特点总结起来将就是调用一次,返回两次,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值