当前位置: 首页 > news >正文

Linux系统进程

Linux系统进程

  • 程序开始
  • 编译链接的引导代码
    操作系统下的应用程序在main执行前也需要先执行段引导代码才能去执行main,但写应用程序时不用考虑引导代码的问题,编译连接时(准确说是链接时)由链接器将编译器中事先准备好的引导代码给链接进去,和应用程序一起构成最终的可执行程序。
  • 运行时的加载器
    加载器是操作系统中的程序,当执行一个程序时(e.g., ./a.out,代码中用exec族函数来运行)加载器负责将这个程序加载到内存中去执行这个程序。
  • 程序结束
  • 正常终止:return、exit、_exit
  • 非正常终止:自已或他人发信号终止进程(e.g., ctrl + c )
  • atexit
    atexit注册多个进程终止函数时,先注册的后执行。(先进后出,和栈一样)
    return和exit效果一样,都会执行进程终止函数;_exit不会执行进程终止函数,即刻终止进程。
#include <stdio.h>
#include <stdlib.h>

//以下func1,func2,func3都是进程终止函数
void term_func1(void){
        puts("term func1");
}
void term_func2(void){
        puts("term func2");
}
void term_func3(void){
        puts("term func3");
}

int main(int argc , char *args[]){
	primtf("hello world");

    atexit(term_func1);
    atexit(term_func2);
    atexit(term_func3);

    exit(0);
}
  • 进程环境
  • 环境变量,可以认为是操作系统的全局变量。
  • export
    查看环境变量
  • 环境变量表
    每一个进程中都有一份所有环境变量构成的一个表格,也就是说当前进程中可以直接使用这些环境变量。进程环境表其实是一个字符串数组,用environ变量指向它。
extern char **environ;
int i=0;
while(*(environ+i)!=NULL){
     printf("%s\n",*(environ+i));
     i++;
}
  • 进程运行的虚拟地址空间

操作系统中每个进程在独立地址空间中运行,每个进程的逻辑地址空间均为4GB(32位系统)。
进程隔离,提供多进程同时运行。

  • 进程

进程是一个动态过程而不是像文件一样的静态实物。进程就是程序的一次运行过程,一个静态的可执行程序a.out的一次运行过程(.
/a.out去运行到结束)就是一个进程。

  • 进程控制块PCB(process control block)
    内核中专门用来管理一个进程的数据结构。
  • 进程ID(PID, process ID)
    用数字标识不同的进程,用getpid获取。
  • 多进程调度
    操作系统同时运行多个进程,其实宏观上是并行,微观上是串行。实际上,现代操作系统最小的调度单元是线程而不是进程。
    getpid
 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 int main()
 {
    printf("pid: %d\n", getpid());
    printf("ppid: %d\n", getppid());
    
    return 0;
  }
  • 父进程
  • fork
    子进程继承父进程中打开的文件。父进程先open打开一个文件得到fd,然后再fork创建子进程。之后在父子进程中各自write向fd中写入内容,结果是接续写。实际上本质原因是父子进程之间的fd对应的文件指针是彼此关联的。
    父进程open打开1.txt然后写入,子进程打开1.txt然后写入,结果是分别写。原因是父子进程分离后才各自打开的1.txt,这时候这两个进程的PCB已经独立了,文件表也独立了,因此2次读写是完全独立的。
  • 僵尸进程
    进程结束时,操作系统会释放此进程占用的资源,但并操作系统不能回收进程本身占用的内存(进程结构体),需要父进程回收。在子进程结束后,父进程还没有回收子进程本身占用的内存,这时的子进程称为僵尸进程。父进程通过调用wait或waitpid来回收子进程。
  • 孤儿进程
    父进程先于子进程结束,此时操作系统会负责回收。
  • pid_t wait(int *wstatus);
    子进程结束时,系统向其父进程发送SIGCHILD信号,父进程调用wait函数后阻塞,父进程被SIGCHILD信号唤醒然后去回收僵尸子进程;若父进程没有任何子进程则wait返回错误。wait主要是用来回收子进程资源,回收同时还可以得知被回收子进程的pid和退出状态。
    status: status用来返回子进程结束时的状态,父进程通过wait得到status后就可以知道子进程的一些结束状态信息。
    pidt: 返回值是本次wait回收的子进程的PID。当前进程有可能有多个子进程,wait函数阻塞直到其中一个子进程结束wait就会返回,wait的
    返回值就可以用来判断到底是哪一个子进程本次被回收了。
    WIFEXITED、WEXITSTATUS等宏用来判断退出状态。
  • exec族函数

fork子进程是为了执行新程序 ,可以直接在子进程的if中写入新程序的代码。这样可以,但是不够灵活,因为我们只能把子进程程序的源代码贴过来执行,但使用exec族可以运行新的可执行程序。

  • int execl(const char *pathname, const char *arg, ... /* (char *) NULL */);
    可以用来执行一个程序,区别是传参的格式不同。execl是把参数列表(本质上是多个字符串,必须以NULL结尾)依次排列而成。(l其实
    就是list的缩写)
  • int execv(const char *pathname, char *const argv[]);
    execv是把参数列表事先放入一个字符串数组中,再把这个字符串数组传给execv函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t pid;
    pid = fork();
    if (pid == -1) {
       perror("fork失败");
       exit(-1);
    } else if (pid == 0) {
       // 子进程
       // 使用execl执行/bin/ls -l命令
       if (execl("/bin/ls", "ls", "-l", NULL) == -1) {
          perror("execl失败");
          exit(-1);
       }
    } else {
       // 父进程
       // 可以在这里做其他事情,或者等待子进程结束
       // 这里简单地等待子进程结束
       wait(NULL);
       printf("子进程执行完毕。\n");
    }
    exit(0);
}
//execv_test.c 
#include<unistd.h>
 
int main()
{
    char * argv[ ]={"ls","-al","/usr",(char*)0};
    execv("/bin/ls",argv);
    
    return 0;
} 
  • 进程状态

进程的5种状态:

  • 就绪态 这个进程当前所有运行条件就绪,只要得到了CPU时间就能直接运行。
  • 运行态 就绪态时得到了CPU就进入运行态开始运行。
  • 僵尸态
  • 等待态(浅度睡眠&深度睡眠)进程在等待某种条件,条件成熟后可进入就绪态。等待态下就算给CPU调度进程也无法执行。
  • 停止态
  • system函数

system函数= fork+exec,属于原子操作,一旦开始就会不被打断的执行完。原子操作的好处就是不会被人打断(不会引来竞争状态),坏处是自己单独连续占用CPU时间太长影响系统整体实时性,因此应该尽量避免不必要的原子操作,就算不得不原子操作也应该尽量原子操作的时间缩短。

  • 进程关系
  1. 无关系 两个进程独立的,无法相互访问。
  2. 父子进程关系 继承
  3. 进程组(group)多个进程构成一个进程组,每个进程组都有唯一的进程组ID(整数,也可以存放在pid_t类型中)。
  4. 会话(session)多个进程组构成一个会话。
  • ps

process status
ps -ajx 偏向显示各种有关的ID号
ps -aux 偏向显示进程各种占用资源

  • kill

kill -信号编号 进程ID,向一个进程发送一个信号
kill -9 xxx,将向xxx这个进程发送9号信号,也就是要结束进程

  • 守护进程

守护进程(daemon),进程名后面加字母d标识,是一类在后台运行的特殊进程,与控制台脱离。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。
服务器(Server),服务器程序就是一个一直在运行的程序,可以给我们提供某种服务(比如,nfs服务器给我们提供nfs通信方式),当我们程序需要这种服务时我们可以调用服务器程序(和服务器程序通信以得到服务器程序的帮助)来进程这种服务操作。服务器程序一般都实现为守护进程。

  • syslogd
    守护进程无法输入输出,只能用syslogd调试信息。操作系统中有一个守护进程syslogd(开机运行,关机时才结束),这个守护进
    程syslogd负责进行日志文件的写入和维护。syslogd是独立于任意一个进程而运行的。
    • openlog
      当前进程和syslogd进程是没有任何关系的,但是当前进程可以通过调用openlog打开一个和syslogd相连接的通道,然后通过syslog向syslogd发消息,然后由syslogd来将其写入到日志文件系统中。
    • syslogd
    • closelog
  • cron
    操作系统时间管理,定时执行程序。
  • 将一个进程实现成守护进程
    create daemon函数要素
    (1)子进程等待父进程退出
    (2)子进程使用setsid创建新的会话期,脱离控制台
    (3)调用chdir将当前工作目录设置为/
    (4)umask设置为0以取消任何文件权限屏蔽
    (5)关闭所有文件描述符
    (6)将0、1、2定位到/dev/null
void create_daemon() {
    pid_t pid;
    pid = fork();

    if (pid == -1) {
        perror("fork error\n");
        exit(-1);
    } else if (pid) {
        exit(0);
    }

    if (-1 == setsid()) {
        perror("setsid error\n");
        exit(-1);
    }

    chdir("/");
    umask(0);
    int cnt = sysconf(_SC_OPEN_MAX);
    int i;
    for (i = 0; i < cnt ; ++i) {
        close(i);
    }
    open("/dev/null", RDWR);
    open("/dev/null", RDWR);
    open("/dev/null", RDWR);
}
  • 让守护进程只执行一次
  • 进程间通信(IPC, Inter-Process Communication)
    复杂、大型的程序,因为设计的需要就必须被设计成多进程程序,常见的如GUI、服务器。
    (1)管道(无名管道)和有名管道
    (2)SystemVIPC:信号量、消息队列、共享内存
    (3)Socket域套接字
    (4)信号
    • 管道(无名管道)
      (1)管道通信的原理:内核维护的一块内存,有读端和写端(管道是单向通信的)
      (2)管道通信的方法:父进程创建管理后fork子进程,子进程集成父进程的管道fd
      (3)管道通信的限制:只能在父子进程间通信、半双工
      (4)管道通信的函数:pipe、write、read、close
    • 有名管道(fifo)
      (1)有名管道的原理:实质也是内核维护的一块内存,表现形式为一个有名字的文件
      (2)有名管道的使用方法:固定一个文件名,2个进程分别使用mkfifo创建fifo文件,然后分别open打开获取到fd,然后一个读一个写
      (3)管道通信限制:半双工(注意不限父子进程,任意2个进程都可)
      (4)管道通信的函数:mkfifo、open、write、read、close
  • SystemVIPC介绍
    SystemVIPc的基本特点:
    (1)系统通过一些专用API来提供SystemVIPC功能
    (2)分为:信号量、消息队列、共享内存
    (3)其实质也是内核提供的公共内存
    • 消息队列
      (1)本质上是一个队列,队列可以理解为(内核维护的一个)FIFO
      (2)工作时A和B2个进程进行通信,A向队列中放入消息,B从队列中读出消息。
    • 信号量
      (1)实质就是个计数器
      (2)通过计数值来提供互斥和同步
    • 共享内存
      (1)大片内存直接映射
      (2)类似于LCD显示时的显存用法
http://www.dtcms.com/a/111961.html

相关文章:

  • AI 浪潮下企业身份管理:特点凸显,安全挑战升级
  • CMake学习-- install 指令详细说明
  • 11.多线程-信号量-线程池
  • AWS 云运维管理指南
  • ekf-imu --- 四元数乘法符号 ⊗ 的含义
  • SQLite 触发器
  • 深入解析CPU主要参数:选购与性能评估指南
  • ngx_alloc
  • 【2022】【论文笔记】基于相变材料的光学激活的、用于THz光束操作的编码超表面——
  • leetcode-代码随想录-哈希表-有效的字母异位词
  • 2007-2019年各省地方财政交通运输支出数据
  • 动物多导生理信号采集分析系统技术简析
  • 分治算法的使用条件
  • 页面简单传参
  • 【Linux】条件变量封装类及环形队列的实现
  • mybatis慢sql无所遁形
  • 学透Spring Boot — 009. Spring Boot的四种 Http 客户端
  • 科技赋能安居梦:中建海龙以模块化革新重塑城市更新范式
  • 多输入多输出 | Matlab实现BO-GRU贝叶斯优化门控循环单元多输入多输出预测
  • 图解AUTOSAR_SWS_LINStateManager
  • prism WPF 模块
  • #SVA语法滴水穿石# (003)关于 sequence 和 property 的区别和联系
  • Mysql 中有哪些日志结构?
  • LeetCode 687 -- 二叉树
  • HTML5+CSS3+JS小实例:带滑动指示器的导航图标
  • 开源且完全没有审核限制的大型语言模型的概述
  • 电力载波单灯控制器:智能照明的关键技术
  • AQS 等待队列中的线程自旋多少次后挂起?
  • 为PXIe控制器配置NI Linux实时操作系统安装软件
  • #python项目生成exe相关了解