🔥博客主页:真的睡不醒
🚀系列专栏:深度学习环境搭建、环境配置问题解决、自然语言处理、语音信号处理、项目开发
💘每日语录:每一步都算数,即使你觉得前进得缓慢,你仍在超越那些停滞不前的人。
🎉感谢大家点赞👍收藏⭐指正✍️
目录
一、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;
}
修改程序,在三进程中分别使用wait、exit、lockf等系统调用“实现”其同步推进,多次反复运行改进后的程序,观察并记录运行结果。
#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;
}
创建文件锁:
首先创建一个文件("lockfile")作为锁文件,用于控制进程之间的互斥访问。创建父子进程:
父进程首先创建第一个子进程(子进程1),然后等待子进程1的结束。子进程1被创建后,它锁定了锁文件,输出"I am son",解锁锁文件,然后使用_exit
退出。子进程1结束后创建第二个子进程:
父进程在等待子进程1结束后,再创建第二个子进程(子进程2)。子进程2被创建后,它锁定了锁文件,输出"I am daughter",解锁锁文件,然后使用_exit
退出。父进程结束后输出:
父进程在等待子进程2结束后,再次锁定锁文件,输出"I am father",最后解锁锁文件。文件锁的作用:
锁文件充当互斥锁,确保一次只有一个进程能够访问锁文件,从而控制了进程的输出顺序和同步。这样可以确保输出的逻辑性和顺序性,避免输出交错。
总结:
- 通过文件锁实现了进程之间的互斥访问,确保了输出的逻辑性和同步性。
- 子进程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)代码创建了一个父进程和一个子进程,它们在同一个终端中运行。
2)子进程首先执行,然后父进程执行。
3)子进程会在屏幕上输出5次 "This is child (pid=%d) process:b",而父进程会在屏幕上输出5次 "Parent process:a"。
4)由于使用了
lockf()
函数,父子进程的输出会按照一定的顺序进行,避免了输出交错。 -
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;
}
结果:
分析:
-
创建管道: 首先,创建一个管道(
pipe_fd
),这个管道用于在父子进程之间传递数据。 -
创建子进程: 通过
fork()
函数,创建了一个子进程。父进程和子进程之间将通过管道通信。 -
子进程部分:
1)子进程首先关闭了管道的读取端(
close(pipe_fd[0])
)。2)然后,子进程使用
write()
函数将字符串消息写入管道的写入端(pipe_fd[1]
)。3)最后,子进程关闭了管道的写入端(
close(pipe_fd[1])
)并退出。 -
父进程部分:
1)父进程关闭了管道的写入端(
close(pipe_fd[1])
),因为它不需要向管道写入数据。2)父进程使用
read()
函数从管道的读取端(pipe_fd[0]
)读取子进程发送的消息。3)读取到的数据被放入
buffer
中,并且在屏幕上显示出来。
总结:
- 子进程通过管道向父进程发送消息。
- 父进程从管道中读取消息并显示在屏幕上。
- 这两个进程之间通过管道进行了简单的单向通信。
- 父进程和子进程之间的通信逻辑性是:子进程写入,父进程读取,这样父子进程可以协同工作并传递信息。
运行程序后,你将看到父进程接收到子进程发送的消息,并在屏幕上显示出来,这演示了进程之间的逻辑性。