Linux系统编程系列(二)

2 篇文章 0 订阅

进程管理

进程运行后会进入执行状态:活跃active,活着alive,运行程序running program

进程IDprocess ID->pid;

进程若产生一个新的进程,则它称为父进程,新的进程成为子进程。

进程ID会表示为 pid_t类型

问题:

子进程创建成功之后,代码的执行位置?
父进程执行到了哪儿,子进程就从哪开始执行
父子进程的执行顺序?
不一定,谁抢到cpu谁执行
如何区分父子进程?
通过fork函数的返回值

fork

创建子进程

fork的返回值

​ >0:父进程的返回值

​ =0子进程的返回值

getpid/getppid

getpid得到子进程的pid

getppid得到父进程的pid

在这里插入图片描述

子进程运行时候,会从父进程执行到的位置开始执行,运行结果如下:

在这里插入图片描述

可以得到,得到的子进程的代码与父进程完全一致;

若要循环得到子进程,可以增加一个判断

for (int i = 0; i < 4; i++) {
                pid = fork();
                if(pid==0){
                	break;
                }
        }

则子进程不会再走fork操作了。

fork出来后,两个地址空间的用户区数据完全相同,但是后续进行了各自的操作,各个进程的地址空间中的数据是完全独立的。

共享全局变量,也就是说,全局变量在每个子进程与父进程中全局变量的改变,不会影响其他进程的该变量的值。

读时共享,写时复制。

创建兄弟进程

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

int main(int argc, char *argv[])
{
    pid_t pid = -1;

    int i = 0;

    for (i = 0; i < 3; ++i) {
        pid = fork();

        if (pid == 0)  //如果当前进程是子进程,就跳出此循环,不在进行 fork() 创建新的子进程
            break;
    }

    if (i == 0)
        printf("this is a child process (i == 0), pid = %d  ppid = %d\n", getpid(), getppid());

    if (i == 1)
        printf("this is a child process (i == 1), pid = %d  ppid = %d\n", getpid(), getppid());

    if (i == 2)
        printf("this is a child process (i == 2), pid = %d  ppid = %d\n", getpid(), getppid());

    if (i == 3)
        printf("this is a parent process (i == 3), pid = %d  ppid = %d\n", getpid(), getppid());

    return 0;
}

ps

用于查看系统中的进程状态,格式为“ps [参数]”。

ps a基础信息

ps au

ps aux 查看所有进程状态

在这里插入图片描述

可以用 ps aux | grep"xxx"ps ajx | grep"xxx"查看更详细的信息;

kill

杀死指定进程

kill -9 + 进程pid

exec函数族

让父子进程执行不相干的函数;

能够替换进程空间中的源代码段;

当前程序中调用另一个应用程序:要首先想到使用exec前需要fork

返回值:如果函数执行成功,不返回;失败则需要打出错误信息,退出子进程;

perror("execlp");
exit(1);

在这里插入图片描述

execl

启动另一个进程,常为自己写的程序;

执行指定目录下的程序.
int execl(const char *path, const char *arg, ..);
path:要执行的程序的绝对路径
变参arg:要执行的程序的需要的参数
第一arg:占位
后边的arg:命令的参数,该参数可以有很多
参数写完之后:NULL;标识该参数结束
一般执行自己写的程序
下面程序来操作ls命令

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

int main(int argc, const char* argv[]) {
	//创建一个子进程
	pid_t pid;
	
	//父进程数
	for (int i = 0; i < 8; i++) {
		printf("parent i = %d\n", i);
	}
	pid = fork();
	//让子进程执行ls命令
	if (pid == 0) {
        //ls用的是子进程的地址空间
		execl("/bin/ls", "666", "-lah", NULL);//NULL指编译结束
	}
	for (int i = 0; i < 3; i++) {
		printf("child i = %d\n", i);
	}
	
	
}

让父进程与子进程做不一样的操作

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

int main(int argc, const char* argv[]) {
        //创建一个子进程
        pid_t pid;

        //父进程数
        for (int i = 0; i < 8; i++) {
                printf("parent i = %d\n", i);
        }
        pid = fork();
        //让子进程执行
        if (pid == 0) {
          execl("/home/xiaoma/Xiaoma/process/hello", "hello", NULL);//NULL指编译结束
        }
        for (int i = 0; i < 3; i++) {
                printf("child i = %d\n", i);
        }


}
execlp

调用系统的命令;

执行PATH环境变量能够搜索到的程序
int execlp(const char *file, const char *arg, ..);
file:执行的命令的名字
■第一arg:占位
■后边的arg:命令的参数
■参数写完之后:NULL
■执行系统自带的程序
口/bin
execlp执行自定义的程序: file参数绝对路径
。int execvp(const char *file, char *const argv[]);
■参数:

2、进程回收

概念:

孤儿进程:

父进程产生子进程,父进程结束,子进程依旧运行,子进程由init进行领养,也就是成为了孤儿。

为了释放子进程占用的系统资源,进程结束后,能够释放用户区空间,因为子进程释放不了PCB,需要父进程进行释放;

僵尸进程

子进程结束,父进程仍在运行,但父进程不去释放子进程的pcb,则子进程成为僵尸进程;

父进程此时不能称为一个进程

进程回收
wait 阻塞函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用waitwaitpid得到它的退出状态同时彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

① 阻塞等待子进程退出

② 回收子进程残留资源

③ 获取子进程结束状态(退出原因)。

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait (int *status);

返回值:

​ -1:回收失败,无子进程

​ >0:回收是子进程对应的pid

参数: status
判断子进程是如何死的
正常退出
被某个信号杀死了
调用一次只能回收一个子进程

status–传出参数
获取退出时候的返回值(正常退出)
``return 0; – mainexit(0);WIFEXITED(status) > 0WEXITSTATUS(status)被信号杀死WIFSIGNALED(status)> 0`

WTERMSIG(status)

waitpid

作用同wait,但可指定pid进程清理,可以不阻塞。

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

成功:返回清理掉的子进程ID;

失败:-1(无子进程)

特殊参数和返回情况:

参数pid:

> 0 回收指定ID的子进程

-1 回收任意子进程(相当于wait)

​ 循环回收

while((wpid=waitpid(-1,&status,xx)!=-1));

​ ==0 回收当前调用waitpid一个组的所有子进程

< -1 回收指定进程组内的任意子进程

返回0:参3为WNOHANG,且子进程正在运行。

options

0-waitpid阻塞
WNOHANG-非阻塞

返回值:

-1:回收失败,无子进程

返回值>0:被回收的子进程

如果为非阻塞:

=0,子进程处于运行状态

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

作业

1.编写测试程序,测试父子进程之间是否共享文件。
共享文件-文件描述符
文件描述符-》pcb
打开一个文件- fd
fork();
父进程-write
子进程- read

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

int main(int argc, const char* argv[]) {
        int fd = open("temp", O_CREAT | O_RDWR, 0664);
        if (fd == 1) {
                perror("open");
                exit(1);
        }

        pid_t pid = fork();
        if (pid == -1) {
                perror("fork error");
                exit(1);
        }
        if (pid > 0) {
                char* p = "你猜我想干啥?";
                write(fd, p, strlen(p) + 1);
                close(fd);
        }
        else if(pid==0){
                //睡1s保证父进程已经完成了文件的写操作
                sleep(1);
                char buf[1024];
                lseek(fd, 0, SEEK_SET);
                int len = read(fd, buf, sizeof(buf));//读取输入
                printf("%s\n", buf);
                close(fd);
        }
        return 0;
}

在这里插入图片描述

2.父进程fork三个子进程:
其中一个调用ps命令;
一个调用自定义应用程序;
一个调用会出现段错误的程序。
父进程回收三个子进程(waitpid),并且打印三个子进程的
退出状态。

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

int main(int argc, const char* argv[]) {
        int num = 3;
        int i = 0;
        pid_t pid;
        //需要三个子进程
        for (i = 0; i < num; i++) {
                pid = fork();
                if (pid == 0) {
                        break;
                }
        }
        if (i == 0) {
                execlp("ls", "ls", NULL);
                perror("execlp ps");
                exit(1);
        }
        else if (i == 1) {
                execl("/home/xiaoma/Xiaoma/process/hello", "hello", NULL);
                perror("execl hello");
                exit(1);
        }
        else if (i == 2) {
                execl("./hello", "hello", NULL);
                perror("execl error");
                exit(1);
        }
        else if (i == num) {
                //回收
                int status;
                pid_t wpid;
                while ((wpid = waitpid(-1,&status,WNOHANG)) != -1) {
                        if(wpid==0){
                                continue;
                        }
                        printf("child died pid= %d\n", wpid);
                        if (WIFEXITED(status)) {
                                printf("return value %d\n", WEXITSTATUS(status));
                        }
						else if (WIFSIGNALED(status)) {
                                printf("died by signal: %d\n", WTERMSIG(status));
                        }
                }
        }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统编程是指在Linux操作系统下进行程序开发的过程和技术。它包括了使用系统调用、编写应用程序、处理文件和目录、进程管理等一系列操作。 在Linux系统编程中,可以使用系统调用来获取系统相关的信息,比如时间和日期。你可以使用time函数来获取当前的系统时间,date函数来获取当前的日期。这些函数可以帮助你获取到你所需要的系统信息。 此外,你可以使用Linux系统下的/proc虚拟文件系统来获取更详细的系统信息。在/proc目录下,每个进程都有一个对应的子目录,其中包含了该进程的相关信息。你可以读取这些文件来获取进程的状态、文件描述符等详细信息。 在Linux系统编程中,你可以使用system函数来执行shell命令。这个函数可以让你在程序中方便地执行任意的shell命令。比如,你可以使用system("ls -la")来执行ls命令来列出当前目录下的所有文件和文件夹。你也可以使用system("echo HelloWorld")来执行echo命令打印出HelloWorld。 除了上述内容,Linux系统编程还涉及到进程管理、文件和目录操作、进程通信等方面的内容。你可以使用fork函数创建子进程,使用exec函数执行新的程序,在程序之间进行进程通信等等。这些都是Linux系统编程中常见的任务和技术。 总结起来,Linux系统编程是在Linux操作系统下进行程序开发的过程和技术,包括使用系统调用、处理文件和目录、进程管理、进程通信等一系列操作。通过使用系统调用和/proc虚拟文件系统,你可以获取到系统的相关信息。同时,使用system函数可以方便地执行shell命令。这些都是Linux系统编程中常见的内容和技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值