Linux进程怎么创建和启动

发布时间:2022-02-18 09:25:25 作者:iii
来源:亿速云 阅读:578
# Linux进程怎么创建和启动

## 1. 进程基础概念

### 1.1 什么是进程
进程(Process)是Linux系统中程序执行的基本单位,它是操作系统资源分配和调度的独立实体。每个进程都有自己独立的地址空间、堆栈、文件描述符等系统资源。

进程与程序的区别:
- 程序是静态的存储在磁盘上的可执行文件
- 进程是程序在内存中的动态执行实例

### 1.2 进程的生命周期
Linux进程通常经历以下状态变化:
1. 创建(Created)
2. 就绪(Ready)
3. 运行(Running)
4. 阻塞(Blocked)
5. 终止(Terminated)

## 2. 进程创建机制

### 2.1 fork()系统调用
`fork()`是Linux创建新进程的基本方式:

```c
#include <unistd.h>

pid_t fork(void);

工作原理: 1. 复制父进程的地址空间、堆栈、文件描述符等 2. 创建新的进程控制块(PCB) 3. 返回两次: - 父进程中返回子进程PID - 子进程中返回0

示例代码:

#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();
    
    if (pid < 0) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        printf("This is child process (PID: %d)\n", getpid());
    } else {
        printf("This is parent process (PID: %d), child PID: %d\n", 
               getpid(), pid);
    }
    return 0;
}

2.2 写时复制技术(Copy-On-Write)

现代Linux系统采用写时复制优化fork()性能: - 父子进程最初共享相同的物理内存页 - 只有当任一进程尝试修改内存页时,才会复制该页 - 显著减少fork()的开销

3. 进程启动机制

3.1 exec系列函数

exec函数族用于将当前进程映像替换为新的程序:

#include <unistd.h>

int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);

典型使用模式:

pid_t pid = fork();
if (pid == 0) {
    // 子进程
    execl("/bin/ls", "ls", "-l", NULL);
    perror("exec failed");
    exit(1);
}

3.2 fork-exec模型

Linux标准进程创建流程: 1. 父进程调用fork()创建子进程 2. 子进程调用exec()加载新程序 3. 父进程通过wait()等待子进程结束

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程
        execlp("ls", "ls", "-l", NULL);
        perror("exec failed");
        return 1;
    } else if (pid > 0) {
        // 父进程
        int status;
        waitpid(pid, &status, 0);
        printf("Child process exited with status %d\n", WEXITSTATUS(status));
    } else {
        perror("fork failed");
        return 1;
    }
    return 0;
}

4. 进程启动的高级方式

4.1 system()函数

简化版的进程创建和启动:

#include <stdlib.h>

int system(const char *command);

示例:

#include <stdlib.h>

int main() {
    int ret = system("ls -l /");
    printf("Command exited with status %d\n", ret);
    return 0;
}

注意:system()会创建shell进程来执行命令,存在安全风险。

4.2 posix_spawn()

更高效的进程创建方式:

#include <spawn.h>

int posix_spawn(pid_t *pid, const char *path,
                const posix_spawn_file_actions_t *file_actions,
                const posix_spawnattr_t *attrp,
                char *const argv[], char *const envp[]);

优点: - 合并了fork和exec操作 - 允许更精细控制文件描述符和属性

4.3 clone()系统调用

提供更灵活的进程创建:

#define _GNU_SOURCE
#include <sched.h>

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...);

特点: - 可以控制共享哪些资源 - 用于实现线程(轻量级进程)

5. 进程启动的Shell视角

5.1 Shell执行命令的流程

  1. 解析命令行
  2. 查找命令路径
  3. 创建子进程(fork)
  4. 子进程执行命令(exec)
  5. 父进程等待子进程结束(wait)

5.2 环境变量影响

重要环境变量: - PATH:命令搜索路径 - LD_LIBRARY_PATH:动态库搜索路径 - HOME:用户主目录

5.3 后台进程与守护进程

启动后台进程:

command &

创建守护进程: 1. fork()创建子进程 2. 子进程调用setsid()创建新会话 3. 改变工作目录到/ 4. 重定向标准I/O 5. 处理信号

6. 进程启动的性能考量

6.1 fork()的开销

影响因素: - 进程地址空间大小 - 页表复制开销 - 写时复制触发频率

优化策略: - 减少不必要的fork() - 使用vfork()(已过时,不推荐) - 考虑posix_spawn()

6.2 exec()的开销

影响因素: - 程序文件大小 - 动态链接库数量 - 环境变量数量

优化策略: - 静态链接减少动态库加载 - 精简环境变量 - 使用execve()直接指定环境

7. 安全注意事项

7.1 权限控制

7.2 注入攻击防范

7.3 资源限制

设置资源限制:

#include <sys/resource.h>

setrlimit(RLIMIT_CPU, &limit);

常见限制类型: - CPU时间 - 内存使用 - 文件大小 - 进程数

8. 实际应用案例

8.1 实现简单的Shell

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

#define MAX_ARGS 20

void parse_command(char *cmd, char **argv) {
    char *token = strtok(cmd, " ");
    int i = 0;
    while (token != NULL && i < MAX_ARGS-1) {
        argv[i++] = token;
        token = strtok(NULL, " ");
    }
    argv[i] = NULL;
}

int main() {
    char cmd[256];
    char *argv[MAX_ARGS];
    
    while (1) {
        printf("mysh> ");
        if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
            break;
        }
        
        cmd[strcspn(cmd, "\n")] = '\0'; // 移除换行符
        if (strlen(cmd) == 0) continue;
        
        parse_command(cmd, argv);
        
        if (strcmp(argv[0], "exit") == 0) {
            break;
        }
        
        pid_t pid = fork();
        if (pid == 0) {
            execvp(argv[0], argv);
            perror("exec failed");
            exit(1);
        } else if (pid > 0) {
            wait(NULL);
        } else {
            perror("fork failed");
        }
    }
    return 0;
}

8.2 实现进程池

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define POOL_SIZE 5

void worker(int id) {
    printf("Worker %d (PID: %d) started\n", id, getpid());
    sleep(3);
    printf("Worker %d (PID: %d) finished\n", id, getpid());
    exit(0);
}

int main() {
    int i;
    pid_t pids[POOL_SIZE];
    
    // 创建工作进程
    for (i = 0; i < POOL_SIZE; i++) {
        pids[i] = fork();
        if (pids[i] == 0) {
            worker(i);
        } else if (pids[i] < 0) {
            perror("fork failed");
            exit(1);
        }
    }
    
    // 等待所有工作进程结束
    for (i = 0; i < POOL_SIZE; i++) {
        waitpid(pids[i], NULL, 0);
    }
    
    printf("All workers completed\n");
    return 0;
}

9. 调试与监控

9.1 跟踪进程创建

使用strace跟踪:

strace -f -e trace=process command

9.2 查看进程树

pstree -p

9.3 性能分析

使用time测量:

time command

使用perf分析:

perf stat command

10. 总结

Linux进程创建和启动涉及多个关键系统调用和复杂机制:

  1. fork()创建新进程,exec()加载新程序
  2. 写时复制技术优化fork性能
  3. 多种进程创建方式适用于不同场景
  4. 安全性和性能是需要重点考虑的因素
  5. 理解底层机制有助于编写高效可靠的程序

掌握这些知识对于Linux系统编程、性能优化和安全加固都至关重要。 “`

推荐阅读:
  1. ElastiCache For Redis Cluster创
  2. Linux进程管理

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

linux

上一篇:Linux的fork函数使用实例分析

下一篇:Linux中如何使用xauth命令

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》