Linux应用编程之——进程

Linux应用编程之——进程

进程:这个我想大家都应该有所了解,进程是进程实体的运行过程,是系统进行资源分配和调度的基本单位,进程由PCB(进程控制块)控制。
就好比什么呢,你在shell下面,可以同时运行好几个main函数,这些就是代表好几个进程。

进程状态

进程有如下几种状态:
R 运行状态。严格来说,应该是”可运行状态”,即表示进程在运行队列中,处于正在执行或即将运行状态,只有在该状态的进程才可能在 CPU 上运行,而同一时刻可能有多个进程处于可运行状态。
S 可中断的睡眠状态。处于这个状态的进程因为等待某种事件的发生而被挂起,比如进程在等待信号。
D 不可中断的睡眠状态。通常是在等待输入或输出(I/O)完成,处于这种状态的进程不能响应异步信号。
T 停止状态。通常是被shell的工作信号控制,或因为它被追踪,进程正处于调试器的控制之下。
Z 退出状态。进程成为僵尸进程。
X 退出状态。进程即将被回收。
s 进程是会话其首进程
l 进程是多线程的
. 进程属于前台进程组。
< 高优先级任务。
在这里插入图片描述

相关函数

1、创建进程

启动进程

int system(const char *command);

这个system ()函数是C标准库中提供的,它主要是提供了一种调用其它程序的简单方法。读者可以利用system()函数调用一些应用程序,它产生的结果与从 shell中执行这个程序基本相似。事实上,system()启动了一个运行着/bin/sh的子进程,然后将命令交由它执行。

pid_t fork(void);//不接收参数。调用一次会返回两次!!!
//返回值等于0的就是子进程,返回值大于0的就是父进程。

init进程可以启动一个子进程,它通过fork()函数从原程序中创建一个完全分离的子进程,当然,这只是init进程启动子进程的第一步,后续还有其他操作的。不管怎么说,fork()函数就是可以启动一个子进程
具体的创建方式如下

main()
{
	pid_t pid=fork();
	if(pid==0){  //子进程
	}
	if(pid>0{//父进程
	}
	if(pid<0{//出错
	}
}

在这里插入图片描述

注意点:如果子进程查询父进程pid的时候,如果父进程已经死掉了,那么就会直接返回init进程的pid
可以通过命令 ps -aux | grep “1990” 查看
ps -aux
-A :所有的进程均显示出来,与 -e 具有同样的效用;
-a : 显示现行终端机下的所有进程,包括其他用户的进程;
-u :以用户为主的进程状态 (当前用户);
x :通常与 a 这个参数一起使用,可列出较完整信息。

2、获取进程

getpid(当前pid)   
getppid(当前父pid)  
getuid   
geteuid 
getgid 
getegid

3、 exce系列函数

事实上,使用fork()函数启动一个子进程是并没有太大作用的,因为子进程跟父进程都是一样的,子进程能干的活父进程也一样能干,因此世界各地的开发者就想方设法让子进程做不一样的事情,因此就诞生了exce系列函数,这个系列函数主要是用于替换进程的执行程序,它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。简单来说就是覆盖进程,举个例子,A进程调用exce系列函数启动一个进程B,此时进程B会替换进程A,进程A的内存空间、数据段、代码段等内容都将被进程B占用,进程A将不复存在。
exec 族函数有 6 个不同的 exec 函数,函数原型分别如下:

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 execve(const char *path, char *const argv[], char *const envp[])

这些函数可以分为两大类, execl、 execlp和execle的参数个数是可变的。execv、execvp和execve的第2个参数是一个字符串数组,参数以一个空指针NULL结束,无论何种函数,在调用的时候都会通过参数将这些内容传递进去,传入的参数一般都是要运行的程序(可执行文件)、脚本等。

总结来说,可以通过它们的后缀来区分他们的作用:

名称包含 l 字母的函数(execl、 execlp 和execle)接收参数列表”list”作为调用程序的参数。

名称包含 p 字母的函数(execvp 和execlp)接受一个程序名作为参数,然后在当前的执行路径中搜索并执行这个程序;名字不包含p字母的函数在调用时必须指定程序的完整路径,其实就是在系统环境变量”PATH”搜索可执行文件。

名称包含 v 字母的函数(execv、execvp 和 execve)的命令参数通过一个数组”vector”传入。

名称包含 e 字母的函数(execve 和 execle)比其它函数多接收一个指明环境变量列表的参数,并且可以通过参数envp传递字符串数组作为新程序的环境变量,这个envp参数的格式应为一个以 NULL 指针作为结束标记的字符串数组,每个字符串应该表示为”environment =

virables”的形式。

下面作者就具体某个函数做介绍:

函数:

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

execl()函数用于执行参数path字符串所代表的文件路径(必须指定路径),接下来是一系列可变参数,它们代表执行该文件时传递过去的argv[0]、argv[1]… argv[n],最后一个参数必须用空指针NULL作为结束的标志。

4、 终止进程

在Linux系统中,进程终止(或者称为进程退出,为了统一,下文均使用”终止”一词)的常见方式有5种,可以分为正常终止与异常终止:

正常终止:

从main函数返回。
调用exit()函数终止。
调用_exit()函数终止。
异常终止:

调用abort()函数异常终止。
由系统信号终止。
在Linux系统中,exit()函数定义在stdlib.h中,而_exit()定义在unistd.h中,exit()和_exit()函数都是用来终止进程的,当程序执行到exit()或_exit()函数时,进程会无条件地停止剩下的所有操作,清除包括 PCB在内的各种数据结构,并终止当前进程的运行。
在这里插入图片描述

5、 wait

函数原型

pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);

waitpid()函数 的作用和wait()函数一样,但它并不一定要等待第一个终止的子进程,它还有其他选项,比如指定等待某个pid的子进程、提供一个非阻塞版本的wait()功能等。实际上 wait()函数只是 waitpid() 函数的一个特例,在 Linux内部实现 wait 函数时直接调用的就是 waitpid 函数。
wait()函数有几点需要注意的地方:

wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID。
参数wstatus用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针,但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样做),我们就可以设定这个参数为NULL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章