Process of Linux

来源:互联网 发布:省市区四级联动数据库 编辑:程序博客网 时间:2024/06/01 09:40

每一个运行的程序的实例被称为 进程。

头文件<unistd.h>

PID, process ID,是一个16-bit的整数,系统自动分配的,依次增加。每个进程都有一个父进程(除了init进程外)。可以将linux系统的进程想象成树状结构,init进程是该树的根。

父进程ID,ppid,就是父进程的pid。

在C/C++中使用pid_t的typedef的类型来表示PID,该类型的定义在<sys/types.h>头文件中。

使用getpid()函数可以获得正在运行的进程的pid。使用getppid()函数可以获得当前进程的父进程的pid

查看系统中当前激活的进程:ps

ps的复杂性源自该程序要实现和各个GNU/Linux发布版本的兼容性,它有很多选项用于控制显示那些进程,对这些进程显示哪些信息。

$ps显示由当前terminal控制的进程

$ps -e -o pid,ppid,command

-e,表示显示系统中所有的进程信息

-o,表示指定输出的进程的显示的信息的格式

终止一个进程

$kill pid

pid是要终止的进程的pid,向该进程发送一个SIGTERM信号(termination信号),这个信号会终止该进程的执行,除非该进程有显示处理SIGTERM信号的代码


创建进程

使用system函数,system函数是C标准库的提供的函数,它提供了一个简单的方式实现了从程序中执行命令。

system函数实际上创建了一个子进程来执行Bourne Shell(/bin/sh),然后将命令传递给这个shell执行。

system函数返回shell命令的退出码,如果shell命令不能执行则system函数返回127,如果是其它错误,则返回-1

system函数的头文件<stdlib.h>


使用fork和exec

fork,创建一个子进程,该子进程是复制父进程而得到的。

一个程序调用fork后产生一个子进程,父进程从fork的调用点继续执行,新产生的子进程也执行相同的程序,也是从fork继续执行。子进程拥有新的PID。

程序如何区分它是在【父进程】中执行,还是在【子进程】中执行?

1、程序使用pid,调用getpid函数

2、fork函数在【父进程】/【子进程】中执行时得到的返回值是不同的,

     fork后,产生两个进程父进程和子进程。复制的是调用fork时以后将要执行的程序代码,程序以前已经执行的代码不会复制。在父进程中,fork的返回值是新创建的子进程的pid,在子进程中,fork的返回值是0【进程号pid是从1开始的!】


exec函数家族

终止一个进程中执行的程序,并开始执行另外一个程序。


有字符p的执行搜索功能,不带p的必须提供完整的可执行程序的路径。

有字符v的接受字符传输组(数组以NULL指示结尾),每个指针指向一个字符串

有字符l的接受C风格的变长参数列表

有字符e的接受一个额外的参数(环境变量数组),一个字符指针数组,以NULL结尾,每个字符串应该是"variable=value"的格式


execl,execlp,execle,execv,execvp,execve,execvpe在头文件<unistd.h>

int execl  (const char *path, const char *arg,...);

int execlp(const char *file,const char *arg,...);

int execle(const char *path, const char *arg,..., char *const envp[]);

int execv (const char *path, char *const argv[]);

int execvp  (const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[], char *const envep[]);



在一个程序中运行一个子程序,通常的模式是fork然后exec,父进程继续执行原先的程序,exec执行子程序。


nice友善的,程序配发好人卡,程序对CPU的时间很友善,可以将CPU时间让给其他程序使用,niceness值越高说明程序越友善,其获得的CPU时间越少,运行越慢。

nice 命令,改变程序的运行优先级(-20到19),数值越大,运行优先级越低。

只有root权限的进程才能拥有负值的niceness或者减少运行进程的niceness值;

nice -n 数值 命令....

renice命令,重新改变运行中的程序的优先级。

在程序中改变程序的优先级,使用nice函数,它提供的值是一种增量,会被添加到原来的niceness值上。


信号

信号是linux中的一种机制,用于管理进程和进程间通信。

信号是一种发送给进程的特殊的消息。信号是异步的,当一个进程接受到信号后,它立刻停止当前的工作,并开始处理接受到的信号。有十几种信号类型,每种信号都有一个类型(用整数表示),信号的定义在/usr/include/bits/signum.h头文件中,不用在程序中直接include该文件,只要在程序中include<signal.h>文件即可。

每个信号都有默认的响应例程,如果程序没有指定接受到信号时该做什么操作,则就会使用该信号的默认响应例程。

在指定的情况下,系统会向进程发送信号。

一个进程可以向另一个进程发送信号。一个进程向另一个进程发送SIGTERM或SIGKILL信号,将会杀死另外一个进程。一个进程另外一个进程发送命令,用户定义的信号:SIGUSR1和SIGUSR2。信号SIGHUP常用来唤醒一个程序或让改程序重新读取配置文件。

SIGBUS,总线错误

SIGSEGV,段错误

SIGFPE,浮点异常

SIGINT,用户输入ctrl+c来终止程序

SIGTERM,表示让进程终止,进程可以【忽略/标记】该信号

SIGKILL,表示让进程终止,进程会被立刻终止(不会被阻塞或被程序"处理")

信号的默认响应例程会终止进程,并产生一个核心文件。

函数sigaction函数可以用来设置进程接受到信号时的响应例程。

头文件<signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

signum,信号的类型号

act,指针,指向sigaction结构,包含预期的信号处理例程

oldact,指针,指向前一个sigaction结构,包含旧的信号处理例程

重要的结构是sigaction中的sa_handler。

SIG_DFL,指定处理信号的默认的例程

SIG_IGN,说明该信号应该被忽略

指针,指向signal-handler函数,该函数接受一个参数(信号的num)并且返回void,函数原型是:void f(int)

signal-handler函数应该进行最少的必要的操作用来响应指定的信号,然后将控制权限返回给主程序或者终止程序。

signal-handler函数可以被另一个信号所中断。


给全局变量赋值可能要2个或更多的机器指令才能完成,在此期间,可能会产生新的信号中断赋值操作,从而导致不完整的赋值。sig_atomic_t类型,linux确保对这种类型变量的赋值使用1个机器指令就可以完成,从而不可能被其他信号中断。在linux中sig_atomic_t类型是int的别名。在linux中对整数的赋值(int或跟小类型,指针类型)都是原子操作。


void *memset(void*s, int c, size_t n);头文件<string.h>

该函数用c填充由s指向的内存的前n个字节,返回s指向的内存的指针。


进程的终止

一般来说,进程终止有两种:程序调用exit函数,或程序从main函数返回。每个进程都会有一个exit-code:整数,进程返回给它的父进程。exit-code是传递给exit函数的整数或main函数的返回值。

进程还可以非正常结束,例如响应一个信号而结束。通过调用abort函数,进程向自己发送SIGABRT信号,这会终止进程,并产生一个核心文件。


终止一个有问题的进程:

$kill -KILL pid,向指定的进程发送SIGKILL信号,立刻终止该进程

由程序发送信号,使用kill函数:int kill(pid_t pid, int sig);第一个参数时进程的pid,第二个参数时信号num

kill函数需要的头文件是:<sys/types.h>和<signal.h>


传统上来说,exit-code用来表示程序是否正常执行完毕并退出。0表示正常结束执行,非0表示程序执行遇到错误。

shell假定,当用&&或||将多个程序链接起来的时候,0表示程序正常执行完毕并结束,非0表示程序执行遇到错误。

即使exit函数的参数是int类型/return的类型也是int,linux系统不会保存完整的32-bit的返回整数。可用的返回值的大小范围在[0,127]之间,返回值大于等于128以后有特殊含义。当一个进程被一个信号终止时,它的exit-code是128加上signal-num。

Linux是多任务操作系统,进程是同时执行的,不能预测子进程在何时被执行。如果希望父进程等待子进程执行完毕后再执行,可以执行wait函数家族的系统调用。让程序等待子进程的执行完毕,并获取子进程相关的信息。


pid_t wait(int *status);等待任意子进程的结束,阻塞程序的执行,直到有一个子进程执行完毕

pid_t waitpid(pid_t pid, int *status, int options);等待指定子进程的结束

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

wait3函数返回子进程使用CPU的统计情况;wait4函数允许通过额外的选项指定等待的进程。

wait3或wait4接受额外的参数,例如(WNOHANG,表示非阻塞模式)它会清理已经执行完毕的子进程,如果没有执行完毕的子进程则立刻返回。如果有执行完毕的子进程被清理,则它返回执行完毕的子进程的ID,如果没有执行完毕的子进程则它返回0


宏WEXITSTATUS获取子进程的exit-code。

宏WIFEXITED用于判断子进程的退出状态(执行完毕正常退出?遇到未处理的信号而异常退出?)

宏WTERMSIG获取导致子进程终止的信号的num


僵尸进程

如果父进调用wait函数,子进程终止(消亡),子进程的exit-code会通过wait函数传递给父进程。当父进程没有调用wait函数,子进程终止(消亡)时,子进程的exit-code会被丢失,此时子进程会成为僵尸进程。

僵尸进程是已经终止的进程,但还没有被清理的进程。僵尸进程的清理工作是由其父进程负责的。wait函数也做这中工作。


如果子进程结束后通过某种方法通知父进程,这样是一种更加智能的方式。Linux有多种方式去完成这个目标:进程间通信。Linux自动在子进程执行完毕后,Linux给父进程发送一个信号SIGCHLD,该信号的默认例程不做任何事情。这样一种清理子进程的简单方式时处理SIGCHLD信号,子进程被清理后它的相关的状态信息就变的不可用。所以在清理前要获取相关子进程的状态信息(如果有必要的话)

0 0
原创粉丝点击