Linux应用软件编程---多任务(进程2)(资源回收函数(wait、waitpid)、exec函数族、linux下的命令、const四种位置表示的含义)

发布于:2025-08-17 ⋅ 阅读:(21) ⋅ 点赞:(0)

一、进程回收资源空间函数

1、wait()

    1)需要包含头文件

       #include <sys/types.h>
       #include <sys/wait.h>

    2)解释说明

pid_t wait(int *wstatus);
功能:阻塞等待回收子进程的资源空间
参数:
      wstatus :保存子进程消亡状态的变量地址
                       若为NULL:不保存子进程退出的状态
返回值:
       成功:返回回收到的子进程的PID号
       失败:-1
     wait(NULL);

        可以在子进程结束时,通过 return 或 exit 来实现给 wait 函数的传参操作。

2、waitpid()

    1)需包含头文件

       #include <sys/types.h>
       #include <sys/wait.h>

    2)解释说明

 pid_t waitpid(pid_t pid, int *status, int options);
功能:回收指定进程的资源。和wait功能相似,比wait更灵活
参数:
     pid:回收范围
           <-1   回收指定进程组内的任意子进程 (-100.等待GID=100的进程组中的任意子进程)
            -1    回收任意子进程,组内外
            0     回收和当前调用waitpid一个组的所有子进程,组内 
           > 0   回收指定ID的子进程
     status:子进程退出时候的状态,
              如果不关注退出状态用NULL;
     options 选项:
                     0             表示回收过程会阻塞等待
             WNOHANG    表示非阻塞模式回收资源。
返回值: 成功 返回接收资源的子进程pid
               失败  -1
               设定为非阻塞且没有回收到子进程返回0 

3、wait函数与waitpid函数的宏

    1)WIFEXITED(status)

        判断子进程是否正常终止,若正常终止返回非零 (真) ,否则返回0 (假)。

    2)WEXITSTATUS(status)

        正常终止时,获取子进程的退出状态码,即子进程退出时返回的值。

    3)WIFSIGNALED(status)

        判断子进程是否被信号终止 (如被 kill 命令发送信号终止),若被信号终止返回非零 (真),否则返回0 (假)。

    4)WTERMSIG(status)

        获取导致子进程终止的信号的编号。

    5)WIFSTOPPED(status)

        判断子进程是否被信号暂停,若子进程被暂停返回非零 (真),否则返回0 (假)。

        *注:仅在使用 waitpid 且指定 WUNTRACED 选项时才可能为真。

    6)WSTOPSIG(status)

        获取导致子进程暂停的信号编号。

4、子进程资源空间回收策略

    1)wait 阻塞回收:一般情况下,父进程专门负责资源回收;
    2)waitpid 非阻塞方式回收:搭配轮询方式回收。
    3)不回收:子进程的任务需要一直执行
    4)异步回收:当子进程结束时通知父进程进行回收

5、例题

    1)wait() 函数

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

int main(int argc, const char *argv[])
{
	pid_t pid = fork(); //创建子进程
	if (pid > 0) //父进程
	{
		int wstatus;
		pid_t pidtmp = wait(&wstatus);
		printf("pidtmp = %d\n", pidtmp);
		if (WIFEXITED(wstatus))
		{
			printf("%d over normall, state : %d\n", pidtmp, WEXITSTATUS(wstatus));
		}
		else if (WIFSIGNALED(wstatus))
		{
			printf("%d ober by signal,signal num = %d\n", pidtmp, WTERMSIG(wstatus));
		}

		while (1)
		{
			printf("I am father : pid= %d\n", getpid());
			sleep(1);
		}
		
	}
	else if (0 == pid) //子进程
	{
		int i = 20;
		while (i--)
		{
			printf("I am son : pid = %d\n", getpid());
			sleep(1);
		}

		exit(9); //终止信号为 9 号 SIGKILL
		//return 9;
	}
	else
	{
		perror("fork error");
	}
	return 0;
}

    2)waitpid() 函数

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

int main(int argc, const char *argv[])
{
	pid_t pid = fork();
	if (pid > 0)
	{
		while (1)
		{
			int wstatus;
			//pid_t pidtmp = waitpid(-1, &wstatus, 0);  ==>wait(&wstatus)
			pid_t pidtmp = waitpid(-1, &wstatus, WNOHANG);
			printf("pidtmp = %d\n", pidtmp);
			if (0 != pidtmp)
			{
				if (WIFEXITED(wstatus))
				{
					printf("%d over normall, state : %d\n",pidtmp,WEXITSTATUS(wstatus));
				}
				else if (WIFSIGNALED(wstatus))
				{
					printf("%d ober by signal,signa is= %d\n",pidtmp,WTERMSIG(wstatus));
				}
			}
			printf("I am father : pid= %d\n", getpid());
			sleep(1);
		}
	}

//轮询
	else if (0 == pid)
	{
		int i = 20;
		while (i--)
		{
			printf("I am son : pid = %d\n", getpid());
			sleep(1);
		}
		exit(9);
		//return 9;
	}
	else
	{
		perror("fork error");
	}
	return 0;
}

 

二、exec 函数族

1、概念

        功能:在一个进程里面执行另外一个文件(可执行文件)。
        本质:将文本去的指令代码替换成exec要执行的文件的指令。

2、分类

        exec 函数族分为 execl、execlp、execle、execv、execvp、execvpe六种,下面对这六种中的函数部分进行说明。

    1)execl()

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

参数:
       path:要执行的可执行文件的路径和名称
       arg:执行该可执行文件时需要传递的参数

                省略号部分填写与 arg 同类的参数,直至填写完毕
       NULL :参数传递结束标志
返回值:
       出错:-1

    2)execlp()

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

功能:从PATH指定的系统路径下寻找该可执行文件

           * 不可执行自己编写的可执行文件。
参数:
        file:需要执行的可执行文件的名称(系统路径下已有的文件)
        arg: 执行该可执行文件时需要传递的参数

    3)execv()

        execv() 与 execl() 作用一样,只是 execv() 被用于绝对路径中的替换执行。

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

参数:

        psth:绝对路径
        argv:指针数组
举例:实现 ls -l 功能

            char *arg[] = {"ls", "-l", NULL);
            execv("/bin/ls", arg);

    4)exec 说明

        l:list 列表

        p:path 路径 (系统路径)

        v:vector 容器

        e:env 环境变量

    5)举例说明

        (1)上述函数的使用格式实例

#include <stdio.h>
#include <unistd.h>

int main(int argc, int *argv[])
{
    printf("exec : pid = %d\n", getpid());
    execl("./hello", "./hello", NULL);

    execl("/bin/ls", "ls", "-l", NULL);

    char *arg[] = {"is", "-l", NULL};
    execv("/bin/ls", arg);

    execlp("ls", "ls", "-l", NULL);
    execvp("ls", arg);
    return 0;
}

        (2)使用 exec函数族 实现 Linux 中 system 函数的功能。

核心思想:

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

int my_system(char *buff)
{
	char *arg[10] = {NULL};
	int i = 0;
	char cmd[512] = {0};
	strcpy(cmd, buff);

	arg[i] = strtok(cmd, " ");
	while (arg[i] != NULL)
	{
		i++;
		arg[i] = strtok(NULL, " ");
	}

	pid_t pid = fork();
	if (pid > 0) //父进程
	{
		wait(NULL);
	}
	else if (0 == pid) //子进程
	{
		execvp(arg[0], arg);
	}
	return 0;
}

int main(int argc, const char *argv[])
{
	my_system("ls -l");

	return 0;
}

三、Linux 下的命令

    1. 内核内置命令:cd
    2. 外部命令:ls、rm、 touch、whereis 命令
    3. 别名:例如:ll <---> (ls -l)

四、const 四种存放位置的解析

        下述说明中假定指针名为 p。

1、const 类型 *指针名

        含义:指向常量字符的指针。

        说明:指针 p 本身可以被修改,可以指向其他地址,但 p 指向的字符内容 (*p) 不能被修改 (被 const 限定) 。例如:

const char *p = "hello";
p = "word"; //正确(指针可以指向新地址)
//*p = 'H'; //错误(不能修改指向的内容)

2、类型 *const 变量名

        含义:指向字符的常量指针。

        说明:指针 p 本身是常量,不能被修改,不能指向其他地址,但 p 指向的字符内容 (*p) 可以被修改。例如:

char str[] = "hello";
char *const p = str;
// p = "world"; // 错误(指针本身不能指向新地址)

*p = 'H'; //合法(可以修改指向的内容)

3、const 类型 变量名

        含义:修饰普通变量,变量的值不能修改,本身是只读的。

        说明:const 修饰后的变量的值在初始化之后,不允许通过赋值等操作修改。例如:

const char p = 'A';
p = 'B'; //错误(不能修改变量的值)

4、const 类型 *const 指针名

        含义:指向常量字符的常量指针。

        说明:指针 p 本身是常量,不能被修改,不能指向其他地址,同时 p 指向的字符内容 (*p) 也不能被修改。例如:

const char *const p = "hello";
p = "world"; //错误(指针本身不能修改)
*p = 'H'; //错误(指向的内容也不能修改)

五、minshell 功能的实现

1、流程图

2、程序代码

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

int my_system(char *buff)
{
    if(buff == NULL || strlen(buff) == 0)
    {
        return 0;
    }

	char *arg[10] = {NULL};
	int i = 0;
	char cmd[1024] = {0};
	strcpy(cmd, buff);

	arg[i] = strtok(cmd, " ");
	while (arg[i] != NULL)
	{
		printf("arg[%d] = %s\n", i, arg[i]);
		i++;
		arg[i] = strtok(NULL, " ");
	}

	pid_t pid = fork();
	if (pid > 0) //父进程
	{
		wait(NULL);
	}
	else if (0 == pid) //子进程
	{
		execvp(arg[0], arg);
        perror("execvp failed");
        exit(1);
	}
    else
    {
        perror("fork failed");
        return -1;
    }
	return 0;
}

int main(int argc, const char *argv[])
{
    while(1)
    {
	    char buff[1024] = {0};
	    getcwd(buff, sizeof(buff));
	    printf("zmy@ubuntu:%s:", buff);
        
        char p[1024] = {0}; 
        fgets(p, sizeof(p), stdin);
        p[strcspn(p, "\n")] = '\0';
        if(strcmp(p, "q") == 0)
        {
            break;
        }
	    my_system(p);
    }
	return 0;
}

3、运行结构示意图

 

【END】

 


网站公告

今日签到

点亮在社区的每一天
去签到