操作系统进程控制实验

🔥博客主页:真的睡不醒

🚀系列专栏:深度学习环境搭建环境配置问题解决自然语言处理语音信号处理项目开发

💘每日语录:每一步都算数,即使你觉得前进得缓慢,你仍在超越那些停滞不前的人。

🎉感谢大家点赞👍收藏⭐指正✍️

目录

一、vi编译器

1 vim基本概念

1.1 命令行模式(command mode)

1.2 插入模式(Insert mode)

1.3 底行模式(last line mode)

2 vim基本操作

2.1 文件的打开、新建、编辑和保存

2.2 通过gcc编译

二、linux进程控制

三、进程控制Lockf()

四、管道控制


一、vi编译器

1 vim基本概念

基本上vi可以分为三种状态,分别是命令行模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:

1.1 命令行模式(command mode)

控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode或last line mode下。

1.2 插入模式(Insert mode)

只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。

1.3 底行模式(last line mode)

将文件保存或退出vi,也可以设置编辑环境,如寻找字符串、列出行号等。

       不过一般我们在使用时把vi简化成两个模式,就是将底行模式(last line mode)也算入命令行模式command mode)。

2 vim基本操作

2.1 文件的打开、新建、编辑和保存

打开或新建一个文件,打开后点击 “ i ” 对文件进行插入操作。

$vi test2.c

1)test.c是文件名字。

2)如果文件存在,输入结束后,:wq保存并退出文件
3)如果文件不存在,输入结束后,:wq就可以新建并保存文件

当我们编辑好文件时,首先按esc退出插入模式。再输入“:wq ”,退出并保存

1)输入:w则保存文件,如果已经保存文件,输入:q则退出文件
2)直接输入:wq保存并退出
3)   如果不想保存被修改的内容,则:q!强制退出。

2.2 通过gcc编译

在2.1我们保存了test.c文件

gcc test2 -o hello

再输入

./hello

运行结果:

二、linux进程控制

1.编写一C语言程序,实现在程序运行时通过系统调用fork( )创建两个子进程,使三进程并发执行,父亲进程执行时屏幕显示“I am father”,儿子进程执行时屏幕显示“I am son”,女儿进程执行时屏幕显示“I am daughter”

代码实现如下:

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

int main() {
    pid_t pid1, pid2;

    // 创建第一个子进程
    pid1 = fork();

    if (pid1 == 0) {
        // 子进程1
        printf("I am son\n");
    } else if (pid1 > 0) {
        // 父进程
        // 创建第二个子进程
        pid2 = fork();

        if (pid2 == 0) {
            // 子进程2
            printf("I am daughter\n");
        } else if (pid2 > 0) {
            // 父进程
            printf("I am father\n");
        } else {
            perror("fork");
            return 1;
        }
    } else {
        perror("fork");
        return 1;
    }

    return 0;
}

修改程序,在三进程中分别使用waitexitlockf等系统调用“实现”其同步推进,多次反复运行改进后的程序,观察并记录运行结果。

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

int main() {
    pid_t pid1, pid2;
    int fd;

    // 创建一个文件作为锁文件
    fd = open("lockfile", O_CREAT | O_RDWR, 0666);

    // 创建第一个子进程
    pid1 = fork();

    if (pid1 == 0) {
        // 子进程1
        lockf(fd, F_LOCK, 0); // 锁住文件
        printf("I am son\n");
        lockf(fd, F_ULOCK, 0); // 解锁文件
        _exit; // 子进程1结束
    } else if (pid1 > 0) {
        // 父进程
        wait(NULL); // 等待子进程1结束
        // 创建第二个子进程
        pid2 = fork();

        if (pid2 == 0) {
            // 子进程2
            lockf(fd, F_LOCK, 0); // 锁住文件
            printf("I am daughter\n");
            lockf(fd, F_ULOCK, 0); // 解锁文件
            _exit; // 子进程2结束
        } else if (pid2 > 0) {
            // 父进程
            wait(NULL); // 等待子进程2结束
            lockf(fd, F_LOCK, 0); // 锁住文件
            printf("I am father\n");
            lockf(fd, F_ULOCK, 0); // 解锁文件
        } else {
            perror("fork");
            return 1;
        }
    } else {
        perror("fork");
        return 1;
    }

    // 关闭文件
    close(fd);

    return 0;
}

  1. 创建文件锁:

    首先创建一个文件("lockfile")作为锁文件,用于控制进程之间的互斥访问。
  2. 创建父子进程:

    父进程首先创建第一个子进程(子进程1),然后等待子进程1的结束。子进程1被创建后,它锁定了锁文件,输出"I am son",解锁锁文件,然后使用 _exit 退出。
  3. 子进程1结束后创建第二个子进程:

    父进程在等待子进程1结束后,再创建第二个子进程(子进程2)。子进程2被创建后,它锁定了锁文件,输出"I am daughter",解锁锁文件,然后使用 _exit 退出。
  4. 父进程结束后输出:

    父进程在等待子进程2结束后,再次锁定锁文件,输出"I am father",最后解锁锁文件。
  5. 文件锁的作用:

    锁文件充当互斥锁,确保一次只有一个进程能够访问锁文件,从而控制了进程的输出顺序和同步。这样可以确保输出的逻辑性和顺序性,避免输出交错。

总结:

  • 通过文件锁实现了进程之间的互斥访问,确保了输出的逻辑性和同步性。
  • 子进程1、子进程2和父进程的输出顺序受到锁文件的控制,确保了输出的有序性。
  • 锁文件在不同进程之间充当了关键的同步和互斥机制,确保了进程的逻辑性。

三、进程控制Lockf()

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

int main(void) {
    int pid, i;
    if ((pid = fork()) < 0) {
        printf("child fail create\n");
        return 1; // 返回错误状态码
    } else if (pid == 0) {
        lockf(1, 1, 0); // 锁定标准输出设备(显示器)
        for (i = 0; i < 5; i++)
            printf("This is child (pid=%d) process:b\n", getpid());
        lockf(1, 0, 0); // 解锁标准输出设备(显示器)
        return 0; // 返回正常状态码
    } else {
        lockf(1, 1, 0);
        for (i = 0; i < 5; i++)
            printf("Parent process:a\n");
        lockf(1, 0, 0);
    }
    return 0; // 返回正常状态码
}

  1. 进程间逻辑性:

    1)代码创建了一个父进程和一个子进程,它们在同一个终端中运行。

    2)子进程首先执行,然后父进程执行。

    3)子进程会在屏幕上输出5次 "This is child (pid=%d) process:b",而父进程会在屏幕上输出5次 "Parent process:a"。

    4)由于使用了lockf()函数,父子进程的输出会按照一定的顺序进行,避免了输出交错。

  2. lockf()函数:

    1)lockf()函数用于锁定文件的一部分,以防止其他进程同时访问相同的文件部分。

    2)lockf(fd, F_LOCK, 0) 锁定了标准输出设备,这意味着其他进程无法在锁定期间写入标准输出设备。

    3)子进程在锁定标准输出后输出消息,然后使用 lockf(fd, F_ULOCK, 0) 解锁标准输出设备,允许其他进程再次写入。

    4)父进程也执行了类似的锁定和解锁操作,以确保父子进程的输出不会交错。

总结:

  • 通过lockf()函数实现了对标准输出设备的互斥访问,以确保父子进程的输出按照一定的顺序进行。
  • 子进程在锁定期间输出消息,然后解锁,接着父进程锁定标准输出,输出消息,然后解锁,以实现输出的同步和有序性。
  • 通过这种方式,可以控制进程间的逻辑性,确保输出不会混淆。

四、管道控制

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

int main() {
    int pipe_fd[2];
    pid_t child_pid;
    char message[] = "is sending a message to parent!";
    char buffer[100];

    // 创建管道
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        exit(1);
    }

    // 创建子进程
    child_pid = fork();

    if (child_pid == -1) {
        perror("fork");
        exit(1);
    }

    if (child_pid == 0) {
        // 子进程
        close(pipe_fd[0]); // 关闭读取端

        // 向管道写入消息
        if (write(pipe_fd[1], message, strlen(message)) == -1) {
            perror("write");
            exit(1);
        }

        close(pipe_fd[1]); // 关闭写入端
        exit(0);
    } else {
        // 父进程
        close(pipe_fd[1]); // 关闭写入端

        // 从管道中读取消息
        ssize_t bytes_read = read(pipe_fd[0], buffer, sizeof(buffer));

        if (bytes_read == -1) {
            perror("read");
            exit(1);
        }

        buffer[bytes_read] = '\0';

        printf("in the parent process...\n");
        printf("%ld bytes of data received from child:\n", (long)bytes_read);
        printf("%s\n", buffer);

        close(pipe_fd[0]); // 关闭读取端
    }

    return 0;
}

结果:

分析:

  1. 创建管道: 首先,创建一个管道(pipe_fd),这个管道用于在父子进程之间传递数据。

  2. 创建子进程: 通过fork()函数,创建了一个子进程。父进程和子进程之间将通过管道通信。

  3. 子进程部分:

    1)子进程首先关闭了管道的读取端(close(pipe_fd[0]))。

    2)然后,子进程使用write()函数将字符串消息写入管道的写入端(pipe_fd[1])。

    3)最后,子进程关闭了管道的写入端(close(pipe_fd[1]))并退出。

  4. 父进程部分:

    1)父进程关闭了管道的写入端(close(pipe_fd[1])),因为它不需要向管道写入数据。

    2)父进程使用read()函数从管道的读取端(pipe_fd[0])读取子进程发送的消息。

    3)读取到的数据被放入buffer中,并且在屏幕上显示出来。

总结:

  • 子进程通过管道向父进程发送消息。
  • 父进程从管道中读取消息并显示在屏幕上。
  • 这两个进程之间通过管道进行了简单的单向通信。
  • 父进程和子进程之间的通信逻辑性是:子进程写入,父进程读取,这样父子进程可以协同工作并传递信息。

运行程序后,你将看到父进程接收到子进程发送的消息,并在屏幕上显示出来,这演示了进程之间的逻辑性。

🚀🚀​​​​​​​🚀感谢关注我的优快云博客,更多自然语言处理技巧和应用,请持续关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是dream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值