Linux网络编程2——多进程编程

一.进程

1.概念复习

程序:程序(program)是存放在磁盘文件中的可执行文件
进程:程序的执行实例被称为进程(process)。

  • 进程具有独立的权限与职责。
  • 如果系统中某个进程崩溃,它不会影响到其余的进程。
  • 每个进程运行在其各自的虚拟地址空间中,进程之间可以通过由内核控制的机制相互通讯。

进程ID:每个linux进程都一定有一个唯一的数字标识符,称为进程ID(process ID),进程ID总是一非负整数。

task_struct===》进程表项(进程控制块)

2.main()函数

我们知道:

void main(){}

int main()
{
    return 0;
}

但是main()函数我们或多或少见识过带参数的,比如:

int main(int argc,char *argv[])
{
    return 0;
}
  • argc:函数内参数的个数,编译器自动计算传入
  • argv:函数内的参数都会被当作字符串类型存储到这里,中间默认用空格分隔

启动例程:

  • 启动例程在main()函数执行之前内核就会启动
  • 在编译时,启动例程代码就会和用户写的代码进行编译链接到可执行文件中
  • 启动例程的作用是收集命令行的参数传递给main()函数的argcargv

3.进程终止方式

正常终止:

  • main函数返回
  • 调用exit(标准c库函数)
  • 调用 _exit_Exit(系统调用)
  • 最后一个线程从其启动例程返回
  • 最后一个线程调用pthread_exit

异常终止:

  • 调用abort
  • 接受到一个信号并终止
  • 最后一个线程对取消请求做处理响应

4.atexit函数

#include <stdlib.h>
int atexit(void (*function)(void))                 //传入自定义终止函数的函数指针
    
//返回: 若成功则为0,若出铅则为-1
//功能: 向内核登记终止函数
  • 每个启动的进程都默认登记了一个标准的终止函数
  • 终止函数在进程终止时释放进程所占用的一些资源
  • 登记的多个终止函数执行顺序是以栈的方式执行,先登记的后执行。

二.进程控制

1.fork()函数

作用:创建一个子进程

在这里插入图片描述

通过fork()创建的子进程类似于父进程的复制版本,fork()以前的代码子进程虽然也有但已不再执行,而fork()后面的代码父子进程分开执行,从而导致有两个返回值,子进程创建成功则返回0,而父进程则返回创建的子进程的pid

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

void term_fun1()
{
    printf("first term function\n");
}

void term_fun2()
{
    printf("second term function\n");
}

int main()
{
    printf("before fork1\n");
    printf("before fork2\n");
    pid_t pid=fork();
    if(pid<0)
    {
        perror("fork error\n");
        exit(1);
    }
    else if(pid==0)
    {
        printf("---child process is created\n");
    }
    else if(pid>0)
    {
        printf("---praent process:my child process is %d\n",pid);
    }
    printf("===============================end process\n");
}

结果:

before fork1
before fork2
—praent process:my child process is 10114
===============================end process
—child process is created
===============================end process

先执行父进程,再执行子进程。

2.getpid与getppid

  • getpid:获取自己的pid号
  • getppid:获取父进程的pid号

改一下上面代码:

    if(pid<0)
    {
        perror("fork error\n");
        exit(1);
    }
    else if(pid==0)
    {
        printf("---child process is created,my_pid:%d,my_prapid:%d\n",getpid(),getppid());
    }
    else if(pid>0)
    {
        printf("---praent process:my child process is %d,my_pid:%d,my_prapid:%d\n",pid,getpid(),getppid());
    }

3.循环建立n个进程

问题一:

  • 怎么建立n个?
  • 怎么保证顺序?
int main()
{
	for(int i=0;i<5;i++)
    {
        if(fork()==0)
            break;
    }
    if(5==i)
    {
        sleep(5);
        printf("I'm parent\n");
    }
    else
    {
        sleep(i);
        printf("I'm %dth child\n",i+1);
    }
    
    return 0;
}

三.其他函数

1.exec函数族

fork 创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新进程,所以调用exec前后该进程的 id 并未改变。重点掌握:execlexeclp

execlp

  • 加载一个进程,借助 PATH 环境变量

    int execlp(const char *file, const char *arg, ...);
    
  • 参数 1:要加载的程序的名字。该函数需要配合 PATH 环境变量来使用,当 PATH 中所有目录搜索后没有参数 1(即环境变量中没有对应的程序)则出错返回。

  • 该函数通常用来调用系统程序。如: ls、date、cp、cat 等命令。“

execl

  • 加载一个进程,通过 路径+程序名 来加载。

    int execl(const char *path, const char *arg, ...);
    
  • 对比execlp,如加载"ls"命令带有-1,-F 参数,使用参数1给出的绝对路径搜索。

    execlp("ls","ls","-l","-F",NULL);
    execl("/bin/s","ls","-l","-F",NULL);
    

2.孤儿进程与僵尸进程

  • 孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程变为init进程,也可以叫init进程领养孤儿进程。

    怎么处理一个孤儿进程:直接kill杀死

  • 僵尸进程:进程终止,父进程尚未回收,子进程残留资源PCB存放在内核中,变成僵尸进程。

    怎么处理一个僵尸进程:kill杀死其父进程,使其变为孤儿进程,init进程发现其是僵尸进程后自动被回收。

  • 守护进程:守护进程运行在后台,不跟任何控制终端关联

    作用:确保运行程序完整执行

3.wait和waitpid

作用:waitpidwait,但可以指定pid进程清理

注意:一次wait/waitpid的函数调用,只能回收一个子进程。

语法:

pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
  • wait函数成功执行返回要终止(回收)子进程的pid;失败则返回-1
  • waitwaitpid里的int *wstatus参数意思是把回收子进程的状态信息存储到int *所指的内存空间里,这里的状态信息是以宏的方式存储
  • options:利用进程发送变换的状态进行回收,一般选择WNOHANG
  • waitpid函数的pid传-1,代表回收任意子进程

代码理解:

wait(NULL);                                //阻塞直至回收任意一个进程
wpid=waitpid(-1,NULL,WNOHANG);             //回收任意子进程,没有结束的子进程,父进程直接返回0
wpid=waitpid(-1,NULL,0);                   //阻塞回收某一子进程

四.进程间通信(未完待续)

同一主机的进程间通信:

  • 管道(Pipe):用于父子进程间通信
  • 命名管道(FIFO):不相关的进程间通信,不适合大量数据传输
  • 消息队列(Message Queue):适用于需要缓冲和异步处理的场景
  • 共享内存(Shared Memory):允许多个进程访问同一块内存区域,适合大数据量传输
  • 信号量(Semaphore):用于控制对共享资源的访问
  • 信号(Signal):用于通知接收进程某个事件已经发生(重点)

不同主机间的进程通信:

  • 服务器与客户端(Socket):网络编程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨城烟柳ベ旧人殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值