linux学习笔记之进程

时间:2024-01-14 19:23:02

一、基础知识

1:进程。

  1,进程ID: 非负整数,具有唯一性。

    1)ID=0的进程:调度进程/交换进程。内核的一部分。不执行任何磁盘上的程序。

    2)ID=1的进程:init进程。

      1-自举结束时,由内核调用,且不会终止。

      2-用于读取与系统有关的初始化文件。并引导系统至一个状态。

      3-使用root权限运行,是所有孤儿进程的父进程。

    3)ID=2的进程:页守护进程。负责支持虚拟存储器系统的分页操作。

  2,僵死进程:已经终止,但其父进程尚未对其进行善后处理的进程。

  3,进程调度。

    1)UNIX系统只提供基于调度优先级的粗粒度的控制。

    2)调度策略和调度优先级由内核确定。

    3)进程可以通过调整nice值来降低优先级运行。(不可提升)

    4)只有特权进程允许提高优先级。(应该指root)

  4,父进程和子进程。

    1)父进程和子进程只共享正文段。其他仅为副本,仅在复制的此刻保持一致。linux中,增加函数clone。可以控制哪些部分共享,哪些部分不共享。

    2)每个进程都有一个父进程。子进程终止时,父进程得到通知并获取子进程的退出状态。

    3)通常进程的父进程是什么?用户进程?

    4)子进程的用途。

      1-父进程希望复制自己。例如:服务器中,等待客户端服务请求。请求到达,复制一份子进程执行请求。父进程继续等待请求。
      2-进程需要执行另外一个程序。此情况下,fork后将立即调用exec函数。

  5,进程组:多个进程的集合。

    1)每个进程组由一个组长进程。组长进程的ID就是进程组ID。

    2)进程组中的最后一个进程可以终止,也可以转移到另一个进程组。

    3)进程可以设置自己和子进程的进程组ID。但子进程调用exec后,就不能再通过父进程修改进程组ID了。

  6,会话:包含一个或多个进程组。

    1)会话ID为会话首进程ID。会话首进程称为 控制进程。

    2)一个会话可以有一个控制终端。(终端或伪终端设备)

    3)会话分为:一个前台进程组和多个后台进程组。

2:进程中的用户和用户组。

  1,进程中分为:实际用户/组,有效用户/组,设置用户/组。

    1)实际:进程执行用户。

    2)有效:决定进程访问权限。通常 实际== 有效

    3)设置:设置ID由exec函数保存。当有效时,则将设置ID变为有效ID。

    4)stat->st_mode中有两个特殊标志位(分别控制用户和组)。置位作用为:当执行此文件,进行的有效ID将便为该文件的所有者ID。(用户和组由不同位控制)

  2,设置ID可以间接获得其他用户权限。需要慎重使用。(例:用户更改自己的密码,口令文件为root所有。)

  3,非root权限的进程,写文件时,设置用户/组ID位会清除。

  4,新文件用户ID为:进程有效用户ID。

  5:新文件的组ID有两种情况(linux 3.2.0):

    1)所在目录的设置组ID置位时:和所在目录的组ID一样。

    2)所在目录的设置组ID未置位时:进程有效组ID。

3:进程启动和终止。

  1,启动:

    1)内核使程序执行的唯一方法时调用一个exec函数。

  2,正常终止:

    1)从main返回。

    2)从exit, _exit, _Exit函数调用。

    3)最后一个线程: 从启动例程返回 或 调用pthread_exit.

  3,异常终止。

    1)调用abort函数。

    2)接收到一个信号(终止相关)

    3)最后一个线程对取消请求做出响应。

3:C程序的储存空间布局。下列顺序由高地址到低地址排序。

  1,命令行参数和环境变量。

  2,栈(stack):往低地址方向增长。保存:自动(临时)变量 和 函数调用相关信息(形参,返回地址等)

  3,堆(head):往高地址方向增长用于动态储存分配。(例如:new,malloc等)

  4,未初始化数据:称之为bss段。保存了已经未初始化的全局/静态变量。

  5,初始化数据:保存了已经初始化的全局/静态变量。

  6,包含常量数据:如果不细分,也可以当作初始化数据的一部分。

  7,正文:CPU执行的机器指令保存部分。

低地址 正文(text) 常量数据(rodata) 初始化数据(data) 未初始化数据(bss) heap(堆)  -->       unused    <--  stack(栈) environment 高地址

  8,子进程创建时,拥有父进程其他一切数据的副本,但只有exec程序执行后,才拥有栈和堆。

4:守护进程。

  1,基本概念。

    1)生存周期长的进程。

    2)通常在后台运行。

    3)使用 ps -axj可以显示进程。守护进程的名字出现在方括号[ ] 中。

    4)需要在进程上下文执行工作,单不被用户层进程上下文调用的内核组件,通常都有自己的守护进程。

    5)守护进程通常以root权限运行。

  2,产生日志消息的方式。

    1)内核例程调用log函数。

    2)守护进程调用syslog函数产生。

    3)将日志消息发送向UDP端口514。(网络用户也可以使用)

  4,守护进程遵循下列通用惯例。

    1)如果守护进程使用锁文件。一般储存在/var/run目录下。锁文件名为name.pid

    2)如支持配置选项,目录为 /etc 名字为 name.conf

    3)可以使用命令行启动,通常由系统初始化脚本启动

    4)如果守护进程由配置文件,那启动时会读取该文件。之后一般不会再查看。如果修改配置,需要重启守护进程。

二、相关函数。

 退出函数。
  void exit( int status )
  void _Exit( int status )
  void _eixt( int status )
  // 1 stdlib.h 包含exit _Exit.unistd.h包含_exit
  // 2 参数status 将作为main函数的返回值返回。
  // 3 只有exit函数而没有return返回时,部分编译器可能会出现报错或者警告
登记一个函数为终止处理函数。
  // 1 终止处理函数将由exit函数自动调用。
  // 2 注册顺序和调用顺序相反。
  // 3 可以用sysconf函数来查询最大终止程序数(最少支持32个)
  int atexit( void (*func)(void) );
内存分配函数。
  void *malloc( size_t size ); // 按大小分配内存。
  void *calloc( size_t nobj, size_t size ); // 按指定数量和指定长度的对象分配储存空间。
  void *realloc( void *ptr, size_t newsize ); //增加/减少以前分配区的长度。
  void free( void *ptr ); //释放内存。
  // 1 分配函数返回的指针一定要适当对齐。例:double必须在8的倍数地址单元处开始。
  // 2 由隐性的转换规则。如无,则默认返回为int。没有正确的转换类型可能导致隐性的系统错误。
环境变量函数
  int putenv( char *str ); // 参数形式:name=value。否则出错。
  int setenv( const char *name, const char *value, int rewrite ); // 参数rewrite 非零时,会强制写。否则,不会进行覆盖操作。
  int unsetenv( const char *name ); // 删除一个环境变量。
进程资源的设置和查询
  int getrlimit( int resource, struct rlimit *rlptr ); // 查询
  int setrlimit( int resource, const struct rlimit *rlptr ); // 设置(更改)
  // 1 软限制 <= 硬限制。
  // 2 任何进程都可以降低硬限制。此过程不可逆。
  // 3 root用户才可以增加硬限制。
  // 4 资源限制会影响到子进程。
查询 子进程终止状态 函数。
  pid_t wait( int *statloc );
  pid_t waitpid( pid_t pid, int *statloc, int options );
  // 1 参数pid:==-1 等待任一子进程,>0 等待与pid相同ID的子进程,==0 等待组ID等于调用进程组ID的任一子进程,<-1 等待组ID等于pid绝对值的任一子进程。
  int waitid( idtype_t idtype, id_t id, siginfo_t *infop, int options );
  pid_t wait3( int *statloc, int options, struct rusage *rusage );
  pid_t wait4( pid_t pid, int *statloc, int options, struct rusage *rusage );
  // 1 三种情况:无终止子进程,则阻塞。有子进程终止,并且有信号发出,则返回该子进程的终止状态。进程本身没有任何子进程,则返回出错。
  // 2 waitpid函数可以选择无终止子进程时,不阻塞
创建子进程
  pid_t fork(void);
  pid_t vfork(void);
  // 1 调用一次,返回两次。父进程返回子进程ID,子进程返回0。返回顺序,取决于执行顺序。
  // 2 可能的失败原因:超过可拥有的最大ID数 或 系统中进程数量过多(系统本身已经存在问题)
  // 3 vfork函数:保证子进程先运行。且必须先exec执行子进程。如不执行,则会在父进程栈中进行运行。
  // 4 尝试代码后,怀疑,从fork开始,就会有2个进程同时执行 fork 之后的代码。
启动函数。
  int execl( const char * pathname, const char *arg0, ...)
  int execv( const char * pathname, char *const argv[] )
  int execle( const char * pathname, const char *arg0, ...)
  int execve( const char * pathname, char *const argv[], char *const envp[] )
  int execlp( const char * pathname, const char *arg0, ...)
  int execvp( const char * pathname, char *const argv[] )
  int fexecve( int fd, char *const argv[], char *const envp[] )
  // 1 filename包含/ 则当作路径名。都则按照PATH环境变量,在各目录搜索可执行文件。
  // 2 PATH变量中包含目录表,用 : 隔开。例:PATH=/bin : /usr/bin
操作 进程 用户/组 ID
  int setuid( uid_t uid ); // 设置有效用户ID。如果有root权限,还可以更改实际用户。
  int setgid( gid_t gid ); // 设置有效组ID。如果有root权限,还可以更改实际组。
  int setreuid( uid_t ruid, uid_t euid ); // 交换实际用户和有效用户ID。
  int setregid( gid_t rgid, gid_t egid ); // 交换实际组和有效组ID。
  int seteuid( uid_t uid ); // 仅设置有效用户ID。
  int setegid( uid_t gid ); // 仅设置有效组ID。
  // 1 只有root用户才可以更改实际用户ID。
  // 2 普通用户不能进行权限以外的其他操作。
进程调度函数(优先级)
  int nice( int incr ); // 更改优先级,只能更改进程本身的优先级(拥有root权限除外)。
  int getpriority( int which, id_t who ); // 获得nice值
  int setpriority( int which, id_t who, int value ); // 设置nice值。
  // 1 nice值输入过大或过小,函数都会自动进行调整。
进程时间 结构体
  clock_t times( struct tms *buf );
  struct tms
  {
   clock_t tms_utime; //用户CPU时间。
   clock_t tms_stime; //系统CPU时间。
   clock_t tms_cutime; //用户CPU时间,以及终止子进程时间
   clock_t tms_cstime; //系统CPU时间,以及终止子进程时间
  }
进程组
  int setpgid( pid_t pid, pid_t pgid ); // 加入/创建一个进程组
  // 1 调用该函数后,进程和控制终端的联系会被切断。
  pid_t tcgetpgrp( int fd); // 得到进程进程组ID
  int tcsetpgrp( int fd, pid_t pgrpid ); // 设置前台进程组ID。
会话
  pid_t setsid( void ); // 建立一个新会话。
  pid_t tcgetsid( int fd ); // 会话首进程的进程组ID。
守护进程函数。
  void openlog( const char *ident, int option, int facility );
  void syslog( int priority, const char *format );
  void closelog( void );
  int setlogmask( int maskpri );

三、