linux - 进程

进程

进程概念

触发 任何一个事件时,系统都会将他定义成为一个进程,并且给予这个进程一个 ID ,称为 PID,同时依据启发这个进程的用户与相关属性关系,给予这个 PID 一组有效的权限设定。

并行和并发

并发的概念是:

并发,在一个时间段内, 是在同一个cpu上, 同时运行多个程序。

如:若将CPU的1S的时间分成1000个时间片,每个进程执行完一个时间片必须无条件让出CPU的使用权,这样1S中就可以执行1000个进程。

在这里插入图片描述

并行性指两个或两个以上的程序在同一时刻发生(需要有多颗)。

在这里插入图片描述

PCB

每个进程都在内核中都有一个进程控制块来维护进程相关的信息,linux内核的进程控制块是task_stacut结构体

  • 进程ID。系统中每个进程都有唯一的ID,在C语言中用pid_t标识

  • 进程的状态,有就绪、运行、挂起、终止等状态

  • 进程切换的时候恢复寄存器

  • 描述虚拟地址空间

  • 控制终端的信息

  • 当前工作目录

  • umask掩码

  • 文件描述表

  • 信号相关信息

  • 用户ID和用户组ID

  • 会话和进程组

  • 进程可使用的资源上限

进程状态

在这里插入图片描述

处于就绪态的进程,有执行资格,但是没有CPU时间片

处于挂起态的进程即没有执行资格,也没有CPU时间片

处于挂起态的进程不可以直接回到运行态,必须回到就绪态

处于就绪态可以回到运行态

运行状态(TASK_RUNNING)
可中断睡眠状态(TASK_INTERRUPTIBLE)
不可中断睡眠状态(TASK_UNINTERRUPTIBLE)
暂停状态(TASK_STOPPED)
僵死状态(TASK_ZOMBIE)

创建进程

fork

创建子进程

pid_t fork(void);

调用成功后返回子进程的PID,子进程返回0;

如果失败,则返回-1

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,char* args[])
{
    printf("begin fork for pid = %d\n",getpid());
    //pid_t fork(void);
    pid_t pid = fork();
    if(pid<0)       //检查fork失败
    {
        perror("fork error");
        return -1;
    }
    else if(pid>0)
    {
        printf("parent pid = %d fpid = %d\n",getpid(),getppid());
        sleep(5);
        return 1;
    }
    else if(pid==0)
    {
        printf("child pid = %d fpid = %d\n",getpid(),getppid());

    }
    printf("fork ends for pid = %d\n",getpid());
    return 0;
}
}

父子进程不可以共享全局变量

进程间的全局变量遵循:写时复制,读时共享;

vfork

vfork创建完成子进程程必须立即执行_exit或者exec函数;


#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>
#include <sys/times.h>
#include <wait.h>
#include <sys/stat.h>
#include <fcntl.h>
//vfork函数
int main(int argc,char* args[])
{
	int ret = 0;
	int i = 10;
	pid_t pid = vfork();
	printf("pid == %d\n",getpid());
	if(pid == -1)
	{
		perror("fork error");
		return -1;	
	}	
	if(pid>0)
	{
		i++;
		printf("parent pid == %d\n",getpid());
		printf("i = %d\n",i);
		
	}
	if(pid==0)
	{	char *const argv[] = {"ls","-l",NULL};
		ret = execve("/bin/ls",argv,NULL);		//拉起ls函数
		printf("ret = %d\n",ret);
		exit(0);
	}
	
	return 0;
}

ps

常用命令:

ps -ajx
ps -aux
pf -ef | grep init

kill

kill -l
#获取所有的信号
kill -9 xxx
kill -sigstop xxx

命令执行

execl

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

执行execl函数后,子进程的代码段被命令的代码段替换;子进程的地址空间不会变化,子进程PID也不会改变

execlp

int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,char* args[])
{
    pid_t pid = fork();
    if(pid<0)       //检查fork失败
    {
        perror("fork error");
        return -1;
    }
    else if(pid>0)
    {

        printf("parent pid = %d fpid = %d\n",getpid(),getppid());

    }
    else if(pid==0)
    {

        printf("child pid = %d fpid = %d\n",getpid(),getppid());
        //execl("./hello","hello","hello","hello","hello",NULL);
        //execlp("./hello","hello","-l",NULL);
        execlp("ls","ls","-l",NULL);
        //execl("/bin/ls","ls","-l",NULL);
        perror("execl error");
    }
    return 0;
}
   

进程回收

当一个进程退出后,进程能够回收自己的用户区的资源,但是不能回收内核空间的PCB资源,必须由它的父进程调用wait或者waitpid函数对子进程进行回收,避免造成系统资源的浪费;

孤儿进程

孤儿进程的父进程已经死掉,而子进程还存活,这个进程就成为了孤儿进程;操作系统为了保证每个进程都有一个父进程,孤儿进程会被init进程领养,由init进程对孤儿进程进行回收;

模拟孤儿进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,char* args[])
{
    pid_t pid = fork();
    if(pid<0)       //检查fork失败
    {
        perror("fork error");
        return -1;
    }
    else if(pid>0)
    {
        exit(0);			//父进程先死
        printf("parent pid = %d fpid = %d\n",getpid(),getppid());

    }
    else if(pid==0)
    {
        printf("child pid = %d fpid = %d\n",getpid(),getppid());
        sleep(20);
        printf("child pid = %d fpid = %d\n",getpid(),getppid());
    }
}

僵尸进程

子进程先退出,父进程在等待子进程退出,这个时候子进程变成了僵尸进程;

僵尸进程并不能被kill杀死,但是我们可以杀死父进程来消灭僵尸进程,原因是杀死父进程后,僵尸进程就会被init进程回收

wait

阻塞线程等待子进程退出;回收残留线程;

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc,char* args[])
{
        pid_t pid = fork();
        pid_t w;
        int status;
        if(pid<0)               //检查fork失败
        {
                perror("fork error");
                return -1;
        }
        else if(pid>0)
        {
                printf("parent pid = %d fpid = %d\n",getpid(),getppid());
                w=wait(&status);
                printf("wpid = %d\n",w);
                if(WIFEXITED(status))//WIFEXITED宏的释义: wait if exit ed
                {
                    printf("子进程返回信息码:%d\n",WEXITSTATUS(status));
                }
                else if(WIFSIGNALED(status))
                {
                    printf("子进程信号中断返回信息码:%d\n",WTERMSIG(status));
                }
                else if(WIFSTOPPED(status))
                {
                    printf("子进程暂停返回信息码:%d\n",WSTOPSIG(status));
                }
                else
                {
                    printf("其他退出信息!\n");
                }

                }
        else if(pid==0)
        {

                printf("child pid = %d fpid = %d\n",getpid(),getppid());
                abort();
        }
        return 0;
}
systemk1t@systemk1t-virtual-machi

waitpid

pid_t waitpid(pid_t pid, int *status, int options);

参数pid:

pid = -1 等待任意子进程。与wait等效。

pid > 0 等待指定的子进程

pid = 0 等待进程组ID与目前进程相同的任何子进程,也就是说任何和调用

waitpid()函数的进程在同一个进程组的进程。

参数options:

设置为WNOHANG,函数非阻塞,设置为0,函数阻塞。

返回值:

如果返回值大于0:回收的子进程的PID

如果返回值等于0:若options取值为WNOHANG,则标识子进程还存在

如果返回值等于-1:标识没有子进程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc,char* args[])
{
    pid_t pid = fork();
    pid_t w;
    int status;
    if(pid<0)       //检查fork失败
    {
        perror("fork error");
        return -1;
    }
    else if(pid>0)
    {
        printf("parent pid = %d fpid = %d\n",getpid(),getppid());
        w = waitpid(-1,&status,WNOHANG);
        printf("wpid = %d\n",w);
    }
    else if(pid==0)
    {

        printf("child pid = %d fpid = %d\n",getpid(),getppid());
        //abort();
        //return 9;
        //exit(0);
        sleep(5);
    }
    return 0;
}

status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充
如果传递NULL,表示不关心子进程的退出状态信息,
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程

status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)

在这里插入图片描述

正常退出
status & 0x7f,   如果是0的话,那就表示正常退出
0xff是0111111,status 按位与0x7f,表示取出status的低7位
异常退出
status & 0x7f,   不为0的话,那就表示异常退出
判断子进程正常/异常退出的时候:
status & 0x7f(status按位与0x7f)的时候,按位与7位就好,如果按位与的结果是0,那就表示子进程正常退出,按位与的结果不是0,那就表明子进程异常退出
阻塞式等待&非阻塞式等待
1.阻塞式等待
   子进程先执行,而且父进程会等待子进程执行结束,再去执行自己
2.非阻塞式等待
   子进程和父进程谁先执行不确定,父进程如果先执行的话,父进程会一边等待
   子进程而另一边又做自己的事情,即就是等待子进程和执行自己的事情是同步
   进行的

status参数:

WIFEXITED(status)		//非0 进程正常退出
WEXITSTATUS(status)		//获取进程退出状态
WIFSIGNALED(status)		//非0 进程异常终止
WTERMSIG(status)		//取得进程终止的信号编号

abort

  • 立即终止当前进程,产生异常程序终止
  • 进程终止时不会销毁任何对象

exit

  • 正常终止进程,并进行清理!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值