一、进程
1.进程是动态的,是程序的一次运行活动。
2.进程在内存中由数据段、堆栈段和代码段组成。c程序内存分布图如下
3.每个进程都有自己的进程标识符pid,可以调用getpid()来得到自己的pid,getppid()获得父进程的标识符
4.创建子进程:
pid_t fork(void);
fork函数调用一次返回两次,子进程的返回值是0,父进程的返回值是子进程的PID,因此通常利用这个区别去设置判断语句令父子进程执行不同的操作。一般来说,fork之后的父子进程的执行顺序是不确定的,这个取决于调度算法。
fork之后的处理方法:
- fork之后子进程和父进程处理不同的代码段
- fork之后调用exec,让子进程处理完全不同的程序
pid_t vfork(void);
vfork创建一个子进程,但是该子进程并不对父进程地址空间进行拷贝而是直接共享。因此如果让子进程对父进程数据进行读或写都可能产生段错误。主要原因在于,因为vfork产生的子进程是要exec一个新程序的,于是也就不会引用该地址空间了,不过子进程再调用exec()或
exit()之前,他将在父进程的空间中运行,但如果子进程想尝试修改数据域(数据段、堆、栈)都会带来未知的结果,就算写实复制copyonwrite(COW)( 这些数据区域由父子进程共享,内核将他们的访问权限改成只读,如果父进程和子进程中的任何一个试图修改这些区域的时候,内核再为修改区域的那块内存制作一个副本。)机制也不如索性不复制节约时间。
vfork保证子进程先运行,在它调用exec或者exit后父进程才可以被调用执
5.wait()和waitpid()
pid_t wait(int *status);
当一个进程正常或异常退出时,内核就会向其父进程发送SIGCHLD信号。因为子进程退出是一个异步事件,所以这种信号也是内核向父进程发送的一个异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即将被执行的函数,父进程可以调用wait()或waitpid()可以用来查看子进程退出的状态。进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
参数:
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针止。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,可以直接传NULL。
返回值:
如果成功,wait会返回被收集的子进程的进程ID
如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
pid_t waitpid(pid_t pid,int *status,int options);
系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,提供了另一种更灵活的方式。
二、使用多进程编程改写服务器的流程如下:
**服务器端的代码如下:**
/*********************************************************************************
* Copyright: (C) 2021 jiaoer237
* All rights reserved.
*
* Filename: socket_server_fork.c
* Description: This file
*
* Version: 1.0.0(11/23/2021)
* Author: yanp <2405204881@qq.com>
* ChangeLog: 1, Release initial version on "11/23/2021 04:35:39 PM"
*
********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>
#define MSG_STR "Hello yanp\n"
#define BACKLOG 13
void printf_usage(char *program);
int socket_init(char *listen_ip,int listen_port);
int main(int argc,char **argv)
{
int daemon_run=0;
char *program;
int serv_port=0;
int listen_fd;
int clifd=-1;
int rv=-2;
int opt;
struct sockaddr_in cliaddr;
socklen_t cliaddr_len;
pid_t pid;
struct option long_options[] =/*参数解析包括需要监听的端口号和是否在后台运行*/
{
{
"daemon", no_argument, NULL, 'b'},
{
"port", required_argument, NULL, 'p'},
{
"help", no_argument, NULL, 'h'},
{
NULL, 0, NULL, 0}
};
program=argv[0];
while((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)
{
switch(opt)
{
case 'b':
daemon_run=1;
break;
case 'p':
serv_port = atoi(optarg);/*将字符串转换成整型*/
break;
case 'h':