计算机操作系统——进程控制(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)由于信号终止非…...
游戏引擎学习第23天
实时代码编辑功能的回顾 当前实现的实时代码编辑功能已经取得了显著的成功,表现出强大的性能和即时反馈能力。该功能允许开发者在修改代码后几乎立即看到变化在运行中的程序中体现出来,极大提升了开发效率。尽管目前的演示内容较为简单,呈现…...
0基础学java之Day25
Vector /** 知识点:Vector独有的方法 理解: * Vector在JDK1.0开始就已经存在 -- 元老级别的集合类, * 集合框架的概念是JDK1.2开始才有的, * 开发人员为了将Vector保留下来…...
android集成FFmpeg步骤以及常用命令,踩坑经历
1、入坑第一步:首先集成的库必须正确。最好是有ndk的,FFmpeg有许多个版本,我才开始接触的时候随便选了一个,一般的 方法没有问题。但是涉及到需要使用libx264等条件进行编码时,老是报错,网上搜索资料也没有人说需要ndk的支持才行。这个问题困扰了好几天,怎么试不行,最后…...
Mac——鼠标增强插件Mos
功能说明: 能够解决鼠标断续、不灵敏等鼠标问题。 下载地址: Mac——鼠标增强插件Mos...
【c++篇】:解读Set和Map的封装原理--编程中的数据结构优化秘籍
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:c篇–CSDN博客 文章目录 前言一.set和map的初步封装1.树的节点封装修改2.Find()查找函数3.红…...
华为鸿蒙内核成为HarmonyOS NEXT流畅安全新基座
HDC2024华为重磅发布全自研操作系统内核—鸿蒙内核,鸿蒙内核替换Linux内核成为HarmonyOS NEXT稳定流畅新基座。鸿蒙内核具备更弹性、更流畅、更安全三大特征,性能超越Linux内核10.7%。 鸿蒙内核更弹性:元OS架构,性能安全双收益 万…...
ArcGIS API for Javascript学习
一、ArcGIS API for Javascript 介绍 ArcGIS API for Javascript 是由美国 Esri 公司推出,跟随ArcGIS 9.3 同时发布的,是Esri 基于dojo 框架和 REST 风格实现的一套编程接口。通过 ArcGIS API for Javascript可以对ArcGIS for Server 进行访问ÿ…...
LeetCode 3206.交替组 I:遍历
【LetMeFly】3206.交替组 I:遍历 力扣题目链接:https://leetcode.cn/problems/alternating-groups-i/ 给你一个整数数组 colors ,它表示一个由红色和蓝色瓷砖组成的环,第 i 块瓷砖的颜色为 colors[i] : colors[i] …...
环形缓冲区
什么是环形缓冲区 环形缓冲区,也称为循环缓冲区或环形队列,是一种特殊的FIFO(先进先出)数据结构。它使用一块固定大小的内存空间来缓存数据,并通过两个指针(读指针和写指针)来管理数据的读写。当任意一个指针到达缓冲区末尾时,会自动回绕到缓冲区开头,形成一个"环"。…...
Maven 仓库
Maven 仓库对于管理构建 Java 项目所需的依赖和插件至关重要。 Maven 仓库主要有三种类型:本地仓库、中央仓库和远程仓库。 本文将探讨每种仓库的用途以及如何有效使用它们。 Maven 仓库类型 本地仓库 本地仓库是位于您本地机器上的一个目录,Maven 在…...
29.UE5蓝图的网络通讯,多人自定义事件,变量同步
3-9 蓝图的网络通讯、多人自定义事件、变量同步_哔哩哔哩_bilibili 目录 1.网络通讯 1.1玩家Pawn之间的同步 1.2事件同步 1.3UI同步 1.4组播 1.5变量同步 1.网络通讯 1.1玩家Pawn之间的同步 创建一个第三人称项目 将网络模式更改为监听服务器,即将房主作为…...
计算机网络习题解答--个人笔记(未完)
本篇文章为关于《计算机网络-自顶向下方法第七版》的阅读总结和课后习题解答(未完待续) 第二章: cookie:(这里是比较老版本的HTTP,具体HTTPs是怎么实现的不是很清楚)cookie的原理其实很简单。就是在HTTP消息头上又多…...
Unity图形学之雾Fog
1.设置雾化: 2.雾化变化曲线:FogMode (1)线性: (2)一次指数: (3)二次指数: Shader "Custom/FogTest" {Properties{_Color ("Color…...
ML 系列:第 36 节 — 统计学中的抽样类型
ML 系列:第 36 天 — 统计学中的抽样类型 文章目录 一、说明二、抽样方法三、简单随机抽样四、 Stratified Sampling分层抽样五、 Cluster Sampling 整群抽样六、Systematic Sampling系统抽样七、Convenience Sampling便利抽样八、结论 一、说明 统计学中的抽样类型…...
docker-compose部署java服务
文章目录 一、下载安装docker-compose二、编写Dockerfile文件三、编写docker-compose.yml文件配置说明 四、服务启动五、测试与验证 一、下载安装docker-compose 在安装docker时,并不会同时把docker-compose安装好,需要额外安装一下 下载docker-compos…...
ubuntu22开机自动登陆和开机自动运行google浏览器自动打开网页
一、开机自动登陆 1、打开settings->点击Users 重启系统即可自动登陆桌面 二、开机自动运行google浏览器自动打开网页 1、安装google浏览器 sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo dpkg -i ./google-chrome-stable…...
java接口对接标准
概述 最近在跟许多外部平台对接,遇到了很多问题,在此记录一下接口的对接标准。 接口对接标准 确认环境,分别获取di和prd环境的接口信息,比如域名。确认不同环境的防火墙是否连通。接口校验,接口携带的token信息如何…...
训练的decoder模型文本长度不一致,一般设置为多大合适,需要覆盖最长的文本长度么
在训练解码器模型时,文本长度不一致是常见的情况,需要根据任务的特性和数据集的长度分布来设置合理的最大长度 (max_length)。以下是一些指导原则,帮助你设置合适的最大长度: 1. 是否需要覆盖最长文本长度 覆盖最长文本长度: 如果任务对完整性要求很高(例如生成数学公式、…...
安装MySQL服务
安装版本MySQL8的安装包 安装界面 在这里选择MySQL中的Server only 只安装服务器端 如果选择custom需要如下图 进入配置导向,点击ready to configure,点击next即可 采用默认形式 执行成功后,会出现自动选择项 点击next然后再点击Finish 启动…...
十二、正则表达式、元字符、替换修饰符、手势和对话框插件
1. 正则表达式 1.1 基本使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title&g…...
Unreal从入门到精通之如何绘制用于VR的3DUI交互的手柄射线
文章目录 前言实现方式MenuLaser实现步骤1.Laser和Cursor2.移植函数3.启动逻辑4.检测射线和UI的碰撞5.激活手柄射线6.更新手柄射线位置7.隐藏手柄射线8.添加手柄的Trigger监听完整节点如下:效果图前言 之前我写过一篇文章《Unreal5从入门到精通之如何在VR中使用3DUI》,其中讲…...
如何提升编程能力第二篇
如何提升编程能力2 1. 引言2. 掌握理论基础2.1 理解编程语言的核心2.2 数据结构与算法2.3 计算机基础与系统设计3.1 多写代码3.2 参与开源项目3.3 开发自己的项目 4. 提高代码质量4.1 代码风格与可读性4.2 测试驱动开发 1. 引言 编程是推动现代科技发展的核心技能,…...
【AI日记】24.11.26 聚焦 kaggle 比赛
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 核心工作 1 内容:研究 kaggle 比赛时间:3 小时 核心工作 2 内容:学习 kaggle 比赛 Titanic - Machine Learning from Disaster时间:4 小时备注:这…...
计算机网络八股整理(一)
计算机网络八股文整理 一:网络模型 1:网络osi模型和tcp/ip模型分别介绍一下 osi模型是国际标准的网络模型,它由七层组成,从上到下分别是:应用层,表示层,会话层,传输层,…...
删除链表中的重复元素
删除链表中的重复元素 单链表的创建和使用删除链表中的重复元素 I题目描述解题思路代码实现 删除链表中的重复元素 II题目描述解题思路代码实现 单链表的创建和使用 使用vector结合单链表数据结构创建一个通用单链表。 #include <iostream> #include <vector>str…...
序列求和 牛客网
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 定义S(n) 12 22 … n2,输出S(n) % 1000000007。 注意:1 < n < 1e18。 输入描述: 多组输入,输入直到遇到EOF为止;第一行输…...
【Oracle11g SQL详解】 SELECT 语句的基础用法与示例
SELECT 语句的基础用法与示例 在 Oracle 11g 中,SELECT 语句是最常用的 SQL 语句,用于从数据库表中查询数据。本文将从语法结构、使用方法和常见示例出发,系统讲解 SELECT 语句的基础用法。 一、SELECT 语句的基本语法 SELECT 列名1, 列名2…...
编译以前项目更改在x64下面时报错:函数“PVOID GetCurrentFiber(void)”已有主体
win32下面编译成功,但是x64报错 1>GetWord.c 1>md5.c 这两个文件无法编译 1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um\winnt.h(24125,1): error C2084: 函数“PVOID GetCurrentFiber(void)”已有主体 1>C:\Program Files (x…...
【小白学机器学习36】关于独立概率,联合概率,交叉概率,交叉概率和,总概率等 概念辨析的例子
目录 1 先说结论 2 联合概率 3 边缘概率 4 (行/列)边缘概率的和 总概率1 5 条件概率 5.1 条件概率的除法公式 5.2 条件概率和联合概率区别 1 先说结论 关于独立概率,联合概率,交叉概率,交叉概率和,总概率 类型含义 …...
如何使用 Tailwind CSS 构建响应式网站:详细指南
文章目录 前言一、安装 Tailwind CSS二、配置 Tailwind CSS三、使用 Tailwind CSS 构建响应式网站四、优化和部署结语 前言 在当今的数字时代,网站不仅需要在桌面浏览器上看起来出色,还需要在移动设备和平板电脑上提供一致的用户体验。响应式设计成为了…...
LabVIEW发动机热磨合试验台
在汽车发动机的研发和质量控制中,发动机热磨合试验是关键环节。它能够检验发动机在实际运行条件下的性能,及时发现异响、振动、漏油等潜在问题。通过搭建基于LabVIEW的高效测试平台,可以显著提高发动机的可靠性和使用寿命。下面介绍LabVIEW开…...
【GPT】力量训练是什么,必要吗,有可以替代的方式吗
什么是力量训练? 力量训练是一种通过抵抗力(如重量、阻力带、自身体重等)来刺激肌肉收缩,从而提高肌肉力量、耐力和体积的运动形式。它包括以下常见形式: 自由重量训练:使用哑铃、杠铃、壶铃等。固定器械…...
pikachu文件上传漏洞通关详解
声明:文章只是起演示作用,所有涉及的网站和内容,仅供大家学习交流,如有任何违法行为,均和本人无关,切勿触碰法律底线 目录 概念:什么是文件上传漏洞一、客户端check二、MIME type三、getimagesi…...
java hashCode() 详解
hashCode() 是 Java 中 Object 类 提供的一个重要方法,它在 Java 集合框架中扮演着关键角色,特别是在使用哈希表相关的集合(如 HashMap、HashSet 和 Hashtable)时。以下是对 hashCode() 方法的详解,包括概念、用法、规…...
鸿蒙学习自由流转与分布式运行环境-价值与架构定义(1)
文章目录 价值与架构定义1、价值2、架构定义 随着个人设备数量越来越多,跨多个设备间的交互将成为常态。基于传统 OS 开发跨设备交互的应用程序时,需要解决设备发现、设备认证、设备连接、数据同步等技术难题,不但开发成本高,还存…...
JavaWeb
JavaWeb 一、JavaWeb 是什么?二、JavaWeb 发展阶段三、JavaWeb 常用架构Servlet JSP 架构SSH 架构SSM 架构SpringBoot架构SpringCloud架构 四、JavaWeb 项目结构(带web.xml的)五、如何打包六、war包部署1. Tomcat 介绍2. Tomcat目录结构3. 开…...
加快发展社会保障事业的必要性
题目 【2011年浙江公务员考试】(二)某市将召开一次加快发展社会保障事业的形势分析会。会上,某领导要就加快发展社会保障事业的必要性做主题发言。请结合给定资料7~8,为领导拟一份发言要点。(25分) 要求&a…...
责任链模式在spring security过滤器链中的应用
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象按照顺序处理请求,并且每个对象可以选择自己是否处理该请求或将其传递给下一个对象。 在Spring Security中,责任链模式得到了广泛应…...
Netty基本原理
目录 前言 原生NIO VS Netty 原生NIO存在的问题 Netty的优点 线程模型 传统阻塞 I/O (Blocking I/O) 2. 非阻塞 I/O (Non-blocking I/O) 3. 多路复用 I/O (Multiplexed I/O) 4. Reactor 模式 常见的 Reactor 模式的变体: Netty线程模型 工作原理 前言 N…...
图论入门编程
卡码网刷题链接:98. 所有可达路径 一、题目简述 二、编程demo 方法①邻接矩阵 from collections import defaultdict #简历邻接矩阵 def build_graph(): n, m map(int,input().split()) graph [[0 for _ in range(n1)] for _ in range(n1)]for _ in range(m): …...
Haproxy
一、haproxy简介 HAProxy 是法国开发者 威利塔罗 (Willy Tarreau) 在 2000 年使用 C 语言开发的一个开源软件 是一款具备高并发 ( 万级以上 ) 、高性能的 TCP 和 HTTP 负载均衡器 支持基于 cookie 的持久性,自动故障切换,支持正则表达式及 web 状态统…...
旋转磁体产生的场 - 实验视频资源下载
先发几个视频,是2019年所作的实验内容 更多视频,到某宝找我吧。注意:是收费的。 20190312-180244-旋转磁体产生的场造成激光功率减小 https://download.csdn.net/download/u014161757/90038058 20190313-090956-旋转磁体产生的场对真空介电…...
Java Map
在Java的集合框架中,Map接口用于存储键值对,提供了一种基于键进行查找和操作的数据结构。Map接口的实现类提供了丰富的方法来操作键值对,例如添加、删除、更新和查找。本文将详细介绍Java中的Map接口及其常见实现类,包括HashMap、…...
长三角文博会:Adobe国际认证体系推动设计人才评价新标准
2024年11月22日,由上海、江苏、浙江、安徽三省一市党委宣传部共同发起的第五届长三角文化博览会(简称“长三角文博会”)在上海国家会展中心盛大启幕。长三角文博会自2018年起已成功举办多届,已成为展示区域文化产业发展成果、推动…...
GoogleTest做单元测试
目录 环境准备GoogleTest 环境准备 git clone https://github.com/google/googletest.git说cmkae版本过低了,解决方法 进到googletest中 cmake CMakeLists.txt make sudo make installls /usr/local/lib存在以下文件说明安装成功 中间出了个问题就是,…...
CSDN 博客自动发布脚本(Python 含自动登录、定时发布)
文章目录 关于 csdn auto publisher使用 关于 csdn auto publisher 源码地址:https://github.com/ezscode/csdn_auto_publisher 使用 def test_simple_pub():file_path /Users/xx/Documents/xxx/tool.md article Article(file_path) article.tags [python] art…...
RL78/G15 Fast Prototyping Board Arduino IDE 平台开发过程
这是一篇基于RL78/G15 Fast Prototyping Board的Arduino IDE开发记录 RL78/G15 Fast Prototyping Board硬件简介(背景)基础测试(方法说明/操作说明)开发环境搭建(方法说明/操作说明代码结果)Arduino IDE RL…...
VsCode 插件推荐(个人常用)
VsCode 插件推荐(个人常用)...
零基础学安全--云技术基础
目录 学习连接 前言 云技术历史 云服务 公有云服务商 云分类 基础设施即服务(IaaS) 平台即服务(PaaS) 软件即服务(SaaS) 云架构 虚拟化 容器 云架构设计 组件选择 基础设施即代码 集成部署…...