1. 进程

1.1 概念

  • 程序

    • 程序是存放在存储介质上的一个可执行文件
  • 进程
    • 进程是程序执行的过程,是程序在执行过程中分配和管理资源的基本单位
    • 程序是静态的,进程是动态的。进程的状态是变化的,其包括进程的创建、调度和消亡
  • 线程
    • 线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源
    • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程
  • 进程ID
    • 进程ID 是一个16位的正整数,默认取值范围是从 232768可以修改
    • PID数字为1的值一般是为特殊进程 init 保留
  • 父进程
    • 任何进程(除init进程)都是由另一个进程启动,该进程称为被启动进程的父进程(ID号称为:PID),被启动的进程称为子进程(ID号称为:PPID),
    • 父进程号无法在用户层修改

1.2 查看进程

  • 查看进程命令

    • ps -aux

      • 查看系统进程
    • pstree
      • 将进程以树状关系列出来

1.3 启动新进程

  • 介绍三种方法启动新进程

    1. system() 函数
    2. fork() 函数
    3. exec() 函数

1.3.1 system() 函数

  • 可以理解为 启动新进程
  • system()启动了一个运行着/bin/sh的子进程
    • 说明 system() 函数依赖与 shell
  • int system (const char *string )
    • 效果就相当于执行 sh –c string
  • system() 函数的特点
    • 建立独立进程,拥有独立的代码空间,内存空间
    • 等待新的进程执行完毕,system才返回。(阻塞)
  • 例程
    • system 运行完才会返回,才会在当前终端打印出数据
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
printf("This is a system demo!\n\n");
/*调用 system()函数*/
result = system("ls -l");
printf("Done!\n\n");
return result;
}

1.3.2 fork() 函数

  • 可以理解为 复制进程
  • 头文件
    1. #include<unistd.h>
    2. #include<sys/types.h>
  • pid_t fork( void);
    • 若成功调用一次则

      • 子进程返回 0
      • 父进程返回子进程 ID
    • 出错返回 -1
  • 例程
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
printf("This is a fork demo!\n\n");
/*调用 fork()函数*/
result = fork();
/*通过 result 的值来判断 fork()函数的返回情况,首先进行出错处理*/
if(result == -1) {
printf("Fork error\n");
}
/*返回值为 0 代表子进程*/
else if (result == 0) {
printf("The returned value is %d, In child process!! My PID is %d\n\n", result, getpid());
}
/*返回值大于 0 代表父进程*/
else {
printf("The returned value is %d, In father process!! My PID is %d\n\n", result, getpid());
}
return result;}

1.3.2 exce 系列函数

  • 可以理解为 替换进程
  • 调用 exec 并不创建新进程,所以前后的进程 ID 并未改变
  • exec 只是用另一个新程序替换了当前进程的正文、数据、堆和栈段
  • 在原进程中已经打开的文件描述符,在新进程中仍将保持打开,除非它们的“执行时关闭标志”(close on exec flag)被置位
  • 任何在原进程中已打开的目录流都将在新进程中被关闭
  • 举个例子,A进程调用 exce 系列函数启动一个进程B,此时进程B会替换进程A,进程A的内存空间、数据段、代码段等内容都将被进程B占用,进程A将不复存在
1.3.2.1 exce 系列函数说明
  • exec 系列函数有 6 个不同的 exec 函数

    1. int execl(const char *path, const char *arg, ...)
    2. int execlp(const char *file, const char *arg, ...)
    3. int execle(const char *path, const char *arg, ..., char *const envp[])
    4. int execv(const char *path, char *const argv[])
    5. int execvp(const char *file, char *const argv[])
    6. int execve(const char *path, char *const argv[], char *const envp[])
  • 函数说明
    • 名称包含 l 字母的函数(execl、 execlp 和execle)接收参数列表”list”作为调用程序的参数
    • 名称包含 p 字母的函数(execvp 和execlp)接受一个程序名作为参数,然后在当前的执行路径中搜索并执行这个程序
    • 名字不包含 p 字母的函数在调用时必须指定程序的完整路径,其实就是在系统环境变量”PATH”搜索可执行文件
    • 名称包含 v 字母的函数(execv、execvp 和 execve)的命令参数通过一个数组”vector”传入
    • 名称包含 e 字母的函数(execve 和 execle)比其它函数多接收一个指明环境变量列表的参数,并且可以通过参数envp传递字符串数组作为新程序的环境变量,这个envp参数的格式应为一个以 NULL 指针作为结束标记的字符串数组,每个字符串应该表示为”environment =virables”的形式

1.3 终止进程

  • 可以分为 5 种进程终止

    • 正常终止

      • main 函数返回
      • 调用 exit() 终止
      • 调用 _exit() 函数终止
    • 异常终止
      • 调用 abort() 函数终止
      • 由系统信号终止

1.4 等待进程

  • 父进程中调用wait()或者waitpid()函数让父进程等待子进程的结束

1.4.1 wait() 函数

  • wait()函数只是 waitpid() 函数的一个特例,在 Linux内部实现 wait 函数时直接调用的就是 waitpid 函数
  • pid_t wait(int *wstatus);
    • wait() 函数在被调用的时候,系统将暂停父进程的执行,直到有信号来到或子进程结束
    • 如果在调用 wait() 函数时子进程已经结束,则会立即返回子进程结束状态值
    • 子进程的结束状态信息会由参数wstatus返回
    • 该函数的返回值为子进程的PID
  • 注意
    • wait()要与fork()配套出现,且 fork() 调用先
    • 参数wstatus用来保存被收集进程退出时的一些状态
  • 可以使用以下宏来判断退出状态
    • WIFEXITED(status) :如果子进程正常结束,返回一个非零值
    • WEXITSTATUS(status): 如果WIFEXITED非零,返回子进程退出码
    • WIFSIGNALED(status) :子进程因为捕获信号而终止,返回非零值
    • WTERMSIG(status) :如果WIFSIGNALED非零,返回信号代码
    • WIFSTOPPED(status): 如果子进程被暂停,返回一个非零值
    • WSTOPSIG(status): 如果WIFSTOPPED非零,返回一个信号代码

1.4.2 waitpid() 函数

  • wait()函数只是 waitpid() 函数的一个特例,在 Linux内部实现 wait 函数时直接调用的就是 waitpid 函数
  • pid_t waitpid(pid_t pid, int *wstatus, int options);
    • pid:参数pid为要等待的子进程ID

      • pid < -1:等待进程组号为pid绝对值的任何子进程
      • pid = -1:等待任何子进程,此时的waitpid()函数就等同于wait()函数
      • pid = 0:等待进程组号与目前进程相同的任何子进程,即等待任何与调用waitpid()函数的进程在同一个进程组的进程
      • pid > 0:等待指定进程号为pid的子进程
    • wstatus:与wait()函数一样
    • options:参数 options 提供了一些另外的选项来控制waitpid()函数的行为。如果不想使用这些选项,则可以把这个参数设为0

2. 管道

2.1 概念

  • 管道

    • 管道是 Linux 由 Unix 那里继承过来的进程间的通信机制,它是Unix早期的一个重要通信机制。
    • 其思想是,在内存中创建一个共享文件,从而使通信双方利用这个共享文件来传递信息。由于这种方式具有单向传递数据的特点,所以这个作为传递消息的共享文件就叫做“管道”
  • 管道分类
    • 匿名管道(无名管道)(PIPE
    • 命名管道(有名管道)(FIFO

2.2 匿名管道

2.2.1 匿名管道特征

  1. 没有名字,因此不能使用 open() 函数打开,但可以使用 close() 函数关闭
  2. 只提供单向通信
  3. 只能用于具有血缘关系的进程间通信,通常用于父子进程建通信
  4. 管道是基于字节流来通信的
  5. 依赖于文件系统,它的生命周期随进程的结束而结束
  6. 写入操作不具有原子性,因此只能用于一对一的简单通信情形
  7. 管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()和write()等函数。但是它又不是普通的文件,并不属于其他任何文件系统,并且只存在于内核的内存空间中,因此不能使用lseek()来定位

2.2.2 pipe() 函数

  • pipe() 函数用于创建一个匿名管道,一个可用于进程间通信的单向数据通道。
  • 头文件
    • #include <unistd.h>
  • 函数原型
    • int pipe(int pipefd[2]);

      • pipefd[0] 指向管道的 读取
      • pipefd[1] 指向管道的
      • 返回 0:匿名管道创建成功
      • 返回 -1:创建失败
  • 使用步骤
    1. 父进程调用 pipe() 函数创建匿名管道
    2. 父进程调用 fork() 函数启动(创建)一个子进程
    3. 若想从父进程将数据传递给子进程
      1. 父进程:关闭读取端
      2. 子进程:关闭写端
    4. 若想从子进程将数据传递给父进程
      1. 父进程:关闭写端
      2. 子进程:关闭读取端
    5. 当不需要使用管道时,关闭所有端口即可
  • 例程
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define MAX_DATA_LEN 256
#define DELAY_TIME 1 int main()
{
pid_t pid;
int pipe_fd[2]; //(1)
char buf[MAX_DATA_LEN];
const char data[] = "Pipe Test Program";
int real_read, real_write; memset((void*)buf, 0, sizeof(buf)); /* 创建管道 */
if (pipe(pipe_fd) < 0) //(2)
{
printf("pipe create error\n");
exit(1);
} /* 创建一子进程 */
if ((pid = fork()) == 0)
{
/* 子进程关闭写描述符,并通过使子进程暂停 3s 等待父进程已关闭相应的读描述符 */
close(pipe_fd[1]);
sleep(DELAY_TIME * 3);
/* 子进程读取管道内容 */
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
}
/* 关闭子进程读描述符 */
close(pipe_fd[0]);
exit(0);
}
else if (pid > 0)
{
/* 父进程关闭读描述符,并通过使父进程暂停 1s 等待子进程已关闭相应的写描述符 */
close(pipe_fd[0]);
sleep(DELAY_TIME);
if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Parent write %d bytes : '%s'\n", real_write, data);
}
/*关闭父进程写描述符*/
close(pipe_fd[1]);
/*收集子进程退出信息*/
waitpid(pid, NULL, 0);
exit(0);
}
}

2.3 命名管道

2.3.1 命名管道特征

  1. 有名字,存储于普通文件系统之中
  2. 任何具有相应权限的进程都可以使用 open() 来获取命名管道的文件描述符
  3. 跟普通文件一样:使用统一的 read()/write() 来读写
  4. 跟普通文件不同:不能使用 lseek() 来定位,原因是数据存储于内存中
  5. 具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏
  6. 遵循先进先出(First In First Out)原则,最先被写入 FIFO 的数据,最先被读出来

2.3.2 创建命名管道命令

  • mkfifo

    • mkfifo test

      • test 文件为命名管道文件

2.3.3 fifo() 函数

  • fifo() 函数
  • 头文件
    • #include <unistd.h>
  • 函数原型
    • int mkfifo(const char * pathname,mode_t mode);

      • pathname:命名管道文件
      • mode:
        • O_RDONLY:读管道
        • O_WRONLY:写管道
        • O_RDWR:读写管道
        • O_NONBLOCK:非阻塞
        • O_CREAT:如果该文件不存在,那么就创建一个新的文件,并用第三个参数为其设置权限
        • O_EXCL:如果使用 O_CREAT 时文件存在,那么可返回错误消息
      • 返回值:
        • 0:成功
        • EACCESS:参数 filename 所指定的目录路径无可执行的权限
        • EEXIST:参数 filename 所指定的文件已存在
        • ENAMETOOLONG:参数 filename 的路径名称太长
        • ENOENT:参数 filename 包含的目录不存在
        • ENOSPC:文件系统的剩余空间不足
        • ENOTDIR:参数 filename 路径中的目录存在但却非真正的目录
        • EROFS:参数 filename 指定的文件存在于只读文件系统内
  • 例程
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h> #define MYFIFO "myfifo" /* 命名管道文件名*/ #define MAX_BUFFER_SIZE PIPE_BUF /* 4096 定义在于 limits.h 中*/ void fifo_read(void)
{
char buff[MAX_BUFFER_SIZE];
int fd;
int nread; printf("***************** read fifo ************************\n");
/* 判断命名管道是否已存在,若尚未创建,则以相应的权限创建*/
if (access(MYFIFO, F_OK) == -1)
{
if ((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST))
{
printf("Cannot create fifo file\n");
exit(1);
}
}
/* 以只读阻塞方式打开命名管道 */
fd = open(MYFIFO, O_RDONLY);
if (fd == -1)
{
printf("Open fifo file error\n");
exit(1);
}
memset(buff, 0, sizeof(buff));
if ((nread = read(fd, buff, MAX_BUFFER_SIZE)) > 0)
{
printf("Read '%s' from FIFO\n", buff);
}
printf("***************** close fifo ************************\n");
close(fd);
exit(0);
} void fifo_write(void)
{
int fd;
char buff[] = "this is a fifo test demo";
int nwrite;
sleep(2); //等待子进程先运行
/* 以只写阻塞方式打开 FIFO 管道 */
fd = open(MYFIFO, O_WRONLY | O_CREAT, 0644);
if (fd == -1)
{
printf("Open fifo file error\n");
exit(1);
}
printf("Write '%s' to FIFO\n", buff);
/*向管道中写入字符串*/
nwrite = write(fd, buff, MAX_BUFFER_SIZE);
if(wait(NULL)) //等待子进程退出
{
close(fd);
exit(0);
}
} int main()
{
pid_t result;
/*调用 fork()函数*/
result = fork();
/*通过 result 的值来判断 fork() 函数的返回情况,首先进行出错处理*/
if(result == -1)
{
printf("Fork error\n");
}
else if (result == 0) /*返回值为 0 代表子进程*/
{
fifo_read();
}
else /*返回值大于 0 代表父进程*/
{
fifo_write();
}
return result;
}

3. 信号

3.1 概念及特征

  • 信号(signal)

    • 又称为软中断信号,用于通知进程发生了异步事件
    • 它是Linux系统响应某些条件而产生的一个事件
    • 它是在软件层次上对中断机制的一种模拟
    • 是一种异步通信方式
    • 在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的
  • 信号是进程间通信机制中唯一的异步通信机制
  • 信号产生
    • 信号可能是由于系统中某些错误而产生
    • 也可以是某个进程主动生成的一个信号

3.2 系统支持的信号

  • 查询系统支持的信号种类命令:kill -l
  • linux支持62种信号(没有 32 号和 33 号信号
    • 非实时信号(不可靠):1-32

      • 没有排队功能,信号可能被丢弃
      • 不会立即执行
      • 先放入该进程控制块(PCB),待合适的时候处理
    • 实时信号(可靠信号):34-64
      • 有排队功能

3.3 信号处理

  • 信号类似可分为三大类型:程序错误、外部事件以及显式请求
  • 当信号发生时,信号可以采取如下三种操作:
    • 忽略信号(SIGTOP 和 SIGKILL 是绝不能被忽略的)
    • 捕获信号
    • 让默认信号起作用
      • 终止进程并且生成内存转储文件
      • 终止终止进程但不生成core文件
      • 忽略信号
      • 暂停进程
      • 若进程是暂时暂停,恢复进程,否则将忽略信号

3.4 发送信号函数

  • kill()
  • raise()
  • alarm()

3.4.1 kill()

  • 命令:kill [信号或选项] PID(s)
  • 函数
    • 头文件:
      #include <sys/types.h>
      #include <signal.h>
    • 函数原型:int kill(pid_t pid, int sig);
      • pid 取值如下

        • pid > 1:将信号sig发送到进程ID值为pid指定的进程
        • pid = 0:信号被发送到所有和当前进程在同一个进程组的进程
        • pid = -1:将sig发送到系统中所有的进程,但进程1(init)除外
        • pid < -1:将信号sig发送给进程组号为-pid (pid绝对值)的每一个进程
      • sig 为 信号值
      • 返回值
        • 0:发送成功
        • -1:发送失败

3.4.2 raise()

  • raise() 函数为进程向自身发送信号
  • 函数
    • 头文件
      #include <signal.h>
    • 函数原型:int raise(int sig);
      • sig 为 信号值
      • 返回值
        • 0:发送成功
        • -1:发送失败

3.4.3 alarm()

  • alarm() 称为闹钟函数,设置时间为 seconds 秒,时间到后,它就向进程发送SIGALARM信号。在时间未到时便重新调用 alarm() 函数,会更新到时值。
  • 函数
    • 头文件
      #include <unistd.h>
    • 函数原型:unsigned int alarm(unsigned int seconds);

3.5 捕获信号函数

  • signal()、sigaction()等函数

3.5.1 signal()

  • signal()主要是用于捕获信号,可以改变进程中对信号的默认行为
  • 函数
    • 头文件
      #include <signal.h>
    • 函数原型
      typedef void (*sighandler_t)(int);
      sighandler_t signal(int signum, sighandler_t handler);
    • signum 是指定捕获的信号,如果指定的是一个无效的信号,或者尝试处理的信号是不可捕获或不可忽略的信号(如SIGKILL),errno将被设置为EINVAL
    • handler 是一个函数指针,它的类型是 void(*sighandler_t)(int) 类型
    • handler 也可以是一个宏定义
      • SIG_IGN:忽略该信号
      • SIG_DFL:采用系统默认方式处理信号

3.5.2 sigaction() *

  • 不推荐读者使用signal(),而推荐使用 sigaction();
  • 函数
    • 头文件
      #include <signal.h>
    • 函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
      • signum:指定捕获的信号值

      • act:是一个结构体

        • sa_handler 是一个函数指针,是捕获信号后的处理函数
        • sa_sigaction 是扩展信号处理函数,它也是一个函数指针,不仅可以接收到int 型的信号值,还会接收到一个 siginfo_t 类 型的结构体指针,还有一个void类型的指针,还有需要注意的就是,不要同时使用 sa_handlersa_sigaction,因为这两个处理函数是有联合的部分(联合体)
        • sa_mask 是信号掩码,它指定了在执行信号处理函数期间阻塞的信号的掩码,被设置在该掩码中的信号,在进程响应信号期间被临时阻塞。除非使用 SA_NODEFER 标志,否则即使是当前正在处理的响应的信号再次到来的时候也会被阻塞
        • re_restorer 则是一个已经废弃的成员变量,不要使用
        • oldact 返回原有的信号处理参数,一般设置为NULL即可
        • sa_flags 是指定一系列用于修改信号处理过程行为的标志
          • SA_NOCLDSTOP 使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。即当它们接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU(停止)中的一种时或接收到SIGCONT(恢复)时,父进程不会收到通知
          • SA_NOCLDWAIT 从Linux 2.6开始就存在这个标志了,它表示父进程在它的子进程终止时不会收到 SIGCHLD 信号,这时子进程终止则不会成为僵尸进程。
          • SA_NODEFER 一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
          • SA_RESETHAND 信号处理之后重新设置为默认的处理方式。
          • SA_SIGINFO 从Linux 2.2开始就存在这个标志了,使用 sa_sigaction成员而不是使用sa_handler 成员作为信号处理函数。
        struct sigaction {
        void (*sa_handler)(int);
        void (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t sa_mask;
        int sa_flags;
        void (*sa_restorer)(void);
        };
        • siginfo_t
        siginfo_t {
        int si_signo; /* 信号数值 */
        int si_errno; /* 错误值 */
        int si_code; /* 信号代码 */
        int si_trapno; /*导致硬件生成信号的陷阱号,在大多数体系结构中未使用*/
        pid_t si_pid; /* 发送信号的进程ID */
        uid_t si_uid; /*发送信号的真实用户ID */
        int si_status; /* 退出值或信号状态*/
        clock_t si_utime; /*消耗的用户时间*/
        clock_t si_stime; /*消耗的系统时间*/
        sigval_t si_value; /*信号值*/
        int si_int; /* POSIX.1b 信号*/
        void *si_ptr;
        int si_overrun; /*计时器溢出计数*/
        int si_timerid; /* 计时器ID */
        void *si_addr; /*导致故障的内存位置 */
        long si_band;
        int si_fd; /* 文件描述符*/
        short si_addr_lsb; /*地址的最低有效位 (从Linux 2.6.32开始存在) */
        void *si_lower; /*地址冲突时的下限*/
        void *si_upper; /*地址冲突时的上限 (从Linux 3.19开始存在) */
        int si_pkey; /*导致的PTE上的保护密钥*/
        void *si_call_addr; /*系统调用指令的地址*/
        int si_syscall; /*尝试的系统调用次数*/
        unsigned int si_arch; /* 尝试的系统调用的体系结构*/
        }

3.6 信号集

  • 数据类型 sigset_t 是信号集,信号掩码就是这种类型
  • 头文件:#include <signal.h>
  • 函数
    1. int sigemptyset(sigset_t *set);

      • 将信号集初始化为空,使进程不会屏蔽任何信号
    2. int sigfillset(sigset_t *set);
      • 将信号集初始化为包含所有已定义的信号
    3. int sigaddset(sigset_t *set, int signum);
      • 添加一个信号到信号集中
    4. int sigdelset(sigset_t *set, int signum);
      • 从信号集中删除一个信号
    5. int sigismember(const sigset_t *set, int signum);
      • 判断一个信号是否在信号集中
  • 注意:
    • 一个应用程序,在使用信号集前,必须对其进行初始化,即是调用 sigemptyset()sigfillset()

3.7 例子

  • 例程来自野火
  • 实验现象
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h> /** 信号处理函数 **/void signal_handler(int sig) //(1){
printf("\nthis signal numble is %d \n",sig);
if (sig == SIGINT) {
printf("I have get SIGINT!\n\n");
printf("The signal is automatically restored to the default handler!\n\n");
/** 信号自动恢复为默认处理函数 **/
} }
int main(void){
struct sigaction act;
printf("this is sigaction function test demo!\n\n");
/** 设置信号处理的回调函数 */
act.sa_handler = signal_handler;
/* 清空屏蔽信号集 */
sigemptyset(&act.sa_mask);
/** 在处理完信号后恢复默认信号处理 */
act.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &act, NULL);
while (1)
{
printf("waiting for the SIGINT signal , please enter \"ctrl + c\"...\n\n");
sleep(1);
}
exit(0);
}

参考:

  * 野火

【linux】系统编程-1-进程、管道和信号的更多相关文章

  1. linux系统编程之进程(一)

    今天起,开始学习linux系统编程中的另一个新的知识点----进程,在学习进程之前,有很多关于进程的概念需要了解,但是,概念是很枯燥的,也是让人很容易迷糊的,所以,先抛开这些抽象的概念,以实际编码来熟 ...

  2. Linux系统编程——Daemon进程

    目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制 ...

  3. linux系统编程之进程(六):父进程查询子进程的退出,wait,waitpid

    本节目标: 僵进程 SIGCHLD wait waitpid 一,僵尸进程 当一个子进程先于父进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行,或者父进程调用了wait才告终止. ...

  4. linux系统编程之进程(二):进程生命周期与PCB(进程控制块)

    本节目标: 进程状态变迁 进程控制块 进程创建 进程撤消 终止进程的五种方法 一,进程状态变迁 进程的三种基本状态 就绪(Ready)状态 当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便 ...

  5. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  6. Linux系统编程(22)——响应信号

    进程对信号的响应 进程可以通过三种方式来响应一个信号: 1.忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及SIGSTOP: 2.捕捉信号.定义信号处理函数,当信号发生时,执 ...

  7. linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程

    本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生)      使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...

  8. Linux系统编程——特殊进程之僵尸进程

    僵尸进程(Zombie Process) 进程已执行结束,但进程的占用的资源未被回收.这种进程称为僵尸进程. 在每一个进程退出的时候,内核释放该进程全部的资源.包含打开的文件.占用的内存等. 可是仍然 ...

  9. linux系统编程之进程(五)

    今天继续学习系统编程,学习的主题还是进程,今天主要讨论的是守护进程相关的概念,开始进入正题: 什么是守护进程:       守护进程的创建步骤: 在描述它之前,首先得先了解两个概念:进程组.会话期: ...

  10. linux系统编程--守护进程,会话,进程组,终端

    终端: 在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal), 进程中,控制终端是保存在PCB中的信息,而f ...

随机推荐

  1. UPX使用教程

    UPX是一个通用可执行文件压缩器,由于其具有: 压缩率高:压缩效果优于zip/gzip: 解压速度快:在奔腾133上即可达到大约10MB/秒: 压缩的可执行文件没有额外的内存开销: 安全:可以列表,检 ...

  2. Django搭建示例项目实战与避坑细节

    Django 开发项目是很快的,有多快?看完本篇文章,你就知道了. 安装 Django 前提条件:已安装 Python. Django 使用 pip 命令直接就可以安装: pip install dj ...

  3. HTTP系列(一)URI、URL、URN的区别

    ​1.URI.URL.URN关系图 1)URI Uniform Resource Identifier 统一资源标识符 每个web服务器资源都有一个名字,服务器资源名被统称为统一资源标识符:URI就像 ...

  4. HDFS客户端操作(JAVA代码)

    环境准备 windows需要配置hadoop的windows下的依赖 安装包地址: 链接:https://pan.baidu.com/s/1QtbH-x-S0QytkmaoSosdEw 提取码:2kt ...

  5. python中的Restful

    哇,昨天组里进行总结的时候,小哥哥和小姐姐真是把我给秀到了,跟他们一比,我总结的太垃圾了,嘤嘤嘤.因为我平常不怎么总结,总结的话,有word还有纸质的,现在偏向于纸质,因为可以练练字.个人观点是,掌握 ...

  6. 水星路由器自动更换IP工具

    这个工具是本人抢火车票的时候,自己写的换IP工具,仅支持自己的水星,其他水星不知道,请自测!!!! 点击更换IP,他会断开链接,重新拨号!!(达到更换IP的目的) !!开发语言:易语言(源码在下方)使 ...

  7. Scrum 冲刺 第六篇

    Scrum 冲刺 第六篇 每日会议照片 昨天已完成工作 队员 昨日完成任务 黄梓浩 完成app项目架构搭建 黄清山 完成部分个人界面模块数据库的接口 邓富荣 完成登录注册接口 钟俊豪 完成部分博客圈模 ...

  8. chrome打开Axure Rp导出的html文件提示需要安装Axure Rp插件解决办法

    1.确保chrome安装了Axure Rp扩展插件,如果按照页面的提示去下载,但是打不开的话就是被墙了,贴上科学的上网梯子https://www.lanzous.com/i7i0wuh,直接下载打开就 ...

  9. vue基础题

    一.对于MVVM的理解? MVVM 是 Model-View-ViewModel 的缩写. Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑. View 代表UI 组件,它负责 ...

  10. STL—— 容器(vector)的各种功能方法

    1. 获取容器的元素个数 size() 使用 vectorName.size() 可以输出这个容器中类型的个数,如下代码: 1 #include <iostream> 2 #include ...