1、什么是进程
在操作系统原理使用这样的术语来描述的:正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进 程。我们使用vim编辑生成的C文件叫做源码,源码给程序员来看的但机器不识别,这时我们需要使用 编译器gcc编译生成CPU可识别的二进制可执行程序并保存在存储介质上,这时编译生成的可执行程序只能叫做程序而不能叫进 程。而一旦我们通过命令(./a.out)开始运行时,那正在运行的这个程序及其占用的资源就叫做进程了。很显然,一个程序可以执行多次,这也意味着多个进程可以执行同一个 程序。
2.fork()系统调用
Linux下有两个基本的系统调用可以用于创建子进程:fork()和vfork()。fork在英文中是"分叉"的意思。为什么取这个名字呢? 因为一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就”分叉”了,所以这个名字取得很形象。在我们编 程的过程中,一个函数调用只有一次返回(return),但由于fork()系统调用会创建一个新的进程,这时它会有两次返回。一次返回 是给父进程,其返回值是子进程的PID(Process ID),第二次返回是给子进程,其返回值为0。所以我们在调用fork()后,需要通 过其返回值来判断当前的代码是在父进程还是子进程运行,如果返回值是0说明现在是子进程在运行,如果返回值>0说明是父进 程在运行,而如果返回值<0的话,说明fork()系统调用出错。fork 函数调用失败的原因主要有两个:
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <errno.h>
5
6 int main(int argc, char **argv)
7 {
8 pid_t pid;
9
10
printf("Parent process PID[%d] start running...\n", getpid() );
11
12 pid = fork();
13 if(pid < 0)
14 {
15 printf("fork() create child process failure: %s\n", strerror(errno));
16 return -1;
17 }
18 else if( pid == 0 )
19 {
20 printf("Child process PID[%d] start running, my parent PID is [%d]\n", getpid(), getppid());
21 return 0;
22 }
23 else // if( pid > 0 )
24 {
25 printf("Parent process PID[%d] continue running, and child process PID is [%d]\n", getpid(), pid);
26 return 0;
27 }
28 }
运行结果:
Parent process PID[26765] start running...
Parent process PID[26765] continue running, and child process PID is [26766]
Child process PID[26766] start running, my parent PID is [1](此处由于父进程比子进程先退出,所以子进程变成孤儿进程,子进程将被init进程认领,所以它的父进程号变成init进程的进程号【1】)
fork()系统调用会创建一个新的子进程,这个子进程是父进程的一个副本。这也意味着,系统在创建新的子进程成功后,会将 父进程的文本段、数据段、堆栈都复制一份给子进程,但子进程有自己独立的空间,子进程对这些内存的修改并不会影响父进程 空间的相应内存。这时系统中出现两个基本完全相同的进程(父、子进程),这两个进程执行没有固定的先后顺序,哪个进程先执 行要看系统的进程调度策略。如果需要确保让父进程或子进程先执行,则需要程序员在代码中通过进程间通信的机制来自己实 现。
接下来我们再写一个例子深入了解一下创建子进程的过程
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int g_var = 6;
char g_buf[]="A string write to stdout.\n";
int main (int argc, char **argv)
{
int var = 88;
pid_t pid;
printf("Befor fork\n");
if( (pid=fork()) < 0)
{
printf("fork() error: %s\n", strerror(errno));
return -2;
}
else if( 0 == pid)
{
printf("Child process PID[%d] running...\n", getpid());
g_var ++;
var ++;
}
else
{
printf("Parent process PID[%d] waiting...\n", getpid());
sleep(1);
}
printf("PID=%ld, g_var=%d, var=%d\n", (long) getpid(), g_var, var);
return 0;
}
运行结果:
Befor fork
Parent process PID[27642] waiting...
Child process PID[27643] running...
PID=27643,
g_var=7,
var=89 PID=27642,
g_var=6, var=88
在上面的编译运行过程我们可以看到,父进程在代码第21行创建了子进程后,系统会将父进程的文本段、数据段、堆栈都拷贝 一份给子进程,这样子进程也就继承了父进程数据段中的的全局变量g_var和局部变量var的值。
子进程继承父进程哪些东西
由子进程自父进程继承到:
1、进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs))
2、环境(environment)变量
3、堆栈
4、内存
5、打开文件的描述符(注意对应的文件的位置由父子进程共享, 这会引起含糊情况) 执行时关闭(close-on-exec) 标志 (译者注:close-on-exec标志可通过fnctl()对文件描 述符设置,POSIX.1要求所有目录 流都必须在exec函数调用时关闭。更详细说明, 参见《APUE》 W. R. Stevens, 1993, 尤晋元等译(以下简称《高级编 程》), 3.13节和8.9节)
6、信号(signal)控制设定
7、nice值 (译者注:nice值由nice函数设定,该值表示进程的优先级, 数值越小,优先级越高) 进程调度类别(scheduler class) (译者注:进程调度类别指进程在系统中被调度时所属的类别,不同类别有不同优先级, 根据进程调度类别和nice值,进程调度程序可计算出每个进程的全局优先级(Global process prority),优先级高的进程优 先执行)
8、进程组号
9、对话期ID(Session ID) (译者注:译文取自《高级编程》,指:进程所属的对话期 (session)ID, 一个对话期包括一个或多 个进程组, 更详细说明参见《APUE》 9.5节) 当前
10、工作目录 根目录(根目录不一定是“/”,它可由chroot函数改变)
11、文件方式创建屏蔽字(file mode creation mask (umask))
12、资源限制
13、控制终端
子进程所独有:
1、进程号
2、不同的父进程号(译者注: 即子进程的父进程号与父进程的父进程号不同, 父进程号可由getppid函数得到)
3、自己的文件描述符和目录流的拷贝(译者注: 目录流由opendir函数创建,因其为顺序读取,顾称“目录流”)
4、子进程不继承父进程的进程,正文(text), 数据和其它锁定内存(memory locks) (译者注:锁定内存指被锁定的虚拟内存 页,锁定后, 不允许内核将其在必要时换出(page out), 详细说明参见《The GNU C Library Reference Manual》 2.2 版, 1999, 3.4.2节)
5、在tms结构中的系统时间(译者注:tms结构可由times函数获得, 它保存四个数据用于记录进程使用中央处理器 (CPU: Central Processing Unit)的时间,包括:用户时间,系统时间, 用户各子进程合计时间,系统各子进程合计时间)
6、资源使用(resource utilizations)设定为0
7、阻塞信号集初始化为空集(译者注:原文此处不明确, 译文根据fork函数手册页稍做修改)
8、不继承由timer_create函数创建的计时器 不继承异步输入和输出
9、父进程设置的锁(因为如果是排他锁,被继承的话就矛盾了)
多进程改写服务器程序:
#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 LingYun IoT Studio Client\n"
void print_usage(char *progname)
{
printf("%s usage: \n", progname);
printf("-p(--port): sepcify server listen port.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
int main(int argc,char **argv)
{
int servfd = 0;
int cliefd = 0;
int port =0;
int on = 1;
int rv = -1;
int ch = 0;
char buf[1024] = {
0};
socklen_t len = 0;
pid_t pid;
struct sockaddr_in servaddr;
struct sockaddr_in clieaddr;
struct option opts[] = {
{
"port", required_argument, NULL, 'p'},
{
"help", no_argument, NULL, 'h'},
{
NULL, 0, NULL, 0}
};
while( (ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )
{
switch(ch)
{
case 'p':
port=atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !port )
{
print_usage(argv[0]);
return 0;
}
servfd = socket(AF_INET,SOCK_STREAM,0);
if(servfd <0)
{
printf("create servfd failure:%s\n",strerror(errno));
return -1;
}
printf("create servfd[%d] successfully!\n",servfd);
setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(servfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
{
printf("bind port[%d] failure:%s\n",port,strerror(errno));
return -2;
}
listen(servfd,13);
printf("bind port[%d] successfully!\n",port);
while(1)
{
printf("Start accept new client incoming...\n");
cliefd = accept(servfd,(struct sockaddr *)&clieaddr,&len);
if(cliefd < 0)
{
printf("accept data[%d] failure:%s\n",cliefd,strerror(errno));
close(cliefd);
continue;
}
pid =fork();
if(pid <0)
{
printf("fork[%d] failure:%s\n",pid,strerror(errno));
close(cliefd);
continue;
}
else if(pid >0)
{
close(cliefd);
continue;
}
else if( pid == 0)
{
close(servfd);
printf("Child process start to commuicate with socket client...\n");
rv = read(cliefd,buf,sizeof(buf));
if(rv < 0)
{
printf("read cliefd[%d] failure:%s\n",cliefd,strerror(errno));
close(cliefd);
exit(0);
}
else if(rv == 0)
{
printf("cliefd disconnect!\n ");
close(cliefd);
exit(0);
}
printf("Read %d bytes data from Server: %s\n", rv, buf);
rv=write(cliefd, MSG_STR, strlen(MSG_STR));
if(rv < 0)
{
printf("Write to client by sockfd[%d] failure: %s\n", cliefd, strerror(errno));
close(cliefd);
exit(0);
}
printf("close client socket[%d] and child process exit\n", cliefd);
close(cliefd);
exit(0);
}
}
close(servfd);
return 0;
文章浏览阅读8.6k次。一、Linux记录用户登录信息文件1 /var/run/utmp----记录当前正在登录系统的用户信息;2 /var/log/wtmp----记录当前正在登录和历史登录系统的用户信息;3 /var/log/btmp:记录失败的登录尝试信息。二、命令用法1.命令last,lastb---show a listing of la_怎么记录linux设备 发声的登录和登出
文章浏览阅读167次。摘要:1. 简介 2. 公园迷宫漫步 3. 无线迷宫与最短(不加权)路径问题 4. 强连通分量1. 简介在计算机科学裡,树的遍历(也称为树的搜索)是圖的遍歷的一种,指的是按照某种规则,不重复地访问某种樹的所有节点的过程。具体的访问操作可能是检查节点的值、更新节点的值等。不同的遍历方式,其访问节点的顺序是不一样的。两种著名的基本遍历策略:深度优先搜索(DFS) 和 广度优先搜索(B...
文章浏览阅读591次。提起报表,大家会觉得即熟悉又陌生,好像常常在工作中使用,又似乎无法准确描述报表。今天我们来一起了解一下什么是报表,报表的结构、构成元素,以及为什么需要报表。什么是报表简单的说:报表就是通过表格、图表等形式来动态显示数据,并为使用者提供浏览、打印、导出和分析的功能,可以用公式表示为:报表 = 多样的布局 + 动态的数据 + 丰富的输出报表通常包含以下组成部分:报表首页:在报表的开..._activereports.net 实现查询报表功能
文章浏览阅读6.6k次。最近实验室需要用Cadence,这个软件的安装非常麻烦,每一次配置都要几个小时,因此打算把Cadence装进Docker。但是Cadence运行时需要GUI,要对Docker进行一些配置。我们实验室的服务器运行的是Ubuntu18.04,默认桌面GNOME,Cadence装进Centos的Docker。安装Ubuntu18.04服务器上安装Ubuntu18.04的教程非常多,在此不赘述了安装..._docker xrdp ubuntu
文章浏览阅读1.8k次,点赞2次,收藏2次。首先导入头文件#import 导入头文件后创建几个相机必须实现的对象 /** * AVCaptureSession对象来执行输入设备和输出设备之间的数据传递 */ @property (nonatomic, strong) AVCaptureSession* session; /** * 输入设备 */_ios avcapturestillimageoutput 兼容性 ios17 崩溃
文章浏览阅读982次。按照OracleDocument中的描述,v$sysstat存储自数据库实例运行那刻起就开始累计全实例(instance-wide)的资源使用情况。 类似于v$sesstat,该视图存储下列的统计信息:1>.事件发生次数的统计(如:user commits)2>._oracle v$sysstat视图
文章浏览阅读7.6k次,点赞2次,收藏9次。我最近做SPA项目开发动态树的时候一直遇到以下错误:当我点击文章管理需要跳转路径时一直报NavigationDuplicated {_name: “NavigationDuplicated”, name: “NavigationDuplicated”}这个错误但是当我点击文章管理后,路径跳转却是成功的<template> <div> 文章管理页面 <..._navigationduplicated {_name: 'navigationduplicated', name: 'navigationduplic
文章浏览阅读3.9k次。版本VoiceEngine 4.1.0舒适噪音生成(comfort noise generator,CNG)是一个在通话过程中出现短暂静音时用来为电话通信产生背景噪声的程序。#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)static const EcModes kDefaultEcMode = kEcAecm;#elsestati..._webrtc aecm 杂音
文章浏览阅读6.3k次,点赞9次,收藏19次。医学成像原理与图像处理一:概论引言:本系列博客为医学成像原理与图像处理重要笔记,由于是手写,在此通过扫描录入以图片的形式和电子版增补内容将其进行组织和共享。前半部分内容为图像处理基础内容,包括图像的灰度级处理、空间域滤波、频率域滤波、图像增强和分割等;后半部分内容为医学影象技术,包括常规胶片X光机、CR、DR、CT、DSA等X射线摄影技术、超声成像技术、磁共振成像(MRI)技术等。本篇主要内容是概论。_医学成像与图像处理技术知识点总结
文章浏览阅读591次,点赞13次,收藏10次。notepad++ v8.5.3 安装插件,下载进度为0_nodepa++
文章浏览阅读2.1w次。用spark执行SQL保存到Hive中: hiveContext.sql("insert overwrite table test select * from aaa")执行完成,没报错,但是核对结果的时候,发现有几笔数据超出指定范围(实际只包含100/200)最终排查到是ret_pay_remark 字段包含换行符,解决方案:执行SQL中把特殊字符替换掉regexp_replace(..._hive sql \n
文章浏览阅读520次,点赞10次,收藏8次。印象笔记05:如何打造更美的印象笔记超级笔记本文介绍印象笔记的具体使用,如何打造更美更实用的笔记。首先想要笔记更加好看和实用,我认为要使用超级笔记。所谓超级笔记就是具有很多便捷功能的笔记。_好的印象笔记怎么做的