系统编程之进程篇

进程

一、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

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 华为移动服务(Huawei Mobile Services,简称 HMS)是一个全面开放的移动服务生态系统,为企业和开发者提供了丰富的工具和 API,助力他们构建、运营和推广应用。其中,HMS Scankit 是华为推出的一款扫描服务 SDK,支持快速集成到安卓应用中,能够提供高效且稳定的二维码和条形码扫描功能,适用于商品扫码、支付验证、信息获取等多种场景。 集成 HMS Scankit SDK 主要包括以下步骤:首先,在项目的 build.gradle 文件中添加 HMS Core 库和 Scankit 依赖;其次,在 AndroidManifest.xml 文件中添加相机访问和互联网访问权限;然后,在应用程序的 onCreate 方法中调用 HmsClient 进行初始化;接着,可以选择自定义扫描界面或使用 Scankit 提供的默认扫描界面;最后,实现 ScanCallback 接口以处理扫描成功和失败的回调。 HMS Scankit 内部集成了开源的 Zxing(Zebra Crossing)库,这是一个功能强大的条码和二维码处理库,提供了解码、生成、解析等多种功能,既可以单独使用,也可以与其他扫描框架结合使用。在 HMS Scankit 中,Zxing 经过优化,以更好地适应华为设备,从而提升扫描性能。 通常,ScanKitDemoGuide 包含了集成 HMS Scankit 的示例代码,涵盖扫描界面的布局、扫描操作的启动和停止以及扫描结果的处理等内容。开发者可以参考这些代码,快速掌握在自己的应用中实现扫码功能的方法。例如,启动扫描的方法如下: 处理扫描结果的回调如下: HMS Scankit 支持所有安卓手机,但在华为设备上能够提供最佳性能和体验,因为它针对华为硬件进行了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值