C/C++ Linux进程操作

news2024/11/18 7:24:29

目录

一、简介

二、创建进程

1. fork

2. wait

3. exit

三、多进程高并发设计

四、孤儿进程

五、僵尸进程

六、守护进程

七、总结


一、简介

进程是什么?

答:可以简单理解为,一个 .exe 的应用程序,就是运行在进程中的!

当然,一个应用程序,可以由多个进程共同运行。

操作系统可以运行多个程序,那他是如何运行的?实际上,CPU的执行是很快的,而待运行的程序很多,那么为了让操作系统运行多个程序,CPU会把它的执行时间划分成很多段,比如每一段是0.1秒,那么就可以这样A程序运行0.1秒,然后B程序运行0.1,然后C程序运行0.2秒,因为这个切换很快,所以我们感觉程序是同时运行的。

 操作系统上看上面提到的运行程序就是指一个进程,因为存在切换,所以进程管理了很多资源(如打开的文件、挂起的信号、进程状态、内存地址空间等等),也就是说进程参与了CPU的调度,和管理了所有资源,哦,这句话,不是很正确,实际上现代CPU的执行非常非常快,而且操作系统有多个CPU,使用一个进程参与调度时,频繁地从CPU的寄存器和进程堆栈的保存运行状态和对应的信息都很耗时,所以现代CPU将进程仅仅作为一个资源管理的东东,而引入了线程作为CPU调度的基本单位,多个线程可以共享同一进程的所有资源(后面会讲线程)。

注意,进程跟进程间一般不共享数据,当然也会有一些特殊情况;线程是在进程内部的,所以线程跟线程之间可以共享数据! 


二、创建进程

1. fork

#include <unistd.h>

pid_t fork(void);

描述:创建一个进程

返回值:

        成功:父进程返回子进程id,子进程返回0;

        失败:父进程返回-1,并设置errno错误标志。

示例:

pid_t fpid = fork();

2. wait

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

pid_t wait(int *status);

描述:父进程等待子进程退出

status:

        获取子进程使用exit函数退出时指定给父进程返回的数据;如果子进程exit(10),返回的是正数,则父进程的status接收到的的是10;如果子进程exit(-10),返回的是补码,即返回246。

返回值:

        成功:返回终止子进程的id;

        失败:返回-1.

示例:

int status;
wait(&status);

3. exit

#include <stdlib.h>

void exit(int status);

描述:退出进程

status:

        返回给父进程的参数

示例:

exit(-10); // 正数正常返回,负数返回的是补码

例:

#include <unistd.h>     // fork
#include <stdlib.h>     // exit
#include <sys/types.h>
#include <sys/wait.h>   // wait

#include <stdio.h>

int main(int argc, char **argv) {
    // 创建进程id
    pid_t fpid;
    
    int count = 0;
    int status = 0;
    
    // 创建进程
    fpid = fork();
    
    if (fpid < 0) {
        printf("error in fork!\n");
    
    } else if (0 == fpid) {
        printf("child process, My process id is %d\n", getpid());
        count += 10;
        
        // 销毁进程
        //exit(-10); // 正数正常返回,负数返回的是补码
    
    } else {
        printf("parent process, My process id si %d\n", getpid());
        count++;
    }
    
    printf("统计结果是:%d\n", count);
    
    //wait(&status);
    printf("parent - status:%d\n", WEXITSTATUS(status));
    
    return 0;
}

 

关于子进程和父进程是否共享变量内存的问题:

创建出了子进程,按照以前旧的版本,子进程创建成功后,会立马拷贝父进程的所有变量等内存到子进程中,这样效率很低;

然后现在的版本,子进程创建成功后,还不会立马拷贝,还是和父进程共享,但当父进程如果修改了某个变量的值时,在修改之时,子进程就会拷贝一份到自己那里,这样效率就高很多了!


三、多进程高并发设计

 由一个Master管理进程和多个相同的work工作进程组成;

在终端输入命令:cat /proc/cpuinfo 查看自己cpu的核数;

例如,processor       : 1 ;我这里最后一个显示的是1,那么就是两核的;

所以在下面的代码中,可以创建两个工作进程去来处理工作了!

#define _GNU_SOURCE

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>


typedef void (*spawn_proc_pt) (void *data);
static void worker_process_cycle(void *data);
static void start_worker_processes(int n);
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name);

int main(int argc, char **argv) {
    start_worker_processes(2);
    
    // 管理子进程
    
    wait(NULL);
}


void start_worker_processes(int n) {
    int i = 0;
    
    for (i = n - 1; i >= 0; i--) {
        spawn_process(worker_process_cycle,(void *)(intptr_t) i, (char*)"worker process");
    }
    
}
    
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name) {
    pid_t pid;
    pid = fork();
    
    switch(pid) {
        case -1:
            fprintf(stderr, "fork() failed while spawning \"%s\"\n", name);
            return -1;
        case 0:
            proc(data);
            return 0;
        default:
            break;
    }
    
    printf("start %s %ld\n", name, (long int)pid);
    return pid;
}



static void worker_process_init(int worker) {
    cpu_set_t cpu_affinity;
    
    // 多核高并发处理
    CPU_ZERO(&cpu_affinity);    // 清零
    CPU_SET(worker%CPU_SETSIZE, &cpu_affinity);
    
    if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_affinity) == -1) {
        fprintf(stderr, "sched_setaffinity() failed\n");
    }
}


void worker_process_cycle(void *data) {
    int worker = (intptr_t)data;
    
    // 初始化
    worker_process_init(worker);
    
    // 进程干的活
    for (;;) {
        sleep(10);
        printf("pid %ld, doing...\n", (long int)getpid());
    }
}

程序运行后, 使用命令: ps -ef | grep 可执行文件名     即可查看自己创建的进程了

其中,4658和4659是程序创建的两个子进程id,4657是父进程id 

ctrl + c 可以退出程序,或者 killall -9  可执行文件名


四、孤儿进程

父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。

init进程是所有进程的祖宗,它的进程id是1;

如何模仿实现孤儿进程呢?

运行以下代码,然后使用命令: kill -9 父进程,即可实现孤儿进程!

#define _GNU_SOURCE

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>


typedef void (*spawn_proc_pt) (void *data);
static void worker_process_cycle(void *data);
static void start_worker_processes(int n);
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name);

int main(int argc, char **argv) {
    start_worker_processes(2);
    
    // 管理子进程
    
    // 程序一直卡在这里,一直没法执行下面的wait代码
    while(1) { sleep(1); }
    
    wait(NULL);
}


void start_worker_processes(int n) {
    int i = 0;
    
    for (i = n - 1; i >= 0; i--) {
        spawn_process(worker_process_cycle,(void *)(intptr_t) i, (char*)"worker process");
    }
    
}
    
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name) {
    pid_t pid;
    pid = fork();
    
    switch(pid) {
        case -1:
            fprintf(stderr, "fork() failed while spawning \"%s\"\n", name);
            return -1;
        case 0:
            proc(data);
            return 0;
        default:
            break;
    }
    
    printf("start %s %ld\n", name, (long int)pid);
    return pid;
}



static void worker_process_init(int worker) {
    cpu_set_t cpu_affinity;
    
    // 多核高并发处理
    CPU_ZERO(&cpu_affinity);    // 清零
    CPU_SET(worker%CPU_SETSIZE, &cpu_affinity);
    
    if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_affinity) == -1) {
        fprintf(stderr, "sched_setaffinity() failed\n");
    }
}


void worker_process_cycle(void *data) {
    int worker = (intptr_t)data;
    
    // 初始化
    worker_process_init(worker);
    
    // 干活
    for (;;) {
        sleep(10);
        printf("pid %ld, doing...\n", (long int)getpid());
    }
}

 当父进程被杀死后,两个子进程的父进程id变为了1,也就是init进程接管了两个子进程!这就是孤儿进程的展示了!


五、僵尸进程

        一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

僵尸进程怎样产生的:

   一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用 exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。

子进程变为僵尸进程后,都是在等待父进程为他收尸,如果父进程没有给他收尸,那么它就一直都是僵尸进程。

所以,为了不让进程成为僵尸进程,务必在父进程里加上wait即可!

当然,也可以把父进程杀掉,那么init进程就会接管那些子进程,也一样会收尸。

或者接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。

使用下方代码实现僵尸进程:

#define _GNU_SOURCE

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>


typedef void (*spawn_proc_pt) (void *data);
static void worker_process_cycle(void *data);
static void start_worker_processes(int n);
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name);

int main(int argc, char **argv) {
    start_worker_processes(2);
    
    // 管理子进程
    
    // 程序一直卡在这里,一直没法执行下面的wait代码
    while(1) { sleep(1); }
    
    wait(NULL);
}


void start_worker_processes(int n) {
    int i = 0;
    
    for (i = n - 1; i >= 0; i--) {
        spawn_process(worker_process_cycle,(void *)(intptr_t) i, (char *)"worker process");
    }
    
}
    
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name) {
    pid_t pid;
    pid = fork();
    
    switch(pid) {
        case -1:
            fprintf(stderr, "fork() failed while spawning \"%s\"\n", name);
            return -1;
        case 0:
            proc(data);
            return 0;
        default:
            break;
    }
    
    printf("start %s %ld\n", name, (long int)pid);
    return pid;
}



static void worker_process_init(int worker) {
    cpu_set_t cpu_affinity;
    
    // 多核高并发处理
    CPU_ZERO(&cpu_affinity);    // 清零
    CPU_SET(worker%CPU_SETSIZE, &cpu_affinity);
    
    if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_affinity) == -1) {
        fprintf(stderr, "sched_setaffinity() failed\n");
    }
}


void worker_process_cycle(void *data) {
    int worker = (intptr_t)data;
    
    // 初始化
    worker_process_init(worker);
    
    // 干活
    //for (;;) {
        //sleep(10);
        printf("pid %ld, doing...\n", (long int)getpid());
    //}
    
    // 子进程退出,变为僵尸进程,等待父进程“收尸”
    exit(1);
}

查看僵尸进程:

ps -ef | grep 可执行文件名

使用命令 :kill -9 父进程id   ,即可结束僵尸进程。(init接管了子进程,为他们收尸)


六、守护进程

不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务。守护进程脱离于终端,是为了避免进程在执行过程中的信息在任何终端上显示,并且进程也不会被任何终端所产生的终端信息所打断(比如关闭终端等)。

有空的可以试一下,使用上面的代码创建子进程后,然后关闭终端,运行程序的进程也会随之关闭。

那如何成为一个守护进程呢? 步骤如下:

  1. 1. 调用fork(),创建新进程,它会是将来的守护进程.
  2. 2. 在父进程中调用exit,保证子进程不是进程组长
  3. 3. 调用setsid()创建新的会话区
  4. 4. 将当前目录改成目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载他作为守护进程的工作目录)
  5. 5. 将标准输入,标输出,标准错误重定向到/dev/null.

守护进程代码:

int daemon(int nochdir, int noclose) {
    int fd;
    
    switch (fork()) {
        case -1:
            return -1;
        case 0:
            break;
        default:
            _exit(0);
    }
    
    if (setsid() == -1) {
        return -1;
    }
    
    if (!nochdir) {
        (void)chdir("/");
    }
    
    if (!noclose && (fd == open("/dev/null", O_RDWR, 0)) != -1) {
        (void)dup2(fd, STDIN_FILENO);
        (void)dup2(fd, STDOUT_FILENO);
        (void)dup2(fd, STDERR_FILENO);
        
        if (fd > 2) {
            (void)close(fd);
        }
        
        return 0;
    }
}

在main函数的第一行直接调用这个函数,参数传两个0即可创建守护进程!

int main(int argc, char **argv) {
    
    // 创建守护进程
    daemon(0, 0);
    
    start_worker_processes(2);
    
    // 管理子进程
    
    wait(NULL);
}

七、总结

进程的一些用法就是这些了,现在我也还不知道进程有哪些作用,日后学习了在写一篇博客记录下来!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT时代,我们可能站到了自然语言编程的大门口

ChatGPT大火&#xff0c;我现在有种感觉&#xff1a;我们可能站到了自然语言编程的门口&#xff0c;一脚下去&#xff0c;也许能把门踹开。 当然&#xff0c;也可能会踢到一块铁板。 回顾我们的编程之路&#xff0c;基本上就是一个编程门槛不断降低的历史。 最早的一批前辈们…

OSCP-Fail(rsync、fail2ban提权)

目录 扫描 rsync 提权 扫描 rsync 基于nmap,确信将进一步研究rsync。 为此,将使用netcat使用的rsync枚举。 使用netcat,我们可以列出rsync托管的当前共享。 我们看到“fox”和“fox home

万字长文带我弄懂JS基础!!!

js的学习笔记 文章目录 js的学习笔记JS基础篇js 的输出js的基本语法js语句js的注释js的变量js数据类型简介js对象简介js函数简介js的作用域js中的事件js字符串js运算符js条件语句js循环for/in循环 js的break和continuejs之typedefnullundefinedjs类型转换**constructor属性**St…

人类思维VS AI智能:谁是未来的胜者?

在“人工智能&#xff08;AI&#xff09;是否会取代人类”的问题上&#xff0c;谷歌的首席执行官埃德拉里博斯&#xff08;Ed Larrabee&#xff09;说&#xff1a;“我不认为 AI会取代人类。”而英国首相鲍里斯约翰逊则认为&#xff1a;“我们不能让 AI成为我们的敌人。”现在&…

MySQL解压版安装步骤

百度网盘有安装版、解压包安装包以及visual插件 链接&#xff1a;https://pan.baidu.com/s/1XXvWa40FYX5mtqofW_knIg 提取码&#xff1a;ky2q 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 解压压缩包&#xff0c;进入bin目录&#xff0c;地址栏输…

从C出发 26 --- 指针 : 一种特殊的变量

指针是变量&#xff0c; 是特殊的变量 在计算机内部逻辑上是一个一个存储单元&#xff0c;每个存储单元是一个字节 8 G /16 G 表示的是存储单元的数量 如果要确定某一个具体的存储单元&#xff0c;要怎么办&#xff1f; 可以编号&#xff0c;这里的 0 1 2 3 指的就是内存地…

如何利用TURF分析来对餐厅菜品进行组合搭配?

1.数据源说明 1.1 数据简单说明 本数据源采用的是某餐厅8月份的销售明细表。本文会主要用到一下字段值&#xff1a; order_id&#xff0c; 产品订单号dishes_name&#xff0c;菜品名称counts, 消费数量amounts&#xff0c;消费金额 1.2 数据截图 以下是数据源的截图 1.3…

【移动端网页布局】移动端网页布局基础概念 ⑤ ( 视网膜屏技术 | 二倍图概念 | 代码示例 )

文章目录 一、视网膜屏技术二、二倍图概念三、代码示例 一、视网膜屏技术 PC 端 和 早期的 移动端 网页中 , CSS 中配置的 1 像素 对应的就是物理屏幕中的 1 像素 ; Retina 视网膜屏幕 技术出现后 , 将多个物理像素压缩到一块屏幕中 , 可以达到更高的分辨率 , 画面显示效果更好…

【Linux】磁盘与文件系统

目录 一、磁盘的物理结构 二、磁盘逻辑抽象 三、文件系统 1、Super Block 2、Group Descriptor Table 3、inode Table 4、Data Blocks 5、inode Bitmap 6、Block Bitmap 四、Linux下文件系统 1、inode与文件名 2、文件的增删查改 2.1、查看文件内容 2.2、删除文件…

Node工程的依赖包管理方式 | 京东云技术团队

作者&#xff1a;京东零售 陈震 在前端工程化中&#xff0c;JavaScript 依赖包管理是非常重要的一环。依赖包通常是项目所依赖的第三方库、工具和框架等资源&#xff0c;它们能够帮助我们减少重复开发、提高效率并且确保项目可以正确的运行。 目前比较常见的前端包管理器有 n…

uniapp打包app,调用相机功能时在真机调试可以,打包成app之后不行

在打包成app之后报如图所示错误&#xff1a; 解决&#xff1a; 在app模块配置勾选上相机这一项&#xff0c;如图&#xff1a; 这是主要针对上面这个报错的&#xff0c;当然还有一下情况比较类似&#xff0c;就是相机功能在真机调试下可以&#xff0c;打包之后就不行了。我总结…

第九章_Redis哨兵(sentinel)

是什么 吹哨人巡查监控后台master主机是否故障&#xff0c;如果故障了根据投票数自动将某一个从库转换为新主库&#xff0c;继续对外服务 作用 哨兵的作用&#xff1a; 1、监控redis运行状态&#xff0c;包括master和slave 2、当master down机&#xff0c;能自动将slave切换成…

面试京东失败,再看看两年前的面试题,根本不是一个难度

刚从京东走出来&#xff0c;被二面难到了&#xff0c;我记得学长两年前去面试的时候&#xff0c;问的问题都特别简单&#xff0c;咋现在难度高了这么多。面试前我也刷过很多的题和看过很多资料&#xff0c;后来想想&#xff0c;这年头网上软件测试资料泛滥&#xff0c;软件测试…

Linux使用crontab编写定时任务

Linux使用crontab编写定时任务 安装 yum install -y crontabcrontab服务启动与关闭 service crond start #启动服务 service crond stop #关闭服务 service crond restart #重启服务 service crond reload #重新载入配置 service crond status #查看状态chkconfig crond on …

【广州华锐互动】火电厂3D沉浸式事故体验,提高员工安全意识和应急处理能力

火电厂是一种重要的能源生产方式&#xff0c;但是在运营过程中也存在着一些风险和隐患。为了降低火电厂事故的发生率&#xff0c;提高员工的安全意识和应急处理能力&#xff0c;火电厂3D沉浸式事故体验系统成为了一个重要的工具&#xff0c;为企业安全管理起到了不可替代的作用…

python 第一章——简介与环境搭建

文章目录 前言一、什么是编程语言二、下载python解释器三、安装pycharm 前言 本系列教程目录可点击这里查看&#xff1a;python教程目录 python在当今世界的流行度应该已经不用我多说了&#xff0c;这可以从TIOBE的榜单中可窥一二 作为一门面向编程新人的语言&#xff0c;它…

【无标题】基于K-means聚类的多智能体跟随多领导者算法

源自&#xff1a;系统仿真学报 作者&#xff1a;袁国栋 何明 马子玉 张伟士 刘学达, 李伟 摘 要 为防止多智能体集群跟随多个领导者时编队混乱&#xff0c;提出了3种K-means聚类算法&#xff0c;将集群分成与领导者数量相同的社区&#xff0c;社区内的智能体将跟随同一领导…

解决PDF转Word问题:三种免费转换方法大比拼

在职场办公中&#xff0c;我们经常会遇到将PDF文件转换为可编辑的Word文件的问题。虽然网上有很多PDF转Word工具&#xff0c;但许多工具要么需要付费&#xff0c;要么转换后的文件无法直接编辑。所以&#xff0c;你可能会想知道是否有免费且效果好的PDF转Word工具。在本文中&am…

基于Java+SpringBoot+vue+element疫情药品采购出入库系统设计实现

基于JavaSpringBootvueelement疫情药品采购出入库系统设计实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联…

使用opencv进行场景识别

opencv场景识别 文章目录 opencv场景识别一、需求1、现状2、设想 二、模型使用1、opencv dnn支持的功能2、ANN_MLP相关知识3、图像分类模型训练学习4、目标检测模型5、opencv调用darknet物体识别模型 三、模型训练1、现状2、步骤-模型编译3、步骤-模型训练 一、需求 1、现状 …