【Linux】:进程管理

1.进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元

 

2.进程与程序的概念: 程序是放到磁盘的可执行文件;

                                         进程是指程序执行的实例。

3.区别:1.进程是动态的,程序是静态的:程序是有序代码的集合;进程是程序的执行。通

                                                                       常进程不可在计算机之间迁移;而程序通常对应                         

                                                                       着文件、静态和可以复制;

          2.进程是暂时的,程序使长久的:进程是一个状态变化的过程,程序可长久保存;

          3.进程与程序组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信

                                                     息);

          4.进程与程序的对应关系:通过多次执行,一个程序可对应多个进程;通过调用关    

                                                         系,一个进程可包括多个程序。

 

 

4.进程的生命周期:   1.创建:   每个进程都是由其父进程创建,进程可以创建子进程,子进

                                           程又可以创建子进程的子进程;                              

                                   2.运行:   多个进程可以同时存在,进程间可以通信;

                                   3.撤销:    进程可以被撤销,从而结束一个进程的运行。

5.进程的状态:有三种,分别是:

                   1) 执行状态:进程正在占用CPU;

                   2)就绪状态:进程已具备一切条件,正在等待分配CPU的处理时间片;

                   3}等待状态:进程不能使用CPU,若等待事件发生则可将其唤醒。

Linux系统是一个多进程的系统,它的进程之间具有并行性、互不干扰等特点。即使一个进程发生异常,它也不会影响到系统中的其他进程。

 

Linux 下进程包含三个段: 数据段代码段堆栈段

“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;

“代码段”存放的是程序代码的数据。

“堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。

在linux下,查看当前进程的口令是 ps -elf

如下:

其中 PID 就是进程的ID, 它是标识进程的唯一数字;

进程的父进程的ID就是PPID;

启动进程的用户ID是UID;

下面介绍几个进程的专业名词:

a.进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其

   他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。

b.操作系统中将一次只允许一个进程访问的资源称为临界资源。(进程中访问临界资源的那段程    

   序代码称为临界区,为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的界区)

c.一组并发进程按一定的顺序执行的过程称为进程间的同步

   具有同步关系一组并发进程称为合作进程,

  合作进程间互相发送的信号称为消息或事件。

d.进程调度:按一定算法,从一组待运行的进程中选出一个来占有CPU运行。

    调度方式:•  抢占式

                           •  非抢占式

    调度算法:先来先服务调度算法

                    短进程优先调度算法

                    高优先级优先调度算法

                          时间片轮转法

e.死锁:多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进

下面介绍如何获取进程的ID

	#include <sys/types.h> 
	#include <unistd.h>
	pid_t getpid(void)    	//获取本进程ID。
	pid_t getppid(void)     // 获取父进程ID

源代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    printf("pid : %d\n", getpid());
    printf("ppid : %d\n", getppid());
    while(1);

    return 0;
}

【注】:加while死循环是为了让这个进程不结束,才可以获取id。

  此时用ps查看如下:

此时若想停止此进程用kill命令:

下面就来介绍如何创建一个进程(好比我们编译结束后产生的hello可执行二进制文件,我们    运行: ./hello之后跑起来的程序,即可视为一个进程)

#include <unistd.h>

pid_t fork(void)

功能:创建子进程

      fork它被调用一次,却返回两次,它可能有三种不同的返回值:

                                                                                                 0:  子进程

                                                                                                子进程ID(大于0):父进程

                                                                                                 -1: 出错

值得注意的是fork( )创建子进程是大多数情况是先返回0,再返回子进程的ID。

测试代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    int a = 0;
    pid = fork();  //创建子进程
    
    if(-1 == pid)  //失败返回-1
    {   
        perror("fork");
    }   

    else if(0 == pid)  //子进程
    {   
        a++;
        printf("this is a childprocess pid : %d, ppid : %d\n", getpid(), getppid());
        printf("%d\n", pid);
        printf("a : %d\n", a); 
        printf("&a is %p\n", &a);
    }   

    else                       //父进程
    {   
        printf("this is a parentprocess pid : %d\n", getpid());
        printf("%d\n", pid);
        printf("a : %d\n", a); 
        printf("&a is %p\n", &a);
    }   
    return 0;
}

从中可以看出,子进程和父进程中的a结果不一样,但是地址却是一样的,这是为什么呢?

子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。

所以在子进程中对a进行加1的操作,并没有影响到父进程中的a值,父进程中的a值仍然为0。

 

下面介绍另个创建子进程的函数:vfork( )

#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void)

该函数创建子进程,共享父进程的地址空间!

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int a = 0;
    pid_t pid;
    pid = vfork();
    if(pid == -1) 
    {   
        perror("vfork");
        exit(1);
    }   

    else if(0 == pid)
    {   
        a++;
        printf("child process  a = %d\n", a); 
        exit(0);
    }   

    else
    {   
        a++;
        printf("parent process a = %d\n", a); 
    }   
    return 0;
}

值得注意的是vfork创建的子进程必须显示调用exit(0);来结束,否则子进程不能结束!

还有一条:子进程先运行父进程后运行。这点必须和fork区分开。

 

fork与vfork的区别:

fork

vfork

子进程拷贝父进程的数据

子进程与父进程共享数据

父、子进程的执行次序不确定

子进程先运行,父进程后运行

exec函数组:包含了几组不通过函数,用被执行的程序替换调用它的程序。

因为exec启动一个新程序,替换原有的进程,因此进程的PID不会改变。

下面介绍execl与execv

execl( )函数原型如下:

Int execl(const char *path, const char *arg,….)

path字符串代表文件的绝对路径,接下来的参数代表表传递过去的argv[0],argv[1]…..,最后一个必须是NULL结束

用于被执行的c代码:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i;
    for(i = 1; i < argc; i++)
    {   
        printf("%s\n", argv[i]);
    }   

    return 0;
}
接着要编译生成可执行二

接着要编译生成可执行二进制文件;

测试代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;

    pid = vfork();
    if(-1 == pid)
    {   
        perror("vfork");
        exit(1);
    }   execl
    else if(0 == pid)
    {   
        execl("/mnt/hgfs/allshare/163/jin程0815/test", "./test", "hello", "world", "123", NULL);
    }   
    else
    {   
        printf("this is parent!\n");
    }   

    return 0;
}

可以看出孤儿进程出现了,这是因为在vfork中子进程遇到execl( ),父进程就不用等子进程结束,遇到execl父进程也开始运行了,至于父和子谁先输出,这就看概率了。

接下来的execv( )函数跟execl( )差不多,下面来看代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    char *argv[] = {"./test", "hello", "world", "123", NULL};
    pid = vfork();
    if(-1 == pid)
    {   
        perror("vfork");
        exit(1);
    }   
    else if(0 == pid)
    {   
        execv("/mnt/hgfs/allshare/163/jin程0815/test", argv);
    }   
    else
    {   
        printf("this is parent!\n");
    }   

    return 0;
}

为了防止刚才那种情况出现,所以这里介绍另一个函数wait,

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait (int * status)

功能:阻塞该进程,直到其某个子进程退出。

 

测试代码:

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

int main()
{
    pid_t pid;
    pid = fork();
    
    if(-1 == pid)
    {   
        perror("fork");
    }   

    else if(0 == pid)
    {   
        sleep(2);
        printf("this is a childprocess !\n");
    }   

    else
    {   
        int status;
        printf("this is a parentprocess !\n");
        wait(&status);
        if(WIFEXITED(status))
        {
            printf("child process exit normally %d\n", WEXITSTATUS(status));
        }
    }   
    return 0;
}

wait是任意一个进程结束父进程就结束,

所以接下来升级一下:waitpid

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid (pid_t pid, int * status, int options)

功能:

会暂时停止目前进程的执行,直到有信号来到或子进程结束

options一般为0;

代码:

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

int main()
{
	pid_t pid;

	pid = fork();   //创建子进程 
	if (-1 == pid)   //失败返回-1
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)    //子进程
	{	
		sleep(1);
		printf("This is ChildProcess pid!\n");
		exit(100);
	}
	else                  //父进程
	{
		int status;
		printf("This is ParentProcess!\n");
		//wait(&status);  //1、等待子进程结束(阻塞)   2、回收子进程资源
		waitpid(pid, &status, 0);
		if (WIFEXITED(status))    //判断子进程是否正常退出
		{
			printf("Child Exit Normally! %d\n", WEXITSTATUS(status));  //获取子进程退出状态
		}
	}


	return 0;
}

结果跟上面一样。

 

下面介绍kill, 杀死进程(实际上是传递信号)

首先随便写一个死循环,获取它的PID:

#include <stdio.h>

int main()
{
    printf("%d\n", getpid());
    while(1)
    {   
        ;
    }   

    return 0;
}

实现kill函数代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>

int main()
{
    int id; 

    scanf("%d", &id);
    kill(id, 9); 


    return 0;
}

这时看一下死循环进程那边:

死翘翘咯!

 

其实还有更简单的杀死进程的方法,那就是raise 函数:

用法贼简单:

#include <stdio.h>

int main()
{
    printf("%d\n", getpid());
    
    raise(9);
    while(1)
    {   
        ;
    }   

    return 0;
}

这算不算是自杀

 

最后介绍一下signal函数:

 #include <signal.h>



typedef void (*sighandler_t)(int);



sighandler_t signal(int  signum, sighandler_t  handler);

他的意思就是当遇到信号signum就调用函数 handler

 

这里明确一点:我目前说的信号 1,2 3 啊之类的不是数字1,2,3,kill的选项:

如下:

Ctrl + c 对于进程来讲就是这里面的2;

Ctrl + z 就是9;

下面来看代码:

#include <stdio.h>
#include <signal.h>

void print(int num)
{
    printf("helloworld!\n");
}

int main()
{
    signal(2, print);
    
    while(1);

    return 0;
}

运行程序后我来个ctrl + c 就是信号2 就输出一个helloworld

输入口令 kill -2 26652  【注】:26652是signal的PID

另一边为:

signal后面不调用函数换成SIG_IGN就能忽略信号

#include <stdio.h>

#include <signal.h>



int main()

{

    signal(2, SIG_IGN);

   

    while(1);



    return 0;

}

这时候我按ctrl + c是没用的:

这时候我按ctrl + z 即为信号9 才能将这程序结束掉:

那么问题来了 我能不能忽略信号9呢?(ctrl + z 就是信号9)

代码实践一下咯!

#include <stdio.h>
#include <signal.h>

int main()
{
    signal(9, SIG_IGN);
    
    while(1);

    return 0;
}

这时我按下ctrl + z,猜猜看结果怎么样:

进程还是gg了,所以我们可以看出信号9 是不能被忽略的,他想杀谁谁也逃不掉,哈哈哈!

对了信号19也不能忽略,可以理解为这两个信号级别最高。

猜你喜欢

转载自blog.csdn.net/weixin_42720703/article/details/81712784