多线程学习(C/C++)

1.进程

        运行着的程序就是进程

        进程的特性:1.独立性 2.动态性 3.并发性

        (1)进程的状态

        进程一共有五种状态分别为:创建态,就绪态,运行态,阻塞态(挂起态),退出态(终止态)其中创建态和退出态维持的时间是非常短的,稍纵即逝。我们主要是需要将就绪态, 运行态, 挂起态,三者之间的状态切换搞明白        

多线程学习(C/C++)_第1张图片

        就绪态: 万事俱备,只欠东风(CPU资源)

  • 进程被创建出来了,有运行的资格但是还没有运行,需要抢CPU时间片
  • 得到CPU时间片,进程开始运行,从就绪态转换为运行态。
  • 进程的CPU时间片用完了, 再次失去CPU, 从运行态转换为就绪态。


        运行态:获取到CPU资源的进程,进程只有在这种状态下才能运行

  • 运行态不会一直持续,进程的CPU时间片用完之后, 再次失去CPU,从运行态转换为就绪态
  • 只要进程还没有退出,就会在就绪态和运行态之间不停的切换。

        阻塞态:进程被强制放弃CPU,并且没有抢夺CPU时间片的资格

  • 比如: 在程序中调用了某些函数(比如: sleep()),进程又运行态转换为阻塞态(挂起态)
  • 当某些条件被满足了(比如:slee() 睡醒了),进程的阻塞状态也就被解除了,进程从阻塞态转换为就绪态。

         退出态: 进程被销毁, 占用的系统资源被释放了

  • 任何状态的进程都可以直接转换为退出态。

        创建子进程的过程就是cow(copy-on-write)写时拷贝

        写时拷贝(COW):copy-on-write:多个调用者指向同一块地址空间,如果其中有一个要对地址空间的内容进行修改的话,就会拷贝一个内容、大小和原来地址空间一模一样的地址,然后再让要修改的变量指向拷贝的那一块地址空间,然后再进行修改。写时拷贝的优点:减少资源的占用,缺点:只是将资源的占用延后了

        (2)特殊进程
                1.僵尸进程             

        在一个启动的进程中创建子进程,这时候就有了父子两个进程,父进程正常运行, 子进程先与父进程结束, 子进程无法释放自己的PCB资源, 需要父进程来做这个件事儿, 但是如果父进程也不管, 这时候子进程就变成了僵尸进程。

                2.孤儿进程

        在一个启动的进程中创建子进程,这时候父子进程同时运行,但是父进程由于某种原因先退出了,子进程还在运行,这时候这个子进程就可以被称之为孤儿进程(跟现实是一样的)。

                3.守护进程

        守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。(守护进程是一种特殊的孤儿进程)

1.Linux下查看系统进程信息的命令

ps aux
    - a: 查看所有终端的信息
    - u: 查看用户相关的信息
    - x: 显示和终端无关的进程信息

多线程学习(C/C++)_第2张图片

ps ajx

     - a: 查看所有终端的信息

     -j: 采用工作控制的格式显示进程状况

    - x: 显示和终端无关的进程信息

多线程学习(C/C++)_第3张图片

2.进程函数操作中使用的一些常用函数    

(1)创建子进程函数fork、vfork

#include

#include

pid_t fork(void);

功能:

            在已有的进程基础上又创建一个子进程

参数:

           

返回值:

             成功:

                  >0 子进程的进程号,标识父进程的代码区

                  =0 子进程的代码区

             失败:

                  -1 返回给父进程,子进程不会创建

#include

#include

pid_t vfork(void);

功能:

            在已有的进程基础上又创建一个子进程

参数:

           

返回值:

             成功:

                  >0 子进程的进程号,标识父进程的代码区

                  =0 子进程的代码区

             失败:

                  -1 返回给父进程,子进程不会创建

        

[1]fork与vfork的区别:

1)fork(): 子进程拷贝父进程的堆栈段、数据段,代码段

     vfork(): 子进程与父进程共享资源

2)fork(): 父子进程执行次序不确定

     vfork(): 保证子进程先运行,在调用exec()或exit()之前,与父进程数据共享,在exec()或exit()调用之后,父进程才能运行

(2)获取进程号函数 :getpid、getppid
#include
#include
pid_t getpid(void);
功能:
     用来取得执行目前进程的pid号
参数:
     
返回值:
      目前进程的pid号

#include

#include

pid_t getppid(void);

功能:

        用来取得执行目前进程的父进程的pid号

参数:     

        无

返回值:      

        当前进程的父进程的pid号

#include 
#include 
#include 
int main(){
    int num = 10;
    pid_t pid =  fork();
    //pid_t pid =  vfork();
    if(pid > 0){
        num += 10;
        printf("in parent child pid %d, %d\n",pid, num);
        exit(0); //退出进程
    }else if(0 == pid){
        num += 5;
        printf("in child pid %d, num  %d\n",getpid(), num);
        exit(0); //退出进程
    }else{
        printf("failed\n");
    }
    return 0;
}

运行结果:

多线程学习(C/C++)_第4张图片 (3)进程回收函数wait、waitpid

        为了避免僵尸进程的产生,一般我们会在父进程中进行子进程的资源回收,回收方式有两种,一种是阻塞方式wait(),一种是非阻塞方式waitpid()

 3.进程间通信的方法

进程间的通信方式有:(一共7种方式)
1.管道:无名管道、有名管道
2.信号:信号 信号量
3.消息队列
4.共享内存
5.套接字

(1)进程间通信——无名管道pipe
         1.创建无名管道函数pipe

#include

#include

int pipe(int pipefd[2])

作用:创建一个匿名管道,用来进程间通信;

参数:

        int pipefd[2]这个数组是一个传出参数;

                pipefd[0] 对应管道的读端;

                pipefd[1] 对应管道的写端;

返回值:

        成功 0;

        失败-1;

#include
#include
#include
int main(){
    int fd[2] = {-1, -1}; //fd[0]是读端 fd[1]是写端
    int res = pipe(fd);
    if(res == -1){
        perror("create pipe failed");
        return -1;
    }
    pid_t pid = fork();
    if(pid > 0){
        wait(NULL); //等待子进程退出
        printf("in parent\n");
        char buf[100] = {0};
        read(fd[0], buf,100);
        printf("recv %s",buf);
        exit(0);
    }else if(pid == 0){
        printf("in child\n");
        write(fd[1], "hello", 5);
        exit(0); //系统退出,退出进程
    }
    return 0; //函数的返回
}

 运行结果:

多线程学习(C/C++)_第5张图片

(2)进程间通信——有名管道mkfifo
        1.创建无名管道函数(mkfifo——创建管道文件)

#include
#include

int mkfifo(const char *filename,mode_t mode);

功能:

        创建一个管道文件

参数:

        filename:管道文件名

        mode:管道文件权限

               O_RDONLY:读管道

    O_WRONLY:写管道

    O_RDWR:读写管道

    O_NONBLOCK:非阻塞

返回值:

        若函数成功执行则返回值为0

        失败返回-1

#include
#include
#include
#include
#include
#include
int main(){
    int ret = mkfifo("myfifo",0666); //创建管道文件
    if(ret == -1){
        perror("mkfifo");
        return -1;
    }
    int fd = open("myfifo", O_RDWR);
    pid_t pid = fork();
    if(pid > 0){
        wait(NULL);
        printf("in parent\n");
        char buf[100] = {0};
        read(fd, buf,100);
        printf("recv %s",buf);
        exit(0);
    }else if(pid == 0){
        printf("in child\n");
        write(fd, "hello", 5);
        exit(0); //系统退出,退出进程
    }
    return 0; //函数的返回
}

运行结果:

多线程学习(C/C++)_第6张图片 

(3)进程间通信——信号signal

Linux中的信号:可以用kill -l命令查看 

多线程学习(C/C++)_第7张图片

#include

typedef void(*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

功能:

        设置某个信号的捕捉行为
参数:
            signum:要捕捉的信号
            handler:捕捉到信号要如何处理
                        SIG_IGN:忽略信号
                        SIG_DFL:使用信号默认的行为

 回调函数:

                这个函数是内核调用,程序员只负责写,捕捉到信号后如何去处理信号。
 回调函数:
             需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义不是程序员调用,而是当信号产生,由内核调用函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置

返回值:
            成功返回上一次注册的信号处理函数的地址。第一次调用返回NULL
            失败返回SIG_ERR,设置错误号
            9)SIGKILL 和 19)SIGSTOP 不能被捕捉和忽略

#include

#include

int kill(pid_t pid, int sig);

功能:

        发送一个信号给进程号为pid的进程

参数:

        pid:进程的id

        sig:要发送信号

返回值:

        成功返回0

        错误返回-1

#include
#include
#include
#include
#include

void sighandle(int signum){
    printf("signum is %d\n",signum);
}
int main(){
    pid_t pid = fork();
    if(pid > 0){
        //signal(SIGQUIT,SIG_IGN); //SIG_IGN忽视信号
        //signal(SIGQUIT,SIG_DFL); //SIG_DFL默认的方式去处理
        signal(SIGQUIT,sighandle); //自定义处理
        wait(NULL);
        printf("in parent\n");
        exit(0);
    }else if(pid == 0){
        printf("in child\n");
        kill(getppid(),3);//SIGQUIT == 3 //getppid()获取进程的父进程pid号
        exit(0); //系统退出,退出进程
    }
    return 0; //函数的返回
}

运行结果:

多线程学习(C/C++)_第8张图片 

(4)消息队列——msgget、msgrcv、msgsnd

        在Linux中查看消息队列命令:ipcs -q 

#include

#include

#include

int msgget(key_t key, int msgflg);

功能:

        创建或打开消息队列 

参数:

         key:和消息队列关联的key值

        msgflg:

                0:取消息队列标识符,若不存在则函数会报错

                IPC_CREAT:当msgflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符

                IPC_CREAT|IPC_EXCL:如果内核中

你可能感兴趣的:(服务器,linux,c语言)