系统编程之进程篇

进程

一、fork系统调用

q 包含头文件 <sys/types.h> <unistd.h>

q 函数功能:创建一个子进程

q 函数原型

         pid_t fork(void);

q 参数:无参数。

q 返回值:

q 如果成功创建一个子进程,对于父进程来说返回子进程ID

q 如果成功创建一个子进程,对于子进程来说返回值为0

q 如果为-1表示创建失败

fork 系统调用注意点

fork系统调用之后,父子进程将交替执行。

q 如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)

q 如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程。

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

intmain(int argc, char *argv[])

{

         signal(SIGCHLD, SIG_IGN);

         printf("before fork pid =%d\n", getpid());

         pid_t pid;

         pid = fork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

         {

                   printf("this is parentpid=%d childpid=%d\n", getpid(), pid);

                   sleep(100);

         }

         else if (pid == 0)

         {

                   printf("this is childpid=%d parentpid=%d\n", getpid(), getppid());

         }

         return 0;

}

二、写时复制copy on write

q 如果多个进程要读取它们自己的那部分资源的副本,那么复制是不必要的。

q 每个进程只要保存一个指向这个资源的指针就可以了。

q 如果一个进程要修改自己的那份资源的“副本”,那么就会复制那份资源。这就是写时复制的含义

三、fork之后父子进程共享文件

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

intmain(int argc, char *argv[])

{

         signal(SIGCHLD, SIG_IGN);

         printf("before fork pid =%d\n", getpid());

         int fd;

         fd = open("test.txt",O_WRONLY);

         if (fd == -1)

                   ERR_EXIT("openerror");

 

        

         pid_t pid;

         pid = fork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

         {

                   printf("this is parentpid=%d childpid=%d\n", getpid(), pid);

                   write(fd, "parent",6);

                   sleep(3);

         }

         else if (pid == 0)

         {

                   printf("this is childpid=%d parentpid=%d\n", getpid(), getppid());

                   write(fd, "child",5);

         }

         return 0;

}

四、forkvfork

q 在fork还没实现copy on write之前。Unix设计者很关心fork之后立刻执行exec所造成的地址空间浪费,所以引入了vfork系统调用。

vfork有个限制,子进程必须立刻执行_exit或者exec函数。

q 即使fork实现了copy on write,效率也没有vfork高,但是我们不推荐使用vfork,因为几乎每一个vfork的实现,都或多或少存在一定的问题。

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

int gval = 100;

 

intmain(int argc, char *argv[])

{

         signal(SIGCHLD, SIG_IGN);

         printf("before fork pid = %d\n",getpid());

        

         pid_t pid;

         pid = fork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

         {

                   sleep(1);

                   printf("this is parentpid=%d childpid=%d gval=%d\n", getpid(), pid, gval);

                   sleep(3);

         }

         else if (pid == 0)

         {

                   gval++;

                   printf("this is childpid=%d parentpid=%d gval=%d\n", getpid(), getppid(), gval);

         }

         return 0;

}

因为fork创建的进程为copy on write,故其不共享gval变量

 

 

 

 

 

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

int gval= 100;

 

intmain(int argc, char *argv[])

{

         signal(SIGCHLD, SIG_IGN);

         printf("before fork pid =%d\n", getpid());

        

         pid_t pid;

         pid= vfork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

         {

                   sleep(1);

                   printf("this is parentpid=%d childpid=%d gval=%d\n", getpid(), pid, gval);

                   sleep(3);

         }

         else if (pid == 0)

         {

                   gval++;

                   printf("this is childpid=%d parentpid=%d gval=%d\n", getpid(), getppid(), gval);

                   exit(0);

         }

         return 0;

}

 

五、atexit

atexit可以注册终止处理程序,ANSI C规定最多可以注册32个终止处理程序。

q 终止处理程序的调用与注册次序相反

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

 

voidmy_exit1(void)

{

         printf("my exit1 ...\n");

}

 

voidmy_exit2(void)

{

         printf("my exit2 ...\n");

}

 

intmain(int argc, char *argv[])

{

         atexit(my_exit1);

         atexit(my_exit2);

         //_exit(0);  不能输出

         exit(0);

}

六、fork深入理解

代码段+数据段+堆栈段+PCB

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

intmain(int argc, char *argv[])

{

         signal(SIGCHLD, SIG_IGN);

         printf("before fork pid =%d\n", getpid());

        

         pid_t pid;

         pid = fork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

         {

                   printf("this is parentpid=%d childpid=%d\n", getpid(), pid);

                   sleep(3);

         }

         else if (pid == 0)

         {

                   printf("this is childpid=%d parentpid=%d\n", getpid(), getppid());

         }

         return 0;

}

理解printf("before fork pid = %d\n", getpid());为什么没有输出两次

 

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

intmain(int argc, char *argv[])

{

         fork();

         fork();

         fork();

         printf("ok\n");

         return 0;

}

 

七、替换进程映像

intexecve(const char *filename, char *const argv[], char *const envp[])

 

#include<unistd.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

int gval= 100;

 

intmain(int argc, char *argv[])

{

         signal(SIGCHLD, SIG_IGN);

         printf("before fork pid =%d\n", getpid());

        

         pid_t pid;

         pid = vfork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

         {

                   printf("this is parentpid=%d childpid=%d gval=%d\n", getpid(), pid, gval);

         }

         else if (pid == 0)

         {

                   char *const args[] ={"ps", NULL};

 

                   execve("/bin/ps",args, NULL);

                   gval++;

                   printf("this is childpid=%d parentpid=%d gval=%d\n", getpid(), getppid(), gval);

         }

         return 0;

}

exec关联函数

q 包含头文件<unistd.h>

q 功能用exec函数可以把当前进程替换为一个新进程。exec名下是由多个关联函数组成的一个完整系列,头文件<unistd.h>

q 原型

     int execl(const char *path, const char*arg, ...);

     int execlp(const char *file, const char*arg, ...);

     int execle(const char *path, const char*arg,

                  ..., char * const envp[]);

     int execv(const char *path, char *constargv[]);

     int execvp(const char *file, char *constargv[]);

q 参数

path参数表示你要启动程序的名称包括路径名

arg参数表示启动程序所带的参数

q 返回值:成功返回0,失败返回-1

execlexeclpexecle(都带“l”)的参数个数是可变的,参数以一个空指针结束。

execvexecvp的第二个参数是一个字符串数组,新程序在启动时会把在argv数组中给定的参数传递到main

q 这些函数通常都是用execve实现的,这是一种约定俗成的做法,并不是非这样不可。

q 名字最后一个字母是“p”的函数会搜索PATH环境变量去查找新程序的可执行文件。如果可执行文件不在PATH定义的路径上,就必须把包括子目录在内的绝对文件名做为一个参数传递给这些函数

man exec查看相关用法

SIGCHLD

q 当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)

q 子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。

q 父进程查询子进程的退出状态可以用wait/waitpid函数

wait

q 头文件<sys/types.h><sys/wait.h>

q 函数功能:当我们用fork启动一个进程时,子进程就有了自己的生命,并将独立地运行。有时,我们需要知道某个子进程是否已经结束了,我们可以通过wait安排父进程在子进程结束之后。

q 函数原型

pid_twait(int *status)

q 函数参数

status:该参数可以获得你等待子进程的信息

q 返回值:

q 成功等待子进程函数返回等待子进程的ID

wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。

q 返回的是子进程的PID,它通常是结束的子进程

q 状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。

q 如果status不是一个空指针,状态信息将被写入它指向的位置

宏定义

描述

WIFEXITED(status)

如果子进程正常结束,返回一个非零值

WEXITSTATUS(status)

如果WIFEXITED非零,返回子进程退出码

WIFSIGNALED(status)

子进程因为捕获信号而终止,返回非零值

WTERMSIG(status)

如果WIFSIGNALED非零,返回信号代码

WIFSTOPPED(status)

如果子进程被暂停,返回一个非零值

WSTOPSIG(status)

如果WIFSTOPPED非零,返回一个信号代码

waitpid

q函数原型:

          pid_twaitpid(pid_t pid, int *status,int options)

 q参数:

status:如果不是空,会把状态信息写到它指向的位置

qoptions:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

q 返回值:如果成功返回等待子进程的ID,失败返回-1

对于waitpidp i d参数的解释与其值有关:

pid == -1等待任一子进程。于是在这一功能方面waitpidwait等效。

pid >0 等待其进程I Dp i d相等的子进程。

pid == 0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。

pid <-1 等待其组I D等于p i d的绝对值的任一子进程。

waitwaitpid的区别

q 在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。

waitpid并不等待第一个终止的子进程它有若干个选择项,可以控制它所等待的特定进程。

q 实际上wait函数是waitpid函数的一个特例。

僵进程

q 当一个子进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行或者父进程调用了wait才告终止。

q 进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。它将称为一个“僵进程”。

 

如何避免僵进程

q 调用wait或者waitpid函数查询子进程退出状态,此方法父进程会被挂起。

q 如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

#include<unistd.h>

#include<sys/stat.h>

#include<sys/wait.h>

#include<sys/types.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

intmain(int argc, char *argv[])

{

         pid_t pid;

         pid = fork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid == 0)

         {

                   sleep(3);

                   printf("this ischild\n");

                   //exit(100);

                   abort();

         }

 

         int ret;

         printf("this is parent\n");

         int status;

         //ret = wait(&status);

         //ret = waitpid(-1, &status, 0);

         ret = waitpid(pid, &status, 0);

         printf("ret = %d pid = %d\n",ret, pid);

         if (WIFEXITED(status))

                   printf("child exitednormal exit status=%d\n", WEXITSTATUS(status));

         /*else

                   printf("child exited abnormal\n");

         */

         else if (WIFSIGNALED(status))

                   printf("child exitedabnormal signal number=%d\n", WTERMSIG(status));

         else if (WIFSTOPPED(status))

                   printf("child stopedsignal number=%d\n", WSTOPSIG(status));

         return 0;

}

system

q 功能:system()函数调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕

q 原型:

    int system(const char *command);

q 返回值:

    如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。

system函数执行时,会调用forkexecvewaitpid等函数。

#include<unistd.h>

#include<sys/stat.h>

#include<sys/wait.h>

#include<sys/types.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

intmy_system(const char *command);

 

intmain(int argc, char *argv[])

{

         //system("ls -l | wc -w");

         my_system("ls -l | wc -w");

         return 0;

}

 

intmy_system(const char *command)

{

         pid_t pid;

         int status;

         if (command == NULL)

                   return 1;

 

         if ((pid = fork()) < 0)

                   status = -1;

         else if (pid == 0)

         {

                   execl("/bin/sh","sh", "-c", command, NULL);

                   exit(127);

         }

         else

         {

                   while (waitpid(pid,&status, 0) < 0)

                   {

                            if (errno == EINTR)

                                     continue;

                           

                            status = -1;

                            break;

                   }

         }

 

         return status;

}

一、什么是守护进程

q 守护进程是在后台运行不受控端控制的进程,通常情况下守护进程在系统启动时自动运行

q 守护进程的名称通常以d结尾,比如sshdxinetdcrond

创建守护进程的步骤

q 调用fork(),创建新进程,它会是将来的守护进程

q 在父进程中调用exit,保证子进程不是进程组组长

q 调用setsid创建新的会话期

q 将当前目录改为根目录

q 将标准输入、标准输出、标准错误重定向到/dev/null

#include<unistd.h>

#include<sys/stat.h>

#include<sys/wait.h>

#include<sys/types.h>

#include<fcntl.h>

 

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

 

 

#defineERR_EXIT(m) \

         do \

         { \

                   perror(m); \

                   exit(EXIT_FAILURE); \

         } while(0)

 

int setup_daemon(intnochdir, int noclose);

 

intmain(int argc, char *argv[])

{

         setup_daemon(1, 1);

         //daemon(1, 1);

         printf("test ...\n");

         for (;;) ;

         return 0;

}

 

intsetup_daemon(int nochdir, int noclose)

{

         pid_t pid;

         pid = fork();

         if (pid == -1)

                   ERR_EXIT("forkerror");

 

         if (pid > 0)

                   exit(EXIT_SUCCESS);

 

         setsid();

         if (nochdir == 0)

                   chdir("/");

         if (noclose == 0)

         {

                   int i;

                   for (i=0; i<3; ++i)

                            close(i);

                   open("/dev/null",O_RDWR);

                   dup(0);

                   dup(0);

         }

         return 0;

        

}

 

intdaemon(int nochdir, int noclose);

q 功能:创建一个守护进程

q 参数:

nochdir=0将当前目录更改至“/

noclose=0将标准输入、标准输出、标准错误重定向至“/dev/null

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 智慧社区背景与挑战 随着城市化的快速发展,社区面临健康、安全、邻里关系和服务质量等多方面的挑战。华为技术有限公司提出智慧社区解决方案,旨在通过先进的数字化技术应对这些问题,提升城市社区的生活质量。 2. 技术推动智慧社区发展 技术进步,特别是数字化、无线化、移动化和物联化,为城市社区的智慧化提供了可能。这些技术的应用不仅提高了社区的运行效率,也增强了居民的便利性和安全性。 3. 智慧社区的核心价值 智慧社区承载了智慧城市的核心价值,通过全面信息化处理,实现对城市各个方面的数字网络化管理、服务与决策功能,从而提升社会服务效率,整合社会服务资源。 4. 多层次、全方位的智慧社区服务 智慧社区通过构建和谐、温情、平安和健康四大社区模块,满足社区居民的多层次需求。这些服务模块包括社区医疗、安全监控、情感沟通和健康监测等。 5. 智慧社区技术框架 智慧社区技术框架强调统一平台的建设,设立数据中心,构建基础网络,并通过分层建设,实现平台能力及应用的可持续成长和扩展。 6. 感知统一平台与服务方案 感知统一平台是智慧社区的关键组成部分,通过统一的RFID身份识别和信息管理,实现社区服务的智能化和便捷化。同时,提供社区内外监控、紧急救助服务和便民服务等。 7. 健康社区的构建 健康社区模块专注于为居民提供健康管理服务,通过整合医疗资源和居民接入,实现远程医疗、慢性病管理和紧急救助等功能,推动医疗模式从治疗向预防转变。 8. 平安社区的安全保障 平安社区通过闭路电视监控、防盗报警和紧急求助等技术,保障社区居民的人身和财产安全,实现社区环境的实时监控和智能分析。 9. 温情社区的情感沟通 温情社区着重于建立社区居民间的情感联系,通过组织社区活动、一键呼叫服务和互帮互助平台,增强邻里间的交流和互助。 10. 和谐社区的资源整合 和谐社区作为社会资源的整合协调者,通过统一接入和身份识别,实现社区信息和服务的便捷获取,提升居民生活质量,促进社区和谐。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值