计算机操作系统——进程控制(Linux)
进程控制
- 进程创建
- fork()函数
- fork() 的基本功能
- fork() 的基本语法
- fork() 的工作原理
- fork() 的典型使用示例
- fork() 的常见问题
- fork() 和 exec() 结合使用
- 总结
- 进程终止与$
- 进程终止的本质
- 进程终止的情况
- 正常退出(Exit)
- 由于信号终止
- 非正常退出(错误或崩溃)
- 进程被父进程终止
- 进程退出的方法
- exit() 函数
- _exit() 系统调用
- abort() 函数
- 接收到信号
- 退出码详解
- 正常退出码
- 非零退出码
- 父进程获取退出码
- 退出码的构成
- 总结
- 进程等待
- 进程等待的必要性
- 等待方法
- wait()
- waitpid()
- waitid()
- 阻塞和非阻塞等待详解
- 阻塞等待
- 非阻塞等待
- 获取子进程的状态
- 总结
- 进程程序替换
- 进程替换的基本概念
- 替换函数
- 替换函数解释
- 总结
进程创建
fork()函数
fork() 是 Linux 和类 Unix 系统中用于创建新进程的系统调用。它是进程创建的核心机制之一。当一个进程调用 fork() 时,它会创建一个几乎完全相同的新进程,称为子进程。
fork() 的基本功能
创建子进程:调用 fork() 时,操作系统会复制调用进程(父进程)的地址空间、堆栈、文件描述符等信息,创建一个新的子进程。子进程几乎是父进程的一个副本,但有两个关键区别:
- 子进程拥有一个新的进程ID(PID)。
- 子进程和父进程的返回值不同,fork() 会返回两次:一次在父进程中,返回子进程的PID;另一次在子进程中,返回0。
父子进程的区分:通过 fork() 返回的值,父进程和子进程可以根据不同的返回值执行不同的代码逻辑:
- 在父进程中,fork() 返回子进程的 PID(大于 0 的值)。
- 在子进程中,fork() 返回 0。
- 进程表:每个进程在操作系统内都有一个进程表项,包括进程ID(PID)、父进程ID(PPID)等信息。父进程的 PID 就是子进程的 PPID。
fork() 的基本语法
#include <unistd.h>
#include <sys/types.h>pid_t fork(void);
返回值:
- 如果 fork() 成功,返回值在父进程中是子进程的 PID,在子进程中是 0。
- 如果 fork() 失败,返回 -1,并设置 errno。
fork() 的工作原理
复制父进程:fork() 会复制父进程的所有资源,如内存、文件描述符、环境变量等。这里需要注意的是,现代操作系统通常会使用 写时复制(Copy on Write,COW) 技术来延迟复制内存,直到父子进程尝试修改数据时,才会进行真正的内存复制,从而提高效率。
进程调⽤fork,当控制转移到内核中的fork代码后,内核做:
- 分配新的内存块和内核数据结构给⼦进程
- 将⽗进程部分数据结构内容拷⻉⾄⼦进程
- 添加⼦进程到系统进程列表当中
- fork返回,开始调度器调度
资源共享:父进程和子进程共享某些资源(例如文件描述符、信号等),但它们有独立的内存空间。
返回值:
父进程:返回子进程的 PID。
子进程:返回 0。
fork() 的典型使用示例
#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork(); // 创建子进程if (pid == -1) {// fork失败perror("fork failed");return 1;} else if (pid > 0) {// 父进程printf("This is the parent process, child PID is %d\n", pid);} else {// 子进程printf("This is the child process\n");}return 0;
}
输出:
This is the parent process, child PID is 12345
This is the child process
注意:fork() 调用时,父进程和子进程的输出顺序不一定相同,具体执行顺序由操作系统调度器决定。
fork() 的常见问题
子进程不会继承父进程的锁:fork() 会复制父进程的资源,包括文件描述符,但不会复制父进程持有的锁(如互斥锁、读写锁)。如果父进程在 fork() 后还持有某些锁,可能会导致子进程死锁。
资源消耗:每次 fork() 都会创建一个新的进程,这会消耗一定的内存和系统资源,尤其是在创建多个进程时,可能会影响系统性能。因此,要注意避免频繁调用 fork()。
进程僵尸:当子进程退出后,父进程应该通过调用 wait() 或 waitpid() 等函数来回收子进程的资源。如果父进程没有处理,子进程会变成 “僵尸进程”(Zombie Process),占用系统资源直到父进程清理它。
进程ID的分配:每个进程都有唯一的进程ID(PID)。父进程的 PID 和子进程的 PID 是不同的。
fork() 和 exec() 结合使用
在许多情况下,fork() 和 exec() 被结合使用来启动新的程序。fork() 用于创建一个子进程,而 exec() 系列函数则用于加载不同的程序。
#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程执行新程序execlp("/bin/ls", "ls", "-l", (char *) NULL);// 如果 execlp 失败,输出错误perror("execlp failed");} else if (pid > 0) {// 父进程等待子进程结束wait(NULL);printf("Child process finished\n");}return 0;
}
在这个示例中,子进程会执行 ls -l 命令,而父进程则等待子进程结束。
总结
- fork() 是 Linux 中用于创建新进程的系统调用,返回两次:一次在父进程中返回子进程的 PID,一次在子进程中返回 0。
- 它复制父进程的资源,并使用写时复制技术优化性能。
- 子进程和父进程是相互独立的,但它们共享某些资源,如文件描述符。
- 父进程应该使用 wait() 等函数来回收子进程的资源,避免僵尸进程。
进程终止与$
进程终止的本质
进程终止指的是操作系统内的进程在完成其任务或遇到异常时,从系统中注销,释放其占用的资源,并通过某种方式通知父进程(如果存在)其退出状态。进程的终止意味着其执行的生命周期结束,操作系统会清理进程占用的内存、文件描述符、子进程等资源。
进程的终止通常由以下几种情况引起:
正常终止:进程完成任务并主动退出。
异常终止:进程遇到错误或异常情况而被操作系统强制终止。
被信号中断:进程接收到外部信号(如 SIGKILL 或 SIGTERM)而终止。
进程终止的情况
进程终止可以发生在不同的情况下,具体有以下几种情况:
正常退出(Exit)
进程执行完其所有任务后,可以主动退出,通常通过调用 exit() 系统调用来实现。进程结束时,操作系统会回收进程的资源,并通知其父进程。父进程通过 wait() 或 waitpid() 等系统调用来获取子进程的退出状态。
由于信号终止
进程在运行时可以接收到来自外部或内部的信号,这些信号可能导致进程中止。例如:
- SIGKILL:无条件终止进程,无法被捕捉或忽略。
- SIGTERM:请求进程终止,允许进程清理资源。
- SIGSEGV:段错误,通常表示进程访问了非法内存。
- SIGABRT:进程由于调用 abort() 函数而终止,通常是由于程序发生了严重错误。
非正常退出(错误或崩溃)
进程可能由于错误(如非法内存访问、除零错误、系统资源不足等)导致崩溃退出。在这种情况下,操作系统会发送错误信号给进程,通常会生成核心转储文件(core dump)以帮助调试。
进程被父进程终止
父进程可以通过发送信号来终止子进程。例如,父进程可以使用 kill() 函数发送一个 SIGKILL 或 SIGTERM 信号来中止子进程的执行。
进程退出的方法
进程可以通过多种方式退出:
exit() 函数
exit() 是标准库函数,用于正常终止进程。它会做以下几件事情:
- 关闭所有已打开的文件描述符。
- 清理由进程分配的内存。
- 调用终止处理程序(如果有)。
- 通知操作系统进程已终止。
#include <stdlib.h>int main() {// 进行某些操作exit(0); // 正常退出,返回退出码0
}
_exit() 系统调用
_exit() 是系统调用,用于立即退出进程,而不执行任何清理操作(如关闭文件描述符、调用终止处理程序等)。它主要用于子进程退出,避免清理父进程设置的资源。
#include <unistd.h>int main() {// 执行操作_exit(0); // 立即退出,不进行标准库的清理操作
}
abort() 函数
abort() 函数用于非正常退出,它通常在程序遇到严重错误时调用。它会立即终止进程并生成核心转储文件,方便调试。
#include <stdlib.h>int main() {// 出现严重错误,退出程序abort(); // 终止进程并生成核心文件
}
接收到信号
进程也可能通过接收到某些信号而被终止。操作系统通过信号机制通知进程发生了某些事件,如 SIGKILL(强制终止)或 SIGSEGV(段错误)。进程可以捕获某些信号并执行自定义的信号处理程序,或者直接退出。
#include <signal.h>void signal_handler(int sig) {if (sig == SIGSEGV) {// 处理段错误exit(1);}
}int main() {signal(SIGSEGV, signal_handler); // 捕获段错误信号// 代码中可能发生段错误return 0;
}
退出码详解
退出码(Exit Status)是一个用于标识进程退出状态的整数。退出码是进程结束时向操作系统报告的一个数字,通常用来告诉父进程进程是如何退出的。
正常退出码
- 0:表示进程成功执行完毕并正常退出,常见的退出码。
非零退出码
非零值:表示进程遇到错误或异常而退出。操作系统和父进程通常会根据非零值来判断错误类型。常见的非零退出码有:
- 1:一般性错误。
- 2:命令行参数错误。
- 126:命令不能执行(权限问题)。
- 127:命令未找到。
- 128:进程由于信号导致的退出,退出码为 128 + 信号编号(例如,SIGKILL 信号编号为 9,退出码为 137)。
父进程获取退出码
父进程可以通过调用 wait() 或 waitpid() 等系统调用来获取子进程的退出码。退出码的高字节和低字节分别表示不同的信息:
高字节:表示子进程是否因信号退出。
低字节:表示进程正常退出时的退出码。
例如,WEXITSTATUS(status) 宏可以用来获取正常退出的退出码,WIFSIGNALED(status) 用来判断进程是否因信号退出。
在 Linux 中,程序的退出码(Exit Code)是一个 8 位无符号整数,通常表示程序的退出状态。它由两个部分组成:低位(低 7 位)和 高位(高 1 位)。这两个部分分别代表不同的含义。
退出码的构成
退出码的范围从 0 到 255(即 0x00 到 0xFF)。通常情况下,退出码的构成可以分为两个部分:
低 7 位(0-127):用于表示程序的执行结果,通常用来表示 成功或错误 的不同状态。
高 1 位(128-255):表示程序是否是因为某个 信号终止 而退出。
低位(0-127)
0:表示程序正常退出,执行成功。通常,任何程序正常执行完毕后,都会返回退出码 0。
1-127:表示程序执行过程中出现了错误。不同的程序可能会定义不同的退出码来表示不同的错误类型。比如:
- 1 通常表示常见的错误。
- 2 可能表示使用错误的命令行参数。
- 126 可能表示权限问题(命令无法执行)。
- 127 通常表示找不到命令。
这部分退出码由程序开发者或操作系统约定,用于标识程序执行的错误类型。
高位(128-255)
当程序因为 信号 被终止时,退出码会反映这种信号的编号。退出码的计算公式是:
退出码 = 128 + 信号编号
例如,程序因为 SIGKILL 信号(信号编号 9)而被强制终止时,退出码将是 137(128 + 9)。
如果程序由于 SIGSEGV 信号(信号编号 11,即段错误)崩溃退出,则退出码将是 139(128 + 11)。
这种退出码范围从 128 到 255,表示程序因接收到特定的信号而终止。
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程正常退出_exit(42); // 退出码为42} else {int status;wait(&status);if (WIFEXITED(status)) {printf("Child exited with code %d\n", WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("Child terminated by signal %d\n", WTERMSIG(status));}}return 0;
}
总结
- 进程终止的本质:进程终止是操作系统回收进程资源、清理内存并通知父进程的过程。
- 终止原因:可以是正常结束、错误崩溃、外部信号等。
- 退出方法:包括 exit()、_exit()、abort()、信号等方式。
- 退出码:是进程退出时向操作系统报告的状态,通常 0 表示成功,非零值表示异常退出,父进程可以通过 wait() 获取子进程的退出状态。
进程等待
在操作系统中,进程的等待通常是指父进程等待子进程完成执行后获取其退出状态(exit status)。这个机制对于进程管理和资源回收非常重要,能够避免僵尸进程的出现,并确保父进程能够得到子进程的执行结果。
进程等待的必要性
当一个进程创建了子进程时,父进程需要等到子进程完成执行,并获取子进程的退出状态。否则,子进程的资源将无法及时回收,导致僵尸进程的产生。僵尸进程指的是子进程已经终止,但其退出状态尚未被父进程获取的进程。僵尸进程会占用系统资源,如进程表项,影响系统性能,并且无法被kill -9杀死
。
为什么需要等待?
避免僵尸进程:子进程终止后,操作系统不会立即销毁子进程的进程表项,父进程需要通过wait()或waitpid()获取子进程的退出状态,从而清理相关资源。
获取子进程的执行结果:父进程需要知道子进程是否正常完成工作、是否发生了错误等信息。这些信息通过退出码传递。
同步进程间的操作:父进程可以在等待子进程时,确保子进程已经完成特定任务,避免在子进程还没完成时继续执行某些操作。
等待方法
在 Linux 系统中,父进程获取子进程状态的方法主要有以下几种:
wait()
wait() 是最基本的等待方法,它会阻塞父进程,直到任一子进程终止。父进程调用该系统调用时,会挂起,直到一个子进程结束。
语法:
pid_t wait(int *status);
status:如果不为 NULL,该参数会返回子进程的退出状态。
返回值:成功时返回终止的子进程的进程ID(PID),失败时返回 -1。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程printf("Child process\n");exit(0);} else {// 父进程int status;wait(&status); // 等待子进程退出if (WIFEXITED(status)) {printf("Child exited with status %d\n", WEXITSTATUS(status));}}return 0;
}
在这个例子中,父进程等待子进程退出,并获取子进程的退出状态。
waitpid()
waitpid() 是 wait() 的增强版本,父进程可以选择等待特定的子进程,或者控制是否阻塞。
语法:
pid_t waitpid(pid_t pid, int *status, int options);
(1)pid:可以指定等待特定的子进程(通过其 PID),或者等待所有子进程。
- pid > 0:等待具有指定 PID 的子进程。
- pid == -1:等待任何子进程(类似 wait() 的行为)。
- pid == 0:等待与当前进程属于同一进程组的子进程。
- pid < -1:等待属于某个进程组的子进程。
status:与 wait() 相同,用于存储子进程的退出状态。
(2)options:控制等待的选项,如:
- WNOHANG:非阻塞方式,如果没有子进程退出,立即返回。
- WUNTRACED:如果子进程停止(例如通过SIGSTOP信号),返回。
- WCONTINUED:如果子进程恢复运行,返回。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程printf("Child process\n");sleep(2); // 模拟子进程运行exit(0);} else {// 父进程int status;pid_t result = waitpid(pid, &status, WNOHANG); // 非阻塞等待if (result == 0) {printf("Child is still running\n");} else if (result == pid) {if (WIFEXITED(status)) {printf("Child exited with status %d\n", WEXITSTATUS(status));}}}return 0;
}
在这个例子中,父进程使用非阻塞的 waitpid() 来检查子进程的状态,如果子进程还未退出,则返回 0。
waitid()
waitid() 是 waitpid() 的另一种变体,提供了更细力度的控制,允许父进程获取更多的状态信息,如进程是否由于信号终止等。
语法:
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
- idtype:指定要等待的进程类别(如 P_PID 等)。
- id:指定要等待的进程 ID。
- infop:存储关于终止进程的详细信息。
- options:与 waitpid() 类似,控制等待行为。
阻塞和非阻塞等待详解
阻塞等待
默认的 wait() 和 waitpid() 都是阻塞的,父进程会等待子进程的终止,直到获取子进程的退出状态。如果没有子进程可等待,父进程将挂起直到有子进程退出。
非阻塞等待
通过在 waitpid() 中传递 WNOHANG 选项,可以使父进程进行非阻塞等待。如果没有子进程退出,父进程会立即返回,程序可以继续执行其他任务。
pid_t waitpid(pid_t pid, int *status, int options);
其中,options 可以指定 WNOHANG,使得调用非阻塞。
- WNOHANG:如果没有子进程退出,waitpid() 会立即返回 0,而不是阻塞等待。
- WUNTRACED:如果子进程已停止,waitpid() 会返回,即使没有退出。
获取子进程的状态
通过 status 变量可以获取子进程的退出状态。父进程可以使用如下宏来分析 status:
- WIFEXITED(status):检查子进程是否正常退出。
- WIFSIGNALED(status):检查子进程是否由于信号导致终止。
- WIFSTOPPED(status):检查子进程是否由于信号停止。
- WEXITSTATUS(status):获取子进程的退出状态码(如果正常退出)。
- WTERMSIG(status):获取导致子进程终止的信号编号。
总结
父进程等待子进程的必要性:防止僵尸进程、获取子进程的退出状态和同步操作。
等待方法:wait()、waitpid() 和 waitid() 提供不同的等待机制。
阻塞与非阻塞等待:wait() 和 waitpid() 默认阻塞,waitpid() 可以通过 WNOHANG 实现非阻塞。
获取子进程状态:通过 status 获取退出状态,并使用宏分析退出原因。
进程程序替换
进程替换是操作系统中用于管理进程执行的一种机制,特别是在多任务操作系统中。它的主要目的是通过上下文切换(Context Switch)实现多个进程的并发执行。操作系统通常通过进程调度来决定哪个进程在某一时刻占用 CPU,这个过程涉及到进程的保存和恢复状态,通常称为“进程替换”。
进程替换的基本概念
进程替换(或进程切换)是指操作系统从一个进程切换到另一个进程的过程。在进程替换的过程中,操作系统需要保存当前进程的执行状态,并恢复下一个进程的执行状态。它通常发生在以下几种情况:
- 时间片到期:如果采用时间片轮转调度算法,当一个进程的时间片用完时,操作系统会选择另一个进程来占用 CPU。
- 进程阻塞:如果进程因等待 I/O 操作、等待资源等原因进入阻塞状态,操作系统会选择其他进程来执行。
- 进程终止:当一个进程执行完成或被终止时,操作系统会调度另一个进程来继续执行。
- 高优先级进程到来:如果一个高优先级的进程被创建,操作系统可能会中断当前进程,切换到高优先级进程。
替换函数
#include <unistd.h>int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ...,char *const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);
替换函数解释
exec 系列函数是 Linux 系统中的一组用于进程替换的系统调用。这些函数会用新的程序替换当前进程的映像(即加载另一个可执行文件),并且会将当前进程的控制转移到新程序中。进程替换后,原有的进程的代码、数据和堆栈等都会被新的程序所替换,但新程序会继承旧进程的进程标识符(PID),文件描述符等。
这些函数有不同的变种,主要的区别在于传递参数和环境变量的方式。下面是各个 exec 系列函数的详细解释。
execl(const char *path, const char *arg, …)
原型:
int execl(const char *path, const char *arg, ..., (char *)NULL);
解释:
- execl 是 “exec with a list of arguments” 的缩写。
- path 是要执行的可执行文件的路径。
- arg 是传递给新程序的第一个参数,后面可以跟任意数量的额外参数。
- 参数列表必须以 NULL 结束,这与 C 风格的字符串数组类似。
- execl 的特点是,参数是以可变参数的形式提供的(即通过 …)。
- 该函数调用成功时,不会返回,当前进程将被新程序替换;如果发生错误,返回 -1,并设置 errno。
示例:
execl("/bin/ls", "ls", "-l", (char *)NULL);
这个例子会用 /bin/ls 程序替换当前进程,并传递 “ls” 和 “-l” 作为参数。
execlp(const char *file, const char *arg, …)
原型:
int execlp(const char *file, const char *arg, ..., (char *)NULL);
解释:
- execlp 类似于 execl,唯一的区别是它会在系统的 PATH 环境变量指定的路径中查找 file,从而不需要提供完整的路径。
- file 是可执行文件的名称,而不是文件的完整路径。
- 参数列表以 NULL 结尾。
- 如果 execlp 成功,它将用新程序替换当前进程;否则,返回 -1。
示例:
execlp("ls", "ls", "-l", (char *)NULL);
这将会在 PATH 环境变量指定的路径中查找 ls 可执行文件,并将其作为新程序执行。
execle(const char *path, const char *arg, …, char *const envp[])
原型:
int execle(const char *path, const char *arg, ..., char *const envp[]);
解释:
- execle 与 execl 类似,但它允许指定新的环境变量。环境变量是通过 envp[] 数组传递的。
- path 是要执行的程序的完整路径。
- 参数列表以 NULL 结束。
- envp[] 是一个字符指针数组,其中每个元素是一个环境变量,格式通常是 key=value。
- 调用成功时,当前进程将被新程序替换,失败时返回 -1。
示例:
char *env[] = { "HOME=/home/user", "PATH=/bin", (char *)NULL };
execle("/bin/ls", "ls", "-l", (char *)NULL, env);
这个例子将用 /bin/ls 程序替换当前进程,传递 “ls” 和 “-l” 作为参数,并且设置新的环境变量 HOME=/home/user 和 PATH=/bin。
execv(const char *path, char *const argv[])
原型:
int execv(const char *path, char *const argv[]);
解释:
- execv 是 “exec with an array of arguments” 的缩写。
- path 是要执行的程序的完整路径。
- argv[] 是一个指向字符串数组的指针,数组的每个元素是程序的一个参数,第一个元素通常是程序的名称,最后一个元素必须是 NULL。
- execv 不像 execl 那样使用可变参数,而是使用一个数组来传递所有参数。
- 调用成功时,当前进程被新程序替换,失败时返回 -1。
示例:
char *args[] = { "ls", "-l", NULL };
execv("/bin/ls", args);
这个例子用 /bin/ls 程序替换当前进程,并将 “ls” 和 “-l” 作为参数传递给新程序。
execvp(const char *file, char *const argv[])
原型:
int execvp(const char *file, char *const argv[]);
解释:
- execvp 类似于 execv,但它会在系统的 PATH 环境变量指定的路径中查找 file,而不需要提供完整路径。
- file 是可执行文件的名称,argv[] 是一个参数数组,和 execv 一样。
- 如果成功,它将用新程序替换当前进程;如果失败,返回 -1。
示例:
char *args[] = { "ls", "-l", NULL };
execvp("ls", args);
这个例子会在 PATH 环境变量中查找 ls 程序,并将 “ls” 和 “-l” 作为参数传递给新程序。
execve(const char *path, char *const argv[], char *const envp[])
原型:
int execve(const char *path, char *const argv[], char *const envp[]);
解释:
- execve 是所有 exec 系列函数的基础。它直接调用内核,执行一个新的程序。
- path 是要执行的程序的完整路径。
- argv[] 是一个指向字符串数组的指针,数组的每个元素是程序的一个参数,第一个元素通常是程序的名称,最后一个元素必须是 NULL。
- envp[] 是一个指向环境变量的指针数组,格式通常是 key=value。
- execve 是唯一一个可以直接指定环境变量的 exec 函数,其他的函数(如 execlp、execvp)都是在内部调用 execve 并传递环境变量。
示例:
char *args[] = { "ls", "-l", NULL };
char *env[] = { "HOME=/home/user", "PATH=/bin", NULL };
execve("/bin/ls", args, env);
这个例子用 /bin/ls 程序替换当前进程,传递 “ls” 和 “-l” 作为参数,并设置新的环境变量 HOME=/home/user 和 PATH=/bin。
总结
- execl 传递参数以可变参数列表形式,路径由完整路径指定
- execlp 传递参数以可变参数列表形式,路径由 PATH 环境变量查找
- execle 传递参数以可变参数列表形式,并允许设置新的环境变量
- execv 传递参数通过字符串数组,路径由完整路径指定
- execvp 传递参数通过字符串数组,路径由 PATH 环境变量查找
- execve 传递参数通过字符串数组,允许设置新的环境变量
这些 exec 系列函数都能替换当前进程,并且只会在失败时返回 -1,成功时不会返回。
相关文章:
计算机操作系统——进程控制(Linux)
进程控制 进程创建fork()函数fork() 的基本功能fork() 的基本语法fork() 的工作原理fork() 的典型使用示例fork() 的常见问题fork() 和 exec() 结合使用总结 进程终止与$进程终止的本质进程终止的情况正常退出(Exit)由于信号终止非…...
【前端】ES6基础
1.开发工具 vscode地址 :https://code.visualstudio.com/download, 下载对应系统的版本windows一般都是64位的 安装可以自选目录,也可以使用默认目录 插件: 输入 Chinese,中文插件 安装: open in browser,直接右键文件…...
【排序算法 python实现】
排序算法 python实现 / 默写 # 汉诺塔 import copy import randomdef hanuo(n, a, b, c):if n 1:print(f{a} --> {c})returnhanuo(n - 1, a, c, b)print(f{a} --> {c})hanuo(n - 1, b, a, c)hanuo(3, A, B, C)# 冒泡排序 def bubble_sort(arr):n len(arr)for i in ran…...
Java图书管理系统(简易保姆级)
前面学习了这么多知识,为了巩固之前的知识,我们就要写一个图书管理系统来帮助大家复习,让大家的知识融会贯通~~~ 话不多说,直接开始今天的内容~ 首先呢,我们要有一个大体的思路: 实现效果思路有两种情况&a…...
嵌入式硬件设计:从概念到实现的全流程
嵌入式硬件设计是现代电子技术中一个至关重要的领域,涉及从硬件架构设计到硬件调试的各个方面。它为我们日常生活中的各类智能设备、家电、工业控制系统等提供了强大的支持。本文将介绍嵌入式硬件设计的基本流程、关键技术、常用工具以及常见的挑战和解决方案&#…...
第 4 章 Java 并发包中原子操作类原理剖析
原子变量操作类 AtomicLong 是原子性递增或者递减类,其内部使用 Unsafe 来实现,AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的。这里的原子操作类都使用 CAS 非阻塞算法 private static final lon…...
从 0 到 1 掌握部署第一个 Web 应用到 Kubernetes 中
文章目录 前言构建一个 hello world web 应用项目结构项目核心文件启动项目 检查项目是否构建成功 容器化我们的应用编写 Dockerfile构建 docker 镜像推送 docker 镜像仓库 使用 labs.play-with-k8s.com 构建 Kubernetes 集群并部署应用构建 Kubernetes 集群环境编写部署文件 总…...
政安晨【零基础玩转各类开源AI项目】探索Cursor-AI Coder的应用实例
目录 Cusor的主要特点 Cusor实操 政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! Cursor 是 Visual Studio Code 的一个分支。这使我们能够…...
CentOS 7 安装部署 KVM
1.关闭虚拟机 打开相关选项 打开虚拟机centos7 连接xshell 测试网络,现在就是没问题的,因为我们要使用网络源 安装 GNOME 桌面环境 安装KVM 模块 安装KVM 调试工具 构建虚拟机的命令行工具 qemu 组件,创建磁盘、启动虚拟机等 输入这条命令,…...
ArcGIS 10.2软件安装包下载及安装教程!
今日资源:ArcGIS 适用系统:WINDOWS 软件介绍:ArcGIS是一款专业的电子地图信息编辑和开发软件,提供一种快速并且使用简单的方式浏览地理信息,无论是2D还是3D的信息。软件内置多种编辑工具,可以轻松的完成地…...
一个专为云原生环境设计的高性能分布式文件系统
大家好,今天给大家分享一款开源创新的分布式 POSIX 文件系统JuiceFS,旨在解决海量云存储与各类应用平台(如大数据、机器学习、人工智能等)之间高效对接的问题。 项目介绍 JuiceFS 是一款面向云原生设计的高性能分布式文件系统&am…...
基于深度学习CNN算法的花卉分类识别系统01--带数据集-pyqt5UI界面-全套源码
文章目录 基于深度学习算法的花卉分类识别系统一、项目摘要二、项目运行效果三、项目文件介绍四、项目环境配置1、项目环境库2、环境配置视频教程 五、项目系统架构六、项目构建流程1、数据集2、算法网络Mobilenet3、网络模型训练4、训练好的模型预测5、UI界面设计-pyqt56、项目…...
3174、清除数字
3174、[简单] 清除数字 1、题目描述 给你一个字符串 s 。你的任务是重复以下操作删除 所有 数字字符: 删除 第一个数字字符 以及它左边 最近 的 非数字 字符。 请你返回删除所有数字字符以后剩下的字符串。 2、解题思路 遍历字符串: 我们需要逐个遍…...
C++ 优先算法 —— 无重复字符的最长子串(滑动窗口)
目录 题目: 无重复字符的最长子串 1. 题目解析 2. 算法原理 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口(同向双指针) 3. 代码实现 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口 题目: 无重复字符的最长子串 1. 题目解析 题目截图: 此题所说的…...
ADS学习笔记 6. 射频发射机设计
基于ADS2023 update2 更多ADS学习笔记:ADS学习笔记 1. 功率放大器设计ADS学习笔记 2. 低噪声放大器设计ADS学习笔记 3. 功分器设计ADS学习笔记 4. 微带分支定向耦合器设计ADS学习笔记 5. 微带天线设计 -1、射频发射机性能指标 在射频电路和系统中,发送…...
上海乐鑫科技一级代理商飞睿科技,ESP32-C61高性价比WiFi6芯片高性能、大容量
在当今快速发展的物联网市场中,无线连接技术的不断进步对智能设备的性能和能效提出了更高要求。为了满足这一需求,乐鑫科技推出了ESP32-C61——一款高性价比的Wi-Fi 6芯片,旨在为用户设备提供更出色的物联网性能,并满足智能设备连…...
QT QRadioButton控件 全面详解
本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…...
51单片机从入门到精通:理论与实践指南(一)
单片机在智能控制领域的应用已非常普遍,发展也很迅猛,学习和使用单片机的人员越来越多。虽然新型微控制器在不断推出,但51单片机价格低廉、易学易用、性能成熟,在家电和工业控制中有一定的应用,而且学好了51单片机&…...
零基础3分钟快速掌握 ——Linux【终端操作】及【常用指令】Ubuntu
1.为啥使用Linux做嵌入式开发 能广泛支持硬件 内核比较高效稳定 原码开放、软件丰富 能够完善网络通信与文件管理机制 优秀的开发工具 2.什么是Ubuntu 是一个以桌面应用为主的Linux的操作系统, 内核是Linux操作系统, 具有Ubuntu特色的可视…...
C#中面试的常见问题007
1.在EF中实现一个实体对应多个表 1. 表拆分(Table Splitting) 表拆分是指将一个实体映射到两个或多个表中的行。这通常发生在实体的属性分布在不同的表中,但这些表通过外键关联到同一个主表。在EF Core中,可以通过Fluent API来配…...
人工智能——大语言模型
5. 大语言模型 5.1. 语言模型历史 20世纪90年代以前的语言模型都是基于语法分析这种方法,效果一直不佳。到了20世纪90年代,采用统计学方法分析语言,取得了重大进展。但是在庞大而复杂的语言信息上,基于传统统计的因为计算量巨大…...
nodejs第三方库sharp对图片的操作生成新图片、压缩、添加文字水印及图片水印等
Sharp是一个基于libvips的高性能Node.js图像处理库,它提供了广泛的功能,包括调整大小、裁剪、旋转、格式转换等。Sharp可以处理多种图像格式,并且能够高效地转换图像格式。 相关说明及用法看:https://sharp.nodejs.cn/ 安装&#…...
力扣第 67 题 “二进制求和”
题目描述 给你两个二进制字符串 a 和 b,以二进制字符串的形式返回它们的和。 示例 1: 输入: a "11", b "1" 输出: "100"示例 2: 输入: a "1010", b "1011" 输出: "10101"提示: 每个字符串仅由…...
Spring Boot优雅读取配置信息 @EnableConfigurationProperties
很多时候我们需要将一些常用的配置信息比如oss等相关配置信息放到配置文件中。常用的有以下几种,相信大家比较熟悉: 1、Value(“${property}”) 读取比较简单的配置信息: 2、ConfigurationProperties(prefix “property”)读取配置信息并与 …...
鸿蒙多线程开发——Sendable对象的序列化与冻结操作
1、Sendable对象的序列化与反序列化 Sendable对象的简单介绍参考文章:鸿蒙多线程开发——线程间数据通信对象03(sendable) 与JSON对象的序列化和反序列化类似,Sendable对象的序列化和反序列化是通过ArkTs提供的ASON工具来完成。 与JSON类似࿰…...
nodepad配置c/c++ cmd快速打开创建项目文件
前提:下载MinGw,并且配置环境变量 点击阅读次篇文章配置MinGw 无论是哪个编译器,执行c文件都是经历以下步骤: 编译文件生成exe文件执行该exe文件 我们先手动完成这两部 手动编译文件使用指令 gcc {你的c文件} -o {生成文件名}生成exe文件 第二步运行exe直接点击该文…...
【C++】读取数量不定的输入数据
读取数量不定的输入数据 似乎是一个很实用的东西? 问题: 我们如何对用户输入的一组数(事先不知道具体有多少个数)求和? 这需要不断读取数据直至没有新的输入为止。(所以我们的代码就是这样设计的&#x…...
ESC字符背后的故事(27 <> 033 | x1B ?)
ANSI不可见字符转义,正确的理解让记忆和书写变得丝滑惬意。 (笔记模板由python脚本于2024年11月26日 15:05:33创建,本篇笔记适合python 基础扎实的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网:https://www.python.org/ Free…...
基于NXP LS1043 OpenWRT智能交通边缘网关设计
0 引言 城市公共交通是与人们生产生活息息相关的重 要基础设施,是关系国计民生的社会公益事业。“城 市公共交通发展的十三五规划”明确指出:建设与移 动互联网深度融合的智能公交系统;推进“互联网 城市公交”发展;推进多元…...
绪论相关题目
1.在数据结构中,从逻辑上可以把数据结构分成( C)。 A. 动态结构和静态结构 B. 紧凑结构和非紧凑结构 C. 线性结构和非线性结构 D. 内部结构和外部结构 2.在数据结构中,从存储结构上可以将之分为( B)。 A. 动态结构和静态结构 B. 顺序存储和非顺序存储 C. 紧凑结构和非紧…...
做移动端网站软件下载/哈尔滨网站优化流程
<?php// 添加应用模块实践class MessageAction extends Action{ //ThinkPHP特殊的操作方法 – 初始化 public function _initialize() { echo 每个操作都会先执行此方法逻辑内容<br /><br />; } // URL访问实践2 public function test2 () { …...
地方网站怎么做的/百度一下官网网址
1. 涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…...
网站建设优化论坛/北京seo优化分析
系统更新安装完系统之后,需要更新一些补丁。CtrlAltT调出终端,执行一下代码:$sudo apt-get update$sudo apt-get upgrade卸载libreOfficelibreoffice事ubuntu自带的开源office软件,体验效果不如windows上的office,于是…...
深圳做营销型网站/免费b站在线观看人数在哪里找到
如前所述,最好让所有代码找到问题所在 . 我想你没有在处理设置中添加函数“bufferUntil()”,所以每次串口收到一个值时,它只显示它 . 我可以为你提出一个解决方案,它不是唯一的解决方案,但似乎有效 .在您的Arduino代码…...
搭建一个网站需要多久/自媒体平台大全
1.Mi 快传 https://www.lanzous.com/i69ty9c 2.快牙(跨平台) https://www.lanzous.com/i69tybe 3.Mi互传(Win10(verson>1903)) http://lanzous.com/i6wiuef 4.百灵快传(跨平台-有二进制包) 基于Go https://github.com/bitepeng/b0pass 国内可以到介绍的gitee地址直接…...
怎么自己做网站凑钱/关于seo如何优化
自动化部署流程1.获取代码(直接拉取)2.编译(可选)3.配置文件4.打包5.SCP到目标服务器6.将目标服务器移除集群7.解压8.放置到webroot9.SCP差异文件10.重启(可选)11.测试12.加入集群上线回滚流程#正常回滚1.列出回滚版本2.目标服务移除集群3.执行回滚4.重启和测试5.加入集群#紧急…...