您的位置:首页 > 运维架构 > Linux

Linux浅谈进程2

2017-12-14 11:48 381 查看
此篇博客作为上一篇博客的补充

Linux浅谈进程1

进程的创建

进程创建的方式:

fork()

vfork()

关于fork()函数

头文件为#include<unistd.h>
pid_t fork(void)
返回值:
子进程返回0,父进程返回子进程id,出错返回-1


进程创建的一般过程

给新建的进程分配一个内部的标识符,在内核中分配PCB。

复制父进程的环境

为进程分配资源(代码,数据,堆栈)

父进程地址空间的内容也复制到新的进程空间中

将该进程放到就绪队列中。

向父进程返回子进程的进程号,对子进程返回0

当一个进程fork出一个子进程后,就有两个二进制代码相同的进程,并且运行到相同的地方,但每个进程都将开始执行自己的代码。

fork()调用失败的原因

系统中进程太多了

或实际用户的进程数超过了限制(每台电脑都有一个进程的最大数)

系统不支持fork()函数,(如windows)

vfork函数

也是用来创建子进程(在fork函数没有出现写时拷贝时出现的,性能高,但是有Bug)

子进程一定先于父进程执行。

子进程调用exec或者exit之后父进程才能执行

vfork与fork区别

fork:父子进程交替运行。vfork:子进程运行,父进程阻塞,直到子进程运行结束(创建出子进程,子进程运行完了才会运行父进程),程序最后用exit(0)进程退出

fork实现了写时拷贝。vfork就算是写也不拷贝

vfork必须使用exit或excl

就算是fork实现了写时拷贝,性能也没有vfork高

vfork虽然性能高,但是每个系统上的vfork都有问题,不要使用

进程的终止(结束)

进程退出的场景:

代码运行完毕,结果正确

代码运行完毕,结果不正确

代码异常终止

进程常见退出方法

正常终止

正常退出返回码为0

1. 从main返回

2. 调用exit

3. _exit

异常退出:

ctrl+C

kill

abort()

查看进程退出码

可以用echo$?查看进程的退出码

echo $?:查看上一个退出码 退出码返回0~255 只用了int的8个比特位,其余位有别的用途,比如,程序是否是正常退出,如果是异常退出,是什么原因导致异常退出

exit所做的工作

执行用户通过atext或on_exit定义的清理函数

关闭所有打开的流,所有的缓冲数据均被写入

调用_exit.

回调处理函数atexit(函数名):在程序结束前执行。有32个上限,运行顺序与定义顺序相反

_exit与exit区别

_exit()直接终止,清理缓冲区。

exit()直接退出程序

_exit是系统调用,exit最终也会调用_exit

进程的撤销(销毁)

关闭软中断

回收资源(如关闭文件)

将进程的状态置为僵尸态

写记账信息

转进程调度

进程的等待

进程等待是一个很重要的过程,如果子进程退出,父进程不回收资源,就会造成我们之前博客讲过的“僵尸进程”的问题,从而导致内存泄漏

进程等待就可以很好的解决这个问题

父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

进程等待方法

wait

waitpid

wait

//#include<sys/types.h>
//#include<sys/wait.h>   //头文件

pid_t wait(int* status)
返回值:成功返回被等待进程pid,失败返回-1
参数:输出型参数,获取子进程的退出状态,不关心可以设置为NULL


wait:做的三件事

阻塞当前进程,直到子进程退出才返回

回收子进程残留资源

获取子进程退出信息(状态)

waitpid

pid_t waitpid(pid_t pid,int *status,int options);
返回值:
当正常返回时waitpid返回收集到的子进程的进程ID
如果设置了选项WHOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0
如果调用中出错,则返回-1,这是errno会被设置成相应的值以指示错误所在
参数:
pid:
pid=-1,等待任一个子进程,与wait等效。
pid >0,等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status):若为正常终止子进程返回的状态,则为真(查看进程是否正常退出)
WEXITSTATUS(status):若WEXITSTATUS非零,提取子进程退出码(查看进程的退出码)
options:
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不等待,若正常结束,返回该子进程的ID。


用代码看一下

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>

int main()
{
pid_t pid;
if((pid=fork())==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
int i=0;
printf("this is maomaochong\n",getpid());
sleep(1);

exit(-1);
}
else
{
int status;
pid_t w=wait(&status);
if(w==-1)
perror("wait");
else
printf("parent w=%d\n",w);
if(WIFEXITED(status))
{
printf("exitcode=%d\n",WEXITSTATUS(status));

}
else if(WIFSIGNALED(status))
{
printf("signum=%d\n",WTERMSIG(status));
}
}
}


结果为:



我们通过结果可以看出,进程正常退出。

进程程序替换

替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

替换函数:

有六种以exec开头的函数,都统称为exec函数

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


函数解释:

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。

如果调用出错则返回-1

所以exec函数只有出错的返回值而没有成功的返回值

命名解释

l(list):表示参数采用列表

v(vector):表示参数采用数组

p(path):带p的函数会自动搜索环境变量PATH

e(env):表示自己维护环境变量
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: