一、基础知识
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)如果守护进程由配置文件,那启动时会读取该文件。之后一般不会再查看。如果修改配置,需要重启守护进程。
二、相关函数。
1 退出函数。 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返回时,部分编译器可能会出现报错或者警告 2 登记一个函数为终止处理函数。 // 1 终止处理函数将由exit函数自动调用。 // 2 注册顺序和调用顺序相反。 // 3 可以用sysconf函数来查询最大终止程序数(最少支持32个) int atexit( void (*func)(void) ); 3 内存分配函数。 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。没有正确的转换类型可能导致隐性的系统错误。 4 环境变量函数 int putenv( char *str ); // 参数形式:name=value。否则出错。 int setenv( const char *name, const char *value, int rewrite ); // 参数rewrite 非零时,会强制写。否则,不会进行覆盖操作。 int unsetenv( const char *name ); // 删除一个环境变量。 5 进程资源的设置和查询 int getrlimit( int resource, struct rlimit *rlptr ); // 查询 int setrlimit( int resource, const struct rlimit *rlptr ); // 设置(更改) // 1 软限制 <= 硬限制。 // 2 任何进程都可以降低硬限制。此过程不可逆。 // 3 root用户才可以增加硬限制。 // 4 资源限制会影响到子进程。 6 查询 子进程终止状态 函数。 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函数可以选择无终止子进程时,不阻塞 7 创建子进程 pid_t fork(void); pid_t vfork(void); // 1 调用一次,返回两次。父进程返回子进程ID,子进程返回0。返回顺序,取决于执行顺序。 // 2 可能的失败原因:超过可拥有的最大ID数 或 系统中进程数量过多(系统本身已经存在问题) // 3 vfork函数:保证子进程先运行。且必须先exec执行子进程。如不执行,则会在父进程栈中进行运行。 // 4 尝试代码后,怀疑,从fork开始,就会有2个进程同时执行 fork 之后的代码。 8 启动函数。 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 8 操作 进程 用户/组 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 普通用户不能进行权限以外的其他操作。 9 进程调度函数(优先级) 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值输入过大或过小,函数都会自动进行调整。 10 进程时间 结构体 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时间,以及终止子进程时间 } 11 进程组 int setpgid( pid_t pid, pid_t pgid ); // 加入/创建一个进程组 // 1 调用该函数后,进程和控制终端的联系会被切断。 pid_t tcgetpgrp( int fd); // 得到进程进程组ID int tcsetpgrp( int fd, pid_t pgrpid ); // 设置前台进程组ID。 12 会话 pid_t setsid( void ); // 建立一个新会话。 pid_t tcgetsid( int fd ); // 会话首进程的进程组ID。 13 守护进程函数。 void openlog( const char *ident, int option, int facility ); void syslog( int priority, const char *format ); void closelog( void ); int setlogmask( int maskpri );
三、