设为首页 加入收藏

TOP

C多进程(一)
2023-07-23 13:41:26 】 浏览:101
Tags:
这篇文章主要是想针对多进程的创建和一些通信手段来进行一下记录

创建子进程

关于创建子进程的原型一般都是用的这个,直接fork,这个函数在父进程中调用,在父子进程中各有一个pid_t类型的返回值,父进程中得到的是子进程的ID,子进程中得到的是0值。当然调用失败就是-1。

//创建进程,然后复制出另一份进程
#include <unistd.h>
pid_t fork();

根据不同的fork返回值,父子进程可以分出自己专属的代码区域段。例子如下:

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

int i = 10;
int main() {
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        i++;
        printf("I' m the subprocess.The i:%d\n", i);
    } else {
        i--;
        printf("I' m the parent process.The i:%d\n", i);
    }
    return 0;
}

一般来说,写代码的理想状态是最后的程序正常跑,更理想的就是完全不出错,不过那个太理想了。比如多进程程序中,当父进程结束了,子进程没有被父进程获取状态信息,从而使得进程号依然保留在系统中,占用系统定数的进程号;又比如父进程都结束运行了,子进程还在继续跑,由init进程来接管。这两种情况,前者被叫僵尸进程,后者被称为孤儿进程(这个概念其实我挺犯迷糊,如果有冲突那就是你对,记得提点一声)。所以,父进程在结束之前,要对子进程负责,要查询子进程的结束状态,并确保子进程跑完了才跑路。

wait一下

简单的方案,就是父进程一直等,实现这个功能的函数原型如下:

#include <sys/wait.h>
pid_t wait(int *statloc);

//配合使用的宏
WIFEXITED(statloc);						//子进程正常终止,返回非0值
WEXITSTATUS(statloc);						//子进程正常终止,返回退出码
WIFSIGNALED(statloc);						//因为未捕获信号而终止,返回非0值
WTERMSIG(statloc);						//配合前一个宏,返回信号值
WIFSTOPPED(statloc);						//子进程意外终止,返回非0
WSTOPSIG(statloc);						//子进程意外终止,返回信号值

上面函数的通用解读就是,wait函数的调用会阻塞父进程,一直等着子进程跑完返回状态信息到statloc才对父进程放行。而对于子进程的结束信息的解读,就是上面对应的宏来进行。不过wait的阻塞让很多人不满,所以他们实现了另一种wait:

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);

使用waitpid处理僵尸进程:

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

int main() {
    pid_t pid;
    int status, i=0;
    pid = fork();
    if (pid == 0) {
        i--;
        printf("subprocess: %d\n", i);
        sleep(5);
        return 6;
    } else {
        //因为只有一个子进程,就不明确指定了
        while (!waitpid(-1, &status, WNOHANG)) {
            i++;
            printf("parent process, %d sec\n", i);
            sleep(1);
        }
        if (WIFEXITED(status))
            printf("Subprocess was ended and return a value :%d\n", WEXITSTATUS(status));
    }
    return 0;
}

进程间通信

比较简单的通信方式,是创建管道,管道和socket套接字同属系统资源,创建了管道,就是使得两个管道在系统提供的内存进行通信。实现的原型如下:

#include <unistd.h>
int pipe(int filedes[2]);

所谓管道,是有着两个口子的,这里的管道也一样,filedes就是一个包含了两个文件描述符的数组,一般传入的这个参数是空的,函数调用结束后就成了新创建的管道的入口和出口。
嗯,所以这个管道的使用,其实就是这两个描述符的使用,filedes数组中,第一个是管道入口,第二个是管道出口,这个要注意。

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

int main() {

    pid_t pid;
    int fds[2];
    char str[20];
    pipe(fds);
    pid = fork();

    if (pid != 0) {
        write(fds[1], "balabala", sizeof("balabala"));
        printf("parent process.\n");
        sleep(3);
    } else {
        read(fds[0], str, 20);
        printf("subprocess, get mes: %s\n", str);
    }

    return 0;
}

例子是父进程发送信息,子进程接收信息,实际上反过来也可以,不限定。但信息放进管道,父子进程其实都可以读取,就像写了信息在文本,谁都可以读取。管道的单向只体现在它的信息是从fds[1]进,fds[0]出。为了保证
信息的受众是对端从而实现双方通信,往往实现两个管道,然后一个管道负责发,一个负责收,这样就不需要预测运行流程。

管道是很便利,但它往往适用于关联进程(像父子进程),想要无关联的通信还需要其他机制,比如下面的3种System V IPC。

System V IPC

针对共享资源的多进程访问,这种独占式的访问会引发大问题,谁先谁后无法控制,这种引发竞争的代码段,被称为临界区。对进程的同步,就是确保进入临界区只有一个进程。

信号量

它是一个特殊的整数值变量,只支持两种操作,一个是取,一个是放,分别是P原语和V原语的解读。因为针对多进程同步和多线程同步都有信号量的概念,虽然语义一致,但实现不一样,姑且把多进程间信号量称为信号量,多线程间信号量称为POSIX信号量。对于信号量的初始化决定了其行为,但最常用的就是二进制信号量,用0和1来代表空置和占用的意义。linux中的实现,往往在sys/sem.h头文件中,三个系统调用设计成操作一组信号量而不是单个信号量,三个系统调用分别是semget、semop和semctl;而POSIX信号量的实现都在semaphore.h头文件中。

信号量的创建

#include <sys/sem.h>

//申请信号集,申请成功就返回信号量标记值,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);

semg

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Linux 基础-文本处理命令 下一篇Linux 系统环境监测

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目