0.进程巫师
函数名称 | 参数 | 返回值 | 用法 |
---|---|---|---|
fork | 无 | 父进程返回子进程ID,子进程返回0 | pid_t fork(void); |
exec 系列 | const char *path, char *const argv[] | -1(失败), 不返回(成功) | int execv(const char *path, char *const argv[]); |
waitpid | pid_t pid, int *status, int options | 子进程ID | pid_t waitpid(pid_t pid, int *status, int options); |
exit | int status | 无 | void exit(int status); |
getpid | 无 | 进程ID | pid_t getpid(void); |
getppid | 无 | 父进程ID | pid_t getppid(void); |
pipe | int pipe(int pipefd[2]); | 0(成功), -1(失败) | 创建一个管道,pipefd[0] 用于读,pipefd[1] 用于写 |
dup | int oldfd | 新文件描述符 | int dup(int oldfd); |
dup2 | int oldfd, int newfd | 新文件描述符 | int dup2(int oldfd, int newfd); |
close | int fd | 0(成功), -1(失败) | 关闭文件描述符 |
kill | pid_t pid, int sig | 0(成功), -1(失败) | 向进程发送信号 |
signal | int signum, void (*handler)(int) | 先前的信号处理程序 | void (*signal(int signum, void (*handler)(int)))(int); |
sigaction | int signum, const struct sigaction *act, struct sigaction *oldact | 0(成功), -1(失败) | 安装、改变或检索信号处理程序 |
popen | const char *command, const char *type | FILE * | 打开一个管道并返回一个文件流,用于读取或写入子进程的标准输入/输出 |
pclose | FILE *stream | 终止进程并关闭文件流 | int pclose(FILE *stream); |
execl | const char *path, const char *arg, ... | -1(失败), 不返回(成功) | 执行一个文件 |
execle | const char *path, const char *arg, ..., char *const envp[] | -1(失败), 不返回(成功) | 执行一个文件,并提供新的环境变量 |
execvp | const char *file, char *const argv[] | -1(失败), 不返回(成功) | 通过搜索 PATH 执行一个文件 |
vfork | 无 | 父进程返回子进程ID,子进程返回0 | pid_t vfork(void); |
wait | int *status | 子进程ID | pid_t wait(int *status); |
1. 进程基础概念
a. 什么是进程?
- 进程是计算机中运行的程序的实例。它包括了程序执行时所需的代码、数据以及系统资源,每个进程都有其独立的内存空间。
b. 进程的状态(运行、就绪、阻塞等)
- 运行(Running): 进程正在执行。
- 就绪(Ready): 进程已准备好执行,等待分配CPU。
- 阻塞(Blocked): 进程等待某个事件的完成,如等待用户输入或等待文件I/O。
c. 进程的标识符(PID)
- 每个进程都有一个唯一的标识符,称为PID。可以使用ps
命令查看正在运行的进程及其PID。
ps aux
d. 父进程和子进程
- 进程可以通过fork
系统调用创建一个新的进程。新进程称为子进程,而调用fork
的进程称为父进程。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t child_pid = fork();
if (child_pid == 0) {
// 子进程执行的代码
printf("This is the child process\n");
} else if (child_pid > 0) {
// 父进程执行的代码
printf("This is the parent process\n");
} else {
// fork失败
perror("fork failed");
return 1;
}
return 0;
}
2. 进程的创建和终止
a. 进程的创建(fork、exec等)
- 进程的创建通常使用fork
系统调用。子进程可以通过exec
族函数加载新的程序。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t child_pid = fork();
if (child_pid == 0) {
// 子进程执行的代码
execlp("/bin/ls", "ls", NULL); // 以ls程序替换子进程
} else if (child_pid > 0) {
// 父进程执行的代码
wait(NULL); // 等待子进程结束
printf("Parent process finished\n");
} else {
perror("fork failed");
return 1;
}
return 0;
}
b. 进程的终止(exit)
- 进程可以通过exit
系统调用正常终止。
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("This is a process\n");
exit(0); // 正常退出
}
c. 僵尸进程和孤儿进程
- 僵尸进程是已经终止但父进程尚未回收的进程。孤儿进程是父进程已经终止而子进程仍在运行的进程。
3. 进程调度
a. 调度算法(先来先服务,短作业优先等)
- 调度算法决定了系统中不同进程之间的优先级和执行顺序。
b. nice值和优先级
- nice
命令用于调整进程的优先级。
nice -n 10 ./my_process
c. 进程调度器(如CFS)
- CFS(完全公平调度器)是Linux中的一个调度算法,通过时间片和进程优先级来平衡系统中运行的进程。
cat /proc/sys/kernel/sched_child_runs_first
5. 进程的优先级和调度策略
a. nice值和进程优先级
- nice
值用于调整进程的优先级,取值范围通常为-20(最高优先级)到19(最低优先级)。
nice -n 10 ./my_process
b. 调度策略(SCHED_FIFO、SCHED_RR等)
- Linux支持不同的调度策略,如先来先服务(SCHED_FIFO)、轮转调度(SCHED_RR)等。
#include <stdio.h>
#include <sched.h>
int main() {
struct sched_param param;
param.sched_priority = 50; // 优先级设置为50
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
perror("Error setting scheduler");
return 1;
}
// 进程的其他代码
return 0;
}
6. 进程的资源限制
a. 进程资源限制的设置(ulimit等)
- 使用ulimit
命令可以设置进程的资源限制,如文件打开数、CPU时间等。
ulimit -n 1000
b. 资源限制的影响
- 设置资源限制可以防止进程占用过多系统资源,导致系统不稳定.
7. 进程间通信(IPC)
方法 | 描述 | 使用场景 | 优点 | 缺点 |
---|---|---|---|---|
文件锁(Flock) | 利用文件锁机制,通过系统调用 flock 来实现,进程对文件的操作会受到文件锁的限制。 | 多进程读写同一文件时的资源互斥 | 简单易用,适用于文件级别的进程同步 | 只能在本地文件系统上使用,不能跨网络 |
信号(Signal) | 除了上文提到的通知外,还可以通过信号进行自定义通信,使用 kill 或 raise 发送信号。 | 需要快速的事件通知 | 轻量级,适用于简单的进程间通信 | 信号不支持传递大量数据,容易被中断或丢失数据 |
套接字(Socket) | 使用套接字进行通信,可以通过本地套接字或网络套接字实现进程间通信。 | 跨网络或本地通信 | 强大而灵活,适用于不同主机间的通信 | 相对较为复杂,需要网络编程知识 |
共享文件映射(Memory-Mapped Files) | 将文件映射到内存中,多个进程可同时读写该文件,实现共享内存的效果。 | 需要共享大量数据 | 高性能,适用于需要大量数据交换的场景 | 需要谨慎处理同步和互斥问题,不适用于大规模并发 |
RPC(Remote Procedure Call) | 调用远程进程的过程,通过网络协议进行通信,实现跨网络的进程通信。 | 跨网络通信 | 适用于分布式系统,实现进程间远程调用 | 需要网络编程知识,相对较为复杂 |
消费者-生产者队列(Message Queue) | 通过消息队列传递数据,可以实现多对多通信,进程通过队列进行数据交换。 | 异步通信,需要解耦生产者和消费者 | 适用于生产者和消费者模型,支持异步通信 | 性能相对较低,需要处理消息格式和序列化问题 |
a. 管道(Pipe)
- 管道是一种进程间通信的机制,通过pipe
系统调用创建。
#include <stdio.h>
#include <unistd.h>
int main() {
int pipe_fd[2];
char buffer[50];
if (pipe(pipe_fd) == -1) {
perror("Pipe creation failed");
return 1;
}
write(pipe_fd[1], "Hello, Pipe!", 13);
read(pipe_fd[0], buffer, sizeof(buffer));
printf("Received message: %s\n", buffer);
return 0;
}
b. 消息队列
- 消息队列用于在进程之间传递消息,通过msgget
、msgsnd
和msgrcv
等系统调用实现。
// 文件1:msg_send.c
#include <stdio.h>
#include <sys/msg.h>
struct msg_buffer {
long msg_type;
char msg_text[50];
};
int main() {
// 创建消息队列
key_t key = ftok("msg_send.c", 65);
int msgid = msgget(key, 0666 | IPC_CREAT);
// 发送消息
struct msg_buffer message;
message.msg_type = 1;
sprintf(message.msg_text, "Hello, Message Queue!");
msgsnd(msgid, &message, sizeof(message), 0);
return 0;
}
// 文件2:msg_receive.c
#include <stdio.h>
#include <sys/msg.h>
struct msg_buffer {
long msg_type;
char msg_text[50];
};
int main() {
// 获取消息队列
key_t key = ftok("msg_send.c", 65);
int msgid = msgget(key, 0666 | IPC_CREAT);
// 接收消息
struct msg_buffer message;
msgrcv(msgid, &message, sizeof(message), 1, 0);
printf("Received message: %s\n", message.msg_text);
// 删除消息队列
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
c. 信号(Signal)
- 信号用于在进程之间传递简单的通知,通过kill
等系统调用发送信号。
#include <stdio.h>
#include <signal.h>
int main() {
// 信号处理函数
void sig_handler(int signo) {
if (signo == SIGUSR1) {
printf("Received SIGUSR1\n");
}
}
signal(SIGUSR1, sig_handler); // 注册信号处理函数
// 发送信号
kill(getpid(), SIGUSR1);
return 0;
}
d. 共享内存
- 共享内存允许多个进程共享一块内存区域,通过shmget
、shmat
等系统调用实现。
// 文件1:shm_write.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
// 创建共享内存
key_t key = ftok("shm_write.c", 65);
int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
// 连接到共享内存
char *str = (char*)shmat(shmid, (void*)0, 0);
// 写入数据
sprintf(str, "Hello, Shared Memory!");
// 分离共享内存
shmdt(str);
return 0;
}
// 文件2:shm_read.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
// 获取共享内存
key_t key = ftok("shm_write.c", 65);
int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
// 连接到共享内存
char *str = (char*)shmat(shmid, (void*)0, 0);
printf("Data read from shared memory: %s\n", str);
// 分离共享内存
shmdt(str);
// 删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
e. 信号量
- 信号量用于进程间的同步与互斥,通过sem_open
、sem_wait
和sem_post
等系统调用实现。
// 文件1:semaphore_create.c
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
int main() {
// 创建信号量
sem_t *sem = sem_open("/my_semaphore", O_CREAT, 0644, 1);
// 进行信号量操作
sem_wait(sem);
// 其他进程间的操作
sem_post(sem);
// 关闭并删除信号量
sem_close(sem);
sem_unlink("/my_semaphore");
return 0;
}
// 文件2:semaphore_use.c
#include <stdio.h>
#include <semaphore.h>
int main() {
// 打开现有的信号量
sem_t *sem = sem_open("/my_semaphore", O_CREAT);
// 进行信号量操作
sem_wait(sem);
// 其他进程间的操作
sem_post(sem);
// 关闭信号量
sem_close(sem);
return 0;
}
8. 进程监控和管理工具
a. ps
命令
- 查看当前系统进程的详细信息。
ps aux
b. top
命令
- 实时查看系统进程的运行情况,包括CPU、内存的占用情况。
top
c. htop
工具
- htop
是一个交互式的进程查看工具,提供了更友好的界面和操作。
htop
d. pstree
命令
- 显示进程树,展示进程之间的关系。
pstree
9. Shell脚本与进程控制
a. 后台运行进程
- 使用&
符号将进程放置到后台运行。
./my_process &
b. 前台运行进程
- 在终端直接运行进程,该进程将占用当前终端。
./my_process
c. 进程的暂停和继续
- 使用Ctrl + Z
可以将前台运行的进程暂停,使用bg
和fg
命令可以将进程放到后台或前台运行。
Ctrl + Z
bg
fg
10. 实际应用和案例分析
a. 进程的实际应用场景
- 详细描述一个实际应用场景,例如Web服务器中的进程管理。
b. 分析一个实际进程的状态和资源使用情况
- 使用ps
、top
等工具分析一个正在运行的进程,查看其状态和资源占用情况。
11. 安全性与权限
a. 进程的权限管理
- 说明进程的权限是如何管理和分配的。
b. su
和sudo
命令
- su
用于切换用户,sudo
用于以超级用户权限执行命令。
su username
sudo command