Linux开发--进程

经典五问:

1.什么是程序?什么是进程?

从是否运行进行判断: 
gcc xxx -o pro,磁盘中生成的pro文件,就是程序
进程是程序一次运行活动

程序是静态的概念,进程是动态的概念。


2.如何查看系统中的进程:

在linux中

a.使用ps(-aux) 命令查看,使用 grep命令过滤
例如: ps -aux | grep init

b. top 指令,类似window的任务管理器

3.什么是进程标识符:


每一个进程 都有一个非负整数 表示唯一ID,叫 pid
pid=0,称为交换进程(swapper),作用--进程调度
pid=1,init进程,作用 -- 系统初始化

调用getpid() 函数获取自身的进程id

getppid() -- 获取父进程id


#include <sys/types.h>
#include <unistd.h>

int main()
{
 pid_t pid;
 pid = getpid();
 printf("pid == %d\n",pid);
 while(1);
    return 0;
}

4. 什么叫父进程?什么叫子进程?


if 进程A创建了进程B,那么A是B 的父进程,B是A的子进程。

5.C程序存储空间是如何分配?


高地址 ------------------------> 低地址

命令行参数和环境变量-----> 栈(函数里的形参  和 局部变量) -------> 堆(malloc等动态内存函数申请的内存空间) ------------>未初始化的数据(BSS段 int a;) -------->初始化的数据(数据段 int b=10;)----->正文(代码段)

int a = 0; //全局初始化区 
char *p1; //全局未初始化区 
void main() 
{
    int b; //栈 
    char s[] = “abc“;//栈 
    char *p2; //栈 
    char *p3 = “123456“; //123456\0在常量区,p3在栈上;体会与 char s[]="abc"; 的不同
    static int c =0; //全局初始化区 
    p2 = (char *)malloc(20); //堆区
    strcpy(p1, “123456“); //123456\0在常量区,编译器可能将它与p3指向的 “123456 “优化成一块
}

参考自:什么变量存放在栈和堆_什么样的数据进堆 什么样的数据进栈-CSDN博客


===================================================

fork函数


进程函数 fork 使用:


头文件:
       #include <sys/types.h>
       #include <unistd.h>
函数原型:
       pid_t fork(void);
返回值:

 调用成功,调用一次,返回两次:  
0--代表当前进程是子进程
非负数 -- 代表是父进程

调用失败,放回-1

--------------------------------------

fork_case:


case1 : 证明fork() 之后的语句,父子进程都会执行,fork之前的语句只有父进程执行

#include <sys/types.h>
#include <unistd.h>

int main()
{
 pid_t pid1;
 pid_t pid2;
 pid1 = getpid();
 printf("Before fork,pid=%d\n",pid1);
 fork(); //创建一个进程
 pid2 = getpid();
 printf("After fork,pid=%d\n",pid2);
 if(pid1 == getpid()){
 printf("This is father print. fatherPid=%d\n",pid1);
 }
 else {
 printf("This is child print. childPid=%d\n",pid2);
 }

    return 0;
}

--------------------------------


case2:验证: fork 返回值,fork调用一次,返回两次,>0 父进程, ==0子进程


#include <sys/types.h>
#include <unistd.h>

int main()
{
pid_t pid;
pid=getpid();
printf("father pid =%d\n",pid);
pid=fork();
if(pid>0){//父进程
printf("This is father print,pid=%d\n",getpid());

}
else if(pid == 0){ //子进程
printf("This is child print,pid=%d\n",getpid());

}

    return 0;
}

----------------------------


case3:  探索fork 父进程返回大于0的数有什么意义

等于子进程Pid号,子进程返回值就是0


//why给返回0的;理由:pid=0被交换进程所占用,不可能作为他的pid


#include <sys/types.h>
#include <unistd.h>

int main()
{
pid_t pid;
pid_t pid2;
pid_t retpid;
pid=getpid();
printf("father pid =%d\n",pid);
retpid=fork();
pid2=getpid();
if(pid==pid2){//父进程
printf("This is father print,retpid=%d,  pid=%d\n",retpid,pid2);

}
else { //子进程
printf("This is child print,retpid=%d,  pid=%d\n",retpid,pid2);

}

    return 0;
}


=============================================================


fork创建进程发生了什么?

 进程早期设计的时候把,全拷贝--内存-空间所有内容都进行了拷贝
后面写时拷贝(不变的内容放在共享空间,不拷贝)只对copy on write- COW- 子进程 修改的内存进行单独拷贝

执行 fork 以后: fork之后的代码会被直接拷贝下来,给父子进程调度使用,
父子进程的变量独立,子进程改变自己的变量,父进程不受影响(因为子进程实际拷贝了一份单独的内存空间,和父进程独立)


case:父子进程内存空间独立


#include <sys/types.h>
#include <unistd.h>

int main()
{
pid_t pid;
pid_t pid2;
int a=88;
pid=getpid();
printf("father pid =%d\n",pid);
fork();
pid2=getpid();
if(pid==pid2){//父进程
printf("This is father print,  pid=%d\n",pid2);

}
else { //子进程
printf("This is child print, pid=%d\n",pid2);
a+=12;
}
printf("a=%d\n",a);

    return 0;
}


============================


fork 创建子进程的目的:

1)父进程希望复制自己,使父子进程同时执行不同的代码段。 -- 常见于网络服务

2)一个进程要执行一个不同的程序。这在shell中常见,这种情况下,子进程从fork返回后,立即调用exec()


case:模拟网络服务(1)


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

int main()
{
    pid_t pid;
    pid_t pid2;
    int data = 0;
    while (1)
    {
        printf("please input a data\n");
        scanf("%d", &data);
        if (data == 1)
        {
            pid = fork(); // 每次近来创建一个子进程

            if (pid > 0)
            { // 父进程
            }
            else
            { // 子进程
                while (1)
                {
                    printf("net request,pid=%d\n", getpid());
                    sleep(10);
                }
            }
        }

        else
        {
            puts("waitting,do noting");
        }
    }

    return 0;
}


 

==========================================


vfork()函数

vfork 和fork的区别:


1.vfork是直接使用父进程的存储空间,不拷贝
2.vfork保证子进程先运行,当子进程调用exit()后,父进程才执行

case 区别验证:

#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
    pid_t pid;
    int cnt=0;
    pid = vfork();
    if (pid > 0)
    {
        while (1)
        {
            printf("cnt = %d\n",cnt);
            printf("this is father pid=%d\n", getpid());
            sleep(1);
        }
    }
    else
    {
        while (1)
        {
            printf("this is child pid=%d\n", getpid());
            sleep(1);
            cnt++;
            if(cnt==5){
            exit(0);
            }
        }
    }

    return 0;
}

=======================================


进程退出:

正常退出:


1.Main 函数return
2.进行调用exit(),标准C库
3.进程调用_exit() 或者_Exit(),属于系统调用

补充:(一个进程包含多个线程,当最后一个线程结束的时候进程就退出了)
1.进程最后一个线程放回
2.最后一个线程调用 pthread_exit


异常退出:


1.调用 abort
2.当进程接收到接收信号,如 ctrl+c
3.最后一个线程对取消(cancellation)请求做出响应

//无论进程如何退出最后都会执行内核的同一段代码,这段代码为所有相关进程关闭所有打开描述符,释放他的所有存储器

子进程调用exit _exit _Exit 的时候父进程可以调用waitpid 查看子进程退出的状态

推荐
exit() 是对_exit() _Exit()的封装,先处理缓冲区,再退出

=====================================


等待子进程退出:wait()


收集退出状态:

why?
创建子进程目的:
子进程退出状态if不被收集会变成僵尸进程


通过ps可以发现他的状态Z+ --僵尸进程,父进程--S+运行中


       #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *wstatus);

       pid_t waitpid(pid_t pid, int *wstatus, int options);

       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

status参数:
是一个整形数值指针
非空: 子进程退出状态 放在他所指向的地址中
空: 不关心退出状态

检测wait和waitpid所放回终止状态的宏:


WEXITSTATUS(status); -- 正常退出
WIFSIGNALED(status) --异常退出
WIFSTOPPED(status) -- 暂停子进程的返回状态
WIFCONTINUED(status) -- 暂停好继续 的子进程放回的状态

wait下的父进程:
- 如果所有子进程都还在进行,则阻塞
-一个子进程已经终止,正等待父进程获取其终止状态,则该子进程终止状态立刻返回
-如果父进程没有终止子进程,则出错返回

wait 和 waitpid 区别:


wait使调用者阻塞,waitpid有一个选项,可以使得调用者不阻塞

当option = WBOHANG 的时候  不阻塞

====================================


孤儿进程:

概念
父进程先于子进程退出,使得子进程变成孤儿进程

linux系统为了避免出现过多的孤儿进程,init进程来收留孤儿进程,init进程就是孤儿进程的父进程

验证程序:

#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<stdlib.h>
int main()
{
    pid_t pid;
    int cnt=0;
    int status=10;
    pid = fork();
    if (pid > 0)
    {

            printf("this is father pid=%d\n", getpid());
      
    }
    else if(pid==0)
    {
        while (1)
        {
            printf("this is child pid=%d\t my father ppid=%d\n", getpid(),getppid());
            sleep(1);
            cnt++;
            if(cnt==3){
            exit(3);
            }
        }
    }

    return 0;
}

=====================================================


exec族函数:


但一个进程跑到一半的时候,调用exec族函数去执行另一个程序.

exec函数族:
execl
,execlp, execv,execvp   ,execle ,execvpe(e结尾不常用)

返回值:
exec成功不会放回,失败设置error并返回 -1,然后从原程序调用点往下执行

参数说明:
path:可执行文件路径
arg:可执行程序所带参数,第一个参数是程序名,没有带路径且arg必须以NULL结束
file: 如果参数中包含/,则视为路径,否则就按PATH环境变量,在他所在目录中搜寻可执行文件


perror -- 打印出错误信息;
perror(why);  why: 错误信息

execl例子:


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

int main()
{
printf("before execl\n");
if(execl("./echo","echo","abc",NULL)==-1){
puts("execl error!!!");
perror("why");
}
puts("after execl");
    return 0;
}


#include<stdio.h>

int main(int argc,char **argv)
{
int i;
for(i=0;i<argc;++i){
printf("argv[%d]=%s\n",i,argv[i]);

}
    return 0;
}

gcc echoarg.c -o echo


-------------------------------------------

execl("/bin/ls","ls","-l",NULL)  -- 第一个参数直接写绝对路径调用系统的命令

execl("/bin/date","date",NULL)==-1) -- 获取系统时间


execlp -- p 通过系统环境变量找到指令,不用写绝对路径了
such as:  execlp("ps","ps",NULL,NULL)==-1)

v -- 使用指针char * [](字符串数组-二维数组)代替参数


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

int main()
{
printf("before execl\n");
char *argv[]={"ps",NULL,NULL};
if(execvp("ps",argv)==-1){
puts("execl error!!!");

}
puts("after execl");
    return 0;
}



exec 配合 fork使用:


case1: 实现功能,当父进程检测到输入为1 的时候,创建子进程吧配置文件的字段修改掉

=============
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    pid_t pid;
    pid_t pid2;
    int data = 0;
    while (1)
    {
        printf("please input a data\n");
        scanf("%d", &data);
        if (data == 1)
        {
            pid = fork(); // 每次近来创建一个子进程

            if (pid == 0)
            { // 子进程
                int fdSrc;

                char *readBuf = NULL;
                fdSrc = open("./config.txt", O_RDWR);
                int size = lseek(fdSrc, 0, SEEK_END);
                lseek(fdSrc, 0, SEEK_SET);

                readBuf = (char *)malloc(sizeof(char) * (size + 8));
                int n_read = read(fdSrc, readBuf, size);
                char *p = strstr(readBuf, "LENG=");
                if (p == NULL)
                {
                    puts("not found");
                    exit(-1);
                }
                p = p + strlen("LENG=");
                *p = '5';
                lseek(fdSrc,0,SEEK_SET);

                write(fdSrc,readBuf,strlen(readBuf));
                close(fdSrc);

            }
        }

        else
        {
            puts("waitting,do noting");
        }
    }

    return 0;
}


----------------------
 

execl进行优化:

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

int main()
{
    pid_t pid;
    pid_t pid2;
    int data = 0;
    while (1)
    {
        printf("please input a data\n");
        scanf("%d", &data);
        if (data == 1)
        {
            pid = fork(); // 每次近来创建一个子进程
            if(pid>0){
                wait(NULL);//防止变成】僵尸进程
            }
            else if (pid == 0)
            { // 子进程
               execl("./changedata","changedata",NULL);
              
            }
        }

        else
        {
            puts("waitting,do noting");
        }
    }

    return 0;
}


=================================


system进行优化:


       #include <stdlib.h>

       int system(const char *command);


system -- 封装后的exec

调用/bin/sh失败返回127 其他失败返回-1

与exec 的区别,执行完后还会回去执行原程序的代码
=============================
一句system调用就可以执行之前的整个可执行文件

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

int main()
{
    system("./exf");// 前面生成的可执行文件直接调用即可
    return 0;
}


=================================================

popen


      #include <stdio.h>

       FILE *popen(const char *command, const char *type);

       int pclose(FILE *stream);


可以获取内存的输出结果:

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

int main()
{
    char ret[1024]={0};
    FILE *fp;
    fp=popen("ps","r");
      //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    int nread = fread(ret,1,1024,fp);
    printf("nread=%d\nret = %s\n",nread,ret);
    return 0;
}
 


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/535620.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode-热题100:64. 最小路径和

题目描述 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 **说明&#xff1a;**每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a; grid [[1,3,1],[1,5,1],[4,2,1]]…

09 - 镜像管理之:部署单点harbor

本次准备了3台机器&#xff1a;harbor-01、harbor-02、harbor-db&#xff0c;用于测试 单点模式、高可用模式 部署 harbor。 ip主机名规格操作系统说明192.168.217.136harbor-012c4gCentos7.9harbor 服务器&#xff0c;测试单点harbor192.168.217.135harbor-022c4gCentos7.9ha…

初始C++之缺省参数 函数重载 引用

初始C之缺省参数 函数重载 引用& 文章目录 初始C之缺省参数 函数重载 引用&一、缺省参数1.1 缺省参数的定义1.2 缺省参数的分类1.3 注意事项 二、 函数重载2.1 函数重载的定义2.2 参数个数不同2.3 参数类型不同2.4 类型顺序不同2.5 为什么C语言不支持函数重载 三、引用…

Python中的错误处理 - 使用try、except、else和finally进行解释,并附带代码示例

最近&#xff0c;我的经理委派我创建一个自动报告。我设计的报告非常简单。它包括一些来自数据库的数字和一些基本的数学运算。我很兴奋最终可以向公司展示我的惊人的Python技能。 我完成并交付了产品。一切都很顺利。至少&#xff0c;直到大约两周后。我的报告由于除以零错误…

编程新手必看,python中条件控制语句学习(13)

介绍&#xff1a; Python3中的条件控制主要通过if、elif和else关键字来实现&#xff0c;它们用于根据条件执行特定的代码块。 if语句&#xff1a;这是最基本的条件控制结构。如果满足某个条件&#xff08;条件为True&#xff09;&#xff0c;则执行相应的代码块。在Python中&am…

蓝牙app设计(方案二) E4A (时钟 优缺点)

例程改的! 主界面 虽然上面有搜索功能,但是本人建议先自行配对在使用,这样更好用,把要使用的设备收藏一下更好找哦(这样就是橙色的了,只需要点对应蓝牙左边) 代码修改部分 原版是不停向下滚动显示,这样个人觉得不太好看,所以加了个时钟,到对应时钟周期清空(达到刷…

Java-Web过滤器

文章目录 1.基本介绍1.为什么需要过滤器&#xff1f;2.基本介绍3.过滤器的基本原理 2.快速入门1.文件目录2.环境配置创建maven项目&#xff0c;导入依赖 3.代码实现1.login.jsp2.LoginCheck.java3.ManagerFilter.java编写过滤规则4.配置web.xml告诉tomcat5.admin.jsp 3.Filter的…

【nodejs基础学习三-浏览器偏好设置】

系列文章目录 第一章 nodejs基础学习–注释、变量、运算符、字符串、函数&#xff08;一&#xff09; 第二章 nodejs基础学习–循环、对象字符、模块导入出&#xff08;二&#xff09; 第三章 nodejs基础学习三-浏览器设置 系列文章目录一、开发者模式二、web偏好设置 一、开发…

【病毒分析】DevicData勒索病毒分析

1.背景 1.1来源 近期&#xff0c;Solar团队收到某医疗单位的援助请求&#xff0c;该公司的计算机受到了某勒索病毒的侵害&#xff0c;所有的文件被加密并且添加了.DevicData-P-470b1abd后缀&#xff0c;我司人员现场取证进行排查并提取加密器,本文是对于加密器的分析。 2.恶…

MySQL高级详解

文章目录 约束概述分类主键约束概述特点定义及删除主键自增 唯一约束作用语法 非空约束作用语法 面试题&#xff1a;非空唯一约束与主键约束有什么区别默认值约束作用语法 总结 表关系及外键约束表关系概述分类一对多关系表设计外键字段设计原则 多对多关系表设定设计原则 一对…

Linux下网络编程基础知识--协议

网络基础 这一个课程的笔记 相关文章 协议 Socket编程 高并发服务器实现 线程池 协议 一组规则, 数据传输和数据的解释的规则。 比如说依次发送文件的文件名, 文件的大小, 以及实际的文件, 这样规定发送一个文件的顺序以及发送的每一个部分的格式等可以算是一种协议 型协议 …

探索ChatGPT-Plus:AI 助手全套开源解决方案

探索ChatGPT-Plus&#xff1a;AI 助手全套开源解决方案 ChatGPT-plus是一种新型的对话生成模型&#xff0c;它是在OpenAI的ChatGPT基础上进行了改进和优化的版本。ChatGPT-plus的出现引起了广泛关注&#xff0c;因为它在对话生成方面展现出了更加出色的表现和能力。在本文中&am…

【C++第三阶段】stackqueue容器

以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 stack容器queue容器 stack容器 是什么&#xff1f;功能是什么&#xff1f;常用接口是什么&#xff1f;局限性有哪些&#xff1f;优势又有哪些&#xff1f; 栈容器&#xff0c;先进…

智能驾驶“血拼”端到端,元戎启行准备好了吗?

智能驾驶从规则驱动转向数据驱动&#xff0c;正在引导行业进入新的竞争区间。 在之前的中国电动汽车百人会论坛(2024) 上&#xff0c;比亚迪董事长兼总裁王传福认为&#xff0c;新能源汽车渗透率在未来3个月将超过50%。自动驾驶公司元戎启行CEO周光指出&#xff0c;在上半场的…

Python实现BOA蝴蝶优化算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝴蝶优化算法(butterfly optimization algorithm, BOA)是Arora 等人于2019年提出的一种元启发式智能算…

社交网络的未来图景:探索Facebook的发展趋势

随着科技的不断进步和社会的快速变迁&#xff0c;社交网络作为连接人与人之间的重要纽带&#xff0c;扮演着日益重要的角色。而在众多社交网络中&#xff0c;Facebook作为老牌巨头&#xff0c;一直在探索着新的发展路径&#xff0c;引领着社交网络的未来图景。本文将深入探索Fa…

跟着Carl大佬学leetcode之27 移除元素

来点强调&#xff0c;刷题是按照代码随想录的顺序进行的&#xff0c;链接如下https://www.programmercarl.com/本系列是记录一些刷题心得和学习过程&#xff0c;就看到题目自己先上手试试&#xff0c;然后看程序员Carl大佬的解释&#xff0c;自己再敲一遍修修补补&#xff0c;练…

数组与伪数组的区别

大家都知道&#xff0c;在js中使用 document.querySelectorAll(选择器&#xff09;获取到的为该选择器能选择到的所有元素组成的伪数组&#xff0c;所谓伪数组&#xff0c;就是外表和数组一样&#xff0c;能够使用索引遍历&#xff0c;但本质是对象。 数组与伪数组之间的区别&…

C语言面试题之合法二叉搜索树

合法二叉搜索树 实例要求 实现一个函数&#xff0c;检查一棵二叉树是否为二叉搜索树&#xff1b; 示例 1: 输入:2/ \1 3 输出: true 示例 2: 输入:5/ \1 4/ \3 6 输出: false 解释: 输入为: [5,1,4,null,null,3,6]。根节点的值为 5 &#xff0c;但是其右子节点值为 4 …

测试开发是“懂测试的开发”还是“懂开发的测试”?

这是个很有意思的话题&#xff0c;我一开始画了这么一张图&#xff1a; 就我自身的工作而言&#xff0c;用着开发的技术&#xff0c;做着开发差不多的工作。归为开发一类并无不妥&#xff01; 后来&#xff0c;我细细琢磨了一下&#xff0c;改为了下图。 其实答案也非常明显&a…