Linux软件编程(五)(exec 函数族、system、线程)
子进程资源回收
当子进程结束后,如果父进程没有回收其资源,会留下 僵尸进程。
回收方式主要有 wait
和 waitpid
。
1.1 wait
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
功能
阻塞等待 任意子进程 退出,并回收其资源空间。
参数
wstatus
:保存子进程退出状态的变量地址NULL
:不保存退出状态
返回值
成功:回收的子进程 PID
失败:
-1
1.2 waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
功能
回收 指定子进程 资源,功能比 wait
更灵活。
参数说明
pid
< -1
:回收指定进程组的任意子进程(如-100
表示等待 GID=100 的进程组)-1
:回收任意子进程0
:回收与调用者同一进程组的所有子进程> 0
:回收指定 PID 的子进程
status
子进程退出时候的状态,
如果不关注退出状态用NULL;
options
0 表示回收过程会阻塞等待;
WNOHANG 表示非阻塞模式回收资源。
返回值
成功:成功 返回接收资源的子进程PID
失败:
-1
非阻塞模式下且无子进程退出:
0
1.3 资源回收策略
阻塞回收:
wait
或waitpid(pid, ..., 0)
非阻塞回收:
waitpid
+WNOHANG
(适合轮询)不回收:子进程的任务需要一直执行
异步回收:配合信号(如
SIGCHLD
)实现,子进程退出时通知父进程
一、exec 函数族
在一个进程里面执行另一个文件(可执行文件)。
本质:将进程文本区的指令代码替换成 exec 要执行的文件的指令。
执行成功后不返回,执行失败返回 -1。
1.
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
功能:执行指定路径的可执行文件
参数:
path
:可执行文件的路径和名称arg
:执行该文件时要传递的参数(arg0
通常为程序本身的名称)NULL
:参数结束标志
返回值:
出错:
-1
2. int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
功能:从
PATH
指定的系统路径中寻找可执行文件参数:
file
:可执行文件的名称(系统路径下已有)arg
:执行该文件时要传递的参数NULL
:参数结束标志
3. int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
功能:执行指定路径的可执行文件,并可传入自定义环境变量
参数:
path
:可执行文件路径arg
:参数列表NULL
:参数结束标志envp[]
:自定义环境变量数组(KEY=value
格式,以 NULL 结束)
4. int execv(const char *path, char *const argv[]);
功能:执行指定路径的可执行文件,参数用字符串数组传递
参数:
path
:可执行文件路径argv[]
:参数数组(最后一个元素为 NULL)
5. int execvp(const char *file, char *const argv[]);
功能:从
PATH
系统路径搜索文件,参数用字符串数组传递参数:
file
:文件名(系统路径中已有)argv[]
:参数数组(最后一个元素为 NULL)
6. int execvpe(const char *file, char *const argv[], char *const envp[]);
功能:结合
execvp
功能,并支持自定义环境变量(GNU 扩展)参数:
file
:文件名(从 PATH 搜索)argv[]
:参数数组(NULL 结束)envp[]
:环境变量数组(NULL 结束)
exec 名称含义
l:list(参数列表传递)
p:path(从系统 PATH 中查找)
v:vector(数组传递)
e:environment(可传自定义环境变量)
二、system 函数
作用
system()
会调用一次 fork()
创建子进程,在子进程中执行命令,父进程等待子进程结束。
#include <stdlib.h> int system(const char *command);
特点:
简单易用
内部实际是
fork() + exec() + wait()
需要 shell 环境解析命令
三、自定义 system 实现
通过 fork()
+ execvp()
手动实现 system
功能:
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> #include <string.h> int my_system(char *buff) { char *arg[10] = {NULL}; char cmd[512] = {0}; strcpy(cmd, buff); int i = 0; arg[i] = strtok(cmd, " "); while (arg[i] != NULL) { i++; arg[i] = strtok(NULL, " "); } pid_t pid = fork(); if (pid > 0) { wait(NULL); } else if (pid == 0) { execvp(arg[0], arg); } return 0; } int main() { printf("system : pid = %d\n", getpid()); my_system("ls -l"); printf("After system\n"); return 0; }
四、线程
1. 概念
进程:操作系统资源分配的最小单位
线程:操作系统任务调度的最小单位
线程由进程创建,拥有 独立的 8M 栈区,但共享进程的堆区、数据区、文本区
2. 进程与线程对比
对比 | 进程 | 线程 |
---|---|---|
资源消耗 | 分配 0~4G 虚拟空间,开销大 | 只分配 8M 栈区,开销小 |
创建速度 | 慢 | 快 |
数据共享 | 需 IPC | 直接共享进程内存 |
安全性 | 高,互不影响 | 低,一个线程崩溃可能导致整个进程崩溃 |
3. 线程相关编程
创建线程
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread
:保存线程 IDattr
:线程属性(默认NULL
)start_routine
:线程入口函数arg
:传递给线程的参数返回值:成功 0,失败 非 0
获取线程 ID
pthread_self();
注意:编译需加 -lpthread
。
五、线程创建示例
#include <stdio.h> #include <pthread.h> #include <unistd.h> void *task(void *arg) { printf("I am thread : tid = %ld\n", pthread_self()); return NULL; } int main() { pthread_t tid; if (pthread_create(&tid, NULL, task, NULL) != 0) { printf("pthread_create error\n"); return -1; } sleep(2); // 防止主线程提前结束 return 0; }