unix系统编程day01--Linux进程讲解

一、进程相关概念

程序与进程

  程序:是指编译好的二进制文件,再磁盘上,不占用系统资源(cpu,内存,打开的文件,设备,锁。。。)
  进程:是一个抽象的概念,与操作系统原理联系紧密,进程是活跃的程序,占用系统资源(程序运行起来,占用一个进程)
  程序 -> 剧本(纸) 进程 -> 戏(舞台、演员、灯光)
  同一剧本可以在多个舞台同时上演。同样同一个程序可以加载为多个不同的进程并互不影响。
  如:开两个终端,各自都有一个bash,但是id却不同。

并发

    简单的说就是同时运行

单道程序设计

    排队使用cpu运行程序,cpu同时只能处理一个程序,如微软的DOS系统

多道程序设计

    cpu通过时钟中断的方法,把cpu的使用权收回,然后下方,同样也是排队执行,但是每个程序并不是一直占用cpu,只是一小段一小段的执行,由于cpu的执行速度比较快,所以在人类眼中感觉不到,所以认为是并行,并称为多道程序设计。

cpu/mmu

    计算机的缓存机制,从硬盘缓存到内存,从内存缓存到cache,从cache缓存到寄存器,然后到预取器处理,到译码器,然后分析出需要的算法,并在寄存器中找到相应的数据,最后在ALU中进行运算,之后把结果回写在寄存器中,然后在缓存到cache到内存然后到输出设备。
     mmu成为内存管理单元,虚拟地址:可用的地址空间(4G),但是实际上并没有这么大,mmu充当一个媒介,把虚拟地址对应到实际的物理地址上,程序中使用的虚拟地址。mmu还可以设置cpu对内存的访问级别一般intel有四个级别(3, 2, 1, 0级别从低到高)

PCB进程控制块

PCB实际是一个结构体,名字为task_struct,路径为include/linux/sched.h

  • 进程id。系统中每一个进程都有唯一的id在C语言中用pid_t表示,实际上为非负整数
  • 进程状态:初始化,就绪(等待cpu分配时间片),运行(占用cpu),挂起(阻塞暂停,等待除cpu外的其他资源),终止(放弃cpu)。
  • 进程切换时需要保存和恢复的一些cpu寄存器。
  • 描述虚拟地址空间信息
  • 描述控制终端信息
  • 当前工作目录(chdir改变当前进程的工作目录)
  • umask掩码(保护文件创建的权限)
  • 文件描述符表(包含很多指向file的结构体指针)
  • 和信号相关的信息
  • 用户ID和组ID
  • 会话和进程组
  • 进程可以使用的资源上限(ulimit -a查看)

二、环境变量

    Linux是多用户,多任务的操作系统,根据个人的习惯以用户为单位配置系统,也就是配置的环境的信息通过环境变量来配置。
    环境变量,是指在操作系统中用来指定操作系统运行环境的一些参数,通常具有以下性质。

  1. 字符串(本质)
  2. 有统一的格式 名=值(:值)
  3. 值用来描述进程环境信息

存储形式:与命令行参数类似。char*[]数组,数组名为environ,内部存储字符串,NULL为哨兵结尾。
使用形式:与命令行参数相似
加载位置:与命令行参数相似。位于用户区,高于stack的起始位置(虚拟内存地址,命令行参数的位置)
引入环境变量表:须声明环境变量。extern char ** environ;

打印所有环境变量

#include <stdio.h>

extern char ** environ;

int main() {
    int i = 0;
    while(environ[i++]) {
        printf("%s\n", environ[i]);
    }
    return 0;
}

常用的环境变量/作用

查看下面的值使用 echo $VAR(VAR为变量名字)
PATH:可执行文件的搜索目录。每个命令按照PATH的值从前往后搜索,如果搜索到就执行,所以如果有新版本的文件,应该放在PATH的前面
SHELL:当前shell的目录,他的值通常为/bin/bash
HOME:显示当前用户的家目录
LANG:查看当前环境使用的语言
TERM:显示当前使用的是什么终端

函数

  1. getenv函数

    • 获取环境变量值
      函数原型:char *getenv(const char * name);成功返回变量值,否则返回NULL;
  2. setenv函数

    • 设置函数变量的值
      函数原型:char *setenv(const char *name, char * val, int overwrite); 如果overwrite为1且name环境变量存在则会覆盖原有的环境变量,如果为0则不会覆盖。如果本身不存在变量,则会创建相应的环境变量。
  3. unsetenv函数

    • 取消或删除某个环境变量的值
      函数原型:int unsetenv(const char * name);在删除一个不存在的变量时返回值为0,表示成功。
      测试代码:
#include <stdio.h> 
#include <stdlib.h>

int main(void) {
    char *val;
    const char * name = "ABD";
    val = getenv(name);
    printf("%s = %s\n", name, val);
    setenv(name, const_cast<char*>("hahhahahaha"), 1);
    val = getenv(name);
    printf("%s = %s\n", name, val);
#if 1 
    int ret = unsetenv(const_cast<char*>("ABD="));
    printf("ret = %d\n", ret);
    val = getenv(name);
    printf("%s = %s\n", name, val);
#else
    int ret = unsetenv("ABD");
    printf("ret = %d\n", ret);
    val = getenv("ABD");
    printf("%s = %s\n", name, val);
#endif
    return 0;
}

三、进程控制原语

fork函数

  1. 作用:创建一个子进程
  2. 函数原型: pid_t fork(void)
  3. 返回值:一次调用有两个返回值:
    • 返回子进程的pid(非负整数)[父进程]
    • 返回0 [子进程]
    • -1表示出错
      创建子进程程序演示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>


int main(void) {
    printf("START!!!!!!!!!!\n");
    pid_t pid = fork();
    if(pid == -1) {
        perror("fork error");
        exit(1);
    } else if(pid > 0) {
        printf("I'm father, pid = %u, ppid = %u\n", getpid(), getppid());
        sleep(1);
    } else if(pid == 0) {
        printf("I'm child pid = %u my father id = %u\n", getpid(), getppid());
    }
    puts("END!!!!!!!!!!!!!");
    return 0;
}

通过打印我们发现父进程也有父进程也有ppid,通过 ps aux | grep xxx 发现- 父进程的ppid为shell的pid
修改上述程序完成循环创建子进程的结构:
以下为错误实例!!!!!!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>


int main(void) {
    printf("xxxxxxxxxxxxxxxxxxxx\n");
    for(int i = 0; i < 5; i++) {
        pid_t pid = fork();
        if(pid == -1) {
            perror("fork error");
            exit(1);
        } else if(pid > 0) {
          //  printf("I'm father, pid = %u, ppid = %u\n", getpid(), getppid());
          //  sleep(1);
        } else if(pid == 0) {
            printf("I'm %dth child pid = %u my father id = %u\n", i + 1,  getpid(), getppid());
        }
    }
    puts("END!!!!!!!!!!!!!");
    return 0;
}

因为子进程继续执行的话还是有fork函数,子进程就会创建孙进程,所以不正确。
正确程序!!!!!!!
创建五个子进程,在子进程被父进程创建完成之后break即可

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>


int main(void) {
    printf("xxxxxxxxxxxxxxxxxxxx\n");
    for(int i = 0; i < 5; i++) {
        pid_t pid = fork();
        if(pid == -1) {
            perror("fork error");
            exit(1);
        } else if(pid > 0) {
            printf("I'm father, pid = %u, ppid = %u\n", getpid(), getppid());
            sleep(i);
        } else if(pid == 0) {
            printf("I'm %dth child pid = %u my father id = %u\n", i + 1,  getpid(), getppid());
            break;//子进程break
        }
    }
    puts("END!!!!!!!!!!!!!");
    return 0;
}

进程共享

父子进程在刚刚fork之后。有哪些相同,有哪些不同。

  • 相同:在fork执行之前的所有数据为相同的,全局变量、.data、.text、堆、栈,用户ID,宿主目录…。
  • 不同:进程ID、fork返回值、进程运行时间、闹钟(定时器)、未决信号集

注意:在fork之后全局变量是独享的了

Linux父子进程之间实行,读时共享写时复制的操作,如果子进程只是读取数据的话,那么共享数据,如果有写的操作,则复制父进程的东西

【重点】:父子进程共享:文件描述符、mmap建立的映射区

gdb调试

通过命令 set-fork-mode child可以设置跟踪子进程,默认为跟踪父进程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值