wordpress仿站函数制作网站的详细步骤
- 作者: 多梦笔记
- 时间: 2026年02月18日 20:03
当前位置: 首页 > news >正文
wordpress仿站函数,制作网站的详细步骤,中国最好的影视后期培训学校,公司经营范围Linux多进程开发 主要介绍多进程开发时的要点 进程状态转换 进程反应了进程执行的变化。 进程的状态分为三种 ,运行态,阻塞态,就绪态 在五态模型中分为以下几种,新建态#xff0c;就绪态#xff0c;运行态#xff0c;阻塞态,终止态。 运行态#xff1a;进程占用处理器正在运…Linux多进程开发 主要介绍多进程开发时的要点 进程状态转换 进程反应了进程执行的变化。 进程的状态分为三种 ,运行态,阻塞态,就绪态 在五态模型中分为以下几种,新建态就绪态运行态阻塞态,终止态。 运行态进程占用处理器正在运行。
就绪态进程已具备运行的条件等待系统分配处理器运行。
阻塞态 又称为等待wait态或睡眠sleep态指进程不具备运行条件正在等待事件的完成。
新建态进程已被创建还未加入就绪队列。
进程相关命令
查看进程
ps aux / ajx
实时查看进程状态
top
杀死进程
kill
kill -l 列出所有信号
kill -9 进程id
killall 根据进程名杀死进程
kill -SICKILL 进程 id
进程创建
系统允许一个进程创建新进程新进程即为子进程子进程还可以创建新的子进程形成 进程树结构模型
pid_t fork(void)//pid_t为int类型进行了重载
pid_t getpid();// 获取当前进程的 pid 值。
pid_t getppid(); //获取当前进程的父进程 pid 值。
通过系统调用获取进程标识符 进程id: (PID) 父进程id: (PPID) #include iostream
#include string
#include unistd.h
#include cstdio
#include sys/types.h
using namespace std;
int main()
{printf(pid: %d\n, getpid());printf(ppid: %d\n, getppid());
}
通过系统调用创建进程 fork
fork 返回值 fork的返回值会返回两次一次是父进程一次是子进程 在父进程中返回子进程的id 子进程返回0 通过 fork的返回值来判断子进程和父进程 fork创建进程失败会返回 -1 并设置 error 总结 父子进程代码共享数据各自开辟空间私有一份采用写时拷贝 #include iostream
#include string
#include unistd.h
#include cstdio
#include sys/types.h
using namespace std;
int main()
{pid_t pid fork();if (pid 0){printf(父进程 id %d , 子进程 ppid %d\n, getpid(), getppid());// 当前是父进程 , 返回创建子进程的进程号}else{// 当前是子进程printf(子进程 id %d , ppid %d\n, getpid(), getppid());}
for (int i 0; i 3; i){printf(i %d \n, i);}
}
exec函数族
exec函数族的作用是根据指定的文件名找到可执行文件并用它来取代调用进程的内容。换句话说就是在进程内部调用一个可执行文件
#include unistd.h
extern char **environ;
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 *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
返回值只有调用错误或调用失败则会返回-1并设置error
execl.cpp
#include iostream
#include string
#include unistd.h
#include cstdio
#include sys/types.h
using namespace std;
int main()
{pid_t pid fork();if (pid 0) {
// 父进程cout 我是父进程 getpid()endl;}else if (pid 0) {// 子进程execl(hello, hello, NULL);cout i am chird process endl;}
for (int i 0; i 3; i) {cout i i pid getpid() endl;}
}
我们使用的比较多的是前两个函数按函数声明使用即可
进程退出、孤儿进程、僵尸进程 1.进程退出
void exit(int status) // 标准C库函数
void _exit(int status) // Linux系统标准库函数
status:进程退出码
_exit是属于 POSIX 系统调用适用于 UNIX 和 Linux 系统。调用该系统调用后会导致当前进程直接退出且函数不会返回。内核会关闭该进程打开的文件描述符若还存在子进程则交由1号进程领养再向进程的父进程发送 SIGCHLD 信号。
返回值无 2.孤儿进程
父进程已经结束了运行子进程还未停止运行这样的进程就称为孤儿进程
每当出现一个孤儿进程的时候系统会把孤儿进程的父进程设置为init
#include stdio.h
#include unistd.h
#include stdlib.h
int main()
{pid_t id fork();if(id 0){perror(fork);return 1;}else if(id 0){//childprintf(I am child, pid : %d\n, getpid());sleep(10);}else{//parentprintf(I am parent, pid: %d\n, getpid());sleep(3);exit(0);}return 0;
}
3.僵尸进程
每个进程结束后都会去释放自己用户区的资源内核区的PCB无法释放需要父进程去释放
进程终止后父进程尚未进行回收子进程残留资源PCB存放在内核中变成僵尸进程。
#include stdio.h#include stdlib.h#include unistd.h#include errno.hint main(){pid_t pid;// 循环创建子进程while (1){pid fork();if (pid 0){perror(fork error:);exit(1);}else if (pid 0){printf(I am a childprocess.\nI am exiting.\n);// 子进程退出成为僵尸进程exit(0);}else{// 父进程休眠20s继续创建子进程sleep(20);continue;}}return 0;
}
以上代码就是一个典型的僵尸进程 使用命令杀死僵尸进程
grep -v grep | cut -c 5-10 | xargs kill -9
wait 函数 wait 函数是符合 POSIX 标准的系统调用的封装器定义在 sys/wait.h 头文件中。该函数用于等待子进程的程序状态变化并检索相应的信息。wait 通常在创建新子进程的 fork 系统调用之后调用。wait 调用会暂停调用程序直到它的一个子进程终止。
用户应该将代码结构化使调用进程和子进程有两条不同的路径。通常用 if…else 语句来实现该语句评估 fork 函数调用的返回值。注意 fork 在父进程中返回子进程 ID一个正整数在子进程中返回 0。如果调用失败fork 将返回-1
#include iostream
#include string
#include unistd.h
#include cstdio
#include cstdlib
#include sys/types.h
#include sys/wait.h
using namespace std;int main()
{pid_t pid;// 创建5个子进程for (int i 0; i 5; i){pid fork();if (pid 0){break;}}if (pid 0){// 父进程while (1){printf(parent, pid %d\n, getpid());// int ret wait(NULL);int st;int ret wait(st);if (ret -1){break;}if (WIFEXITED(st)){// 是不是正常退出printf(退出的状态码%d\n, WEXITSTATUS(st));}if (WIFSIGNALED(st)){// 是不是异常终止printf(被哪个信号干掉了%d\n, WTERMSIG(st));}printf(child die, pid %d\n, ret);sleep(1);}}else if (pid 0){// 子进程while (1){printf(child, pid %d\n, getpid());sleep(1);}exit(0);}return 0; // exit(0)
}
waitpid函数
#include sys/types.h
#include sys/wait.h
pid_t waitpid(pid_t pid,int *status,int options);
功能回收指定进程号的子进程可以设置是否阻塞
参数 : -pid
参数功能pid0某个子进程的idpid 0回收当前进程组的所有进程pid -1回收所有子进程相当于 waitpid -1回收某个进程组的id
返回值
valuereturn value 0返回子进程的id0WNOHANG表示还要子进程或者-1错误没有子进程了
#include sys/types.h
#include sys/wait.h
#include unistd.h
#include stdio.h
#include stdlib.h
int main()
{pid_t pid, childpid;int status 0;pid fork();if (pid 0)printf(Error occured on forking.\n);else if (pid 0) //子进程{sleep(3); //换成30s然后kill -9 子进程pidexit(0);} else //父进程{//返回后继续执行父进程的代码段}printf(pid:%d\n,pid); //打印子进程iddo{childpid waitpid(pid, status, WNOHANG);if (childpid 0){printf(No child exited,ret %d\n, childpid);sleep(1);}} while (childpid 0);if (WIFEXITED(status))printf(正常退出:%d\n,childpid);if(WIFSIGNALED(status) WTERMSIG(status) SIGKILL)printf(被SIGKILL信号结束\n);
}进程间通信 进程是一个独立的资源分配单元不同进程之间的资源是独立的没有关联不能在一个进程中访问另一个进程的资源。 进程间通信的目的
1.数据传输
2.通知事件
3.进程控制
4.资源共享
进程间通信的方式 匿名管道 匿名管道也叫无名管道它是UNIX系统最古老的IPC(进程间通信)的方式 所有的UNIX系统都支持这种通信机制 管道的特点 父子进程通过匿名管道通信 创建匿名管道 #includeunistd.hint pipe(int filedes[2]);
返回值成功返回0否则返回-1。参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道fd[1]:写管道
注意匿名管道只能用于具有关系进程之间的通信
#include sys/types.h
#include sys/wait.h
#include unistd.h
#include iostream
#include cstring
#includecstdio
using namespace std;
int main()
{int pipefd[2];int ret pipe(pipefd);if (ret -1){perror(pipe);return -1;}pid_t pid fork();if (pid 0){char buff[1024]{0};while(1){printf(开始读数据…………\n);int len read(pipefd[0], buff, sizeof(buff)); // read 默认阻塞sleep(1);printf(prcv %s ,pid %d \n, buff, getpid());}}else if (pid 0){while(1){printf(开始写数据中………\n);const char *str hello world;write(pipefd[1], str, strlen(str));sleep(1);}}
}查看缓冲区大小命令
ulimit -a
查看缓冲区大小函数
与pathconf函数功能一样只是第一个参数不一样pathconf的第一个参数 pathname是路径名数据类型是字符数组指针而fpathconf的第一个参数 files是一个已打开文件的文件标识符数据类型是一个整数。两个函数的第二个参数name完全一样。
#include unistd.hlong fpathconf(int fd, int name);#include sys/types.h
#include sys/wait.h
#include unistd.h
#include iostream
#include cstring
#includecstdio
using namespace std;
int main()
{int pipefd[2];int ret pipe(pipefd);long size fpathconf(pipefd[0],_PC_PIPE_BUF);printf(pipe size %d\n,size);
}匿名管道通信案例
#include sys/types.h
#include sys/wait.h
#include unistd.h
#include iostream
#include cstring
#includecstdio
#includecstdlib
using namespace std;
int main()
{// 创建一个管道int fd[2];int ret pipe(fd);if(ret -1) {perror(pipe);exit(0);}// 创建子进程pid_t pid fork();if(pid 0) {// 父进程// 关闭写端close(fd[1]);// 从管道中读取char buf[1024] {0};int len -1;while((len read(fd[0], buf, sizeof(buf) - 1)) 0) {// 过滤数据输出printf(%s, buf);memset(buf, 0, 1024);}wait(NULL);} else if(pid 0) {// 子进程// 关闭读端close(fd[0]);// 文件描述符的重定向 stdout_fileno - fd[1]dup2(fd[1], STDOUT_FILENO);// 执行 ps auxexeclp(ps, ps, aux, NULL);perror(execlp);exit(0);} else {perror(fork);exit(0);}}管道的读写特点和管道设置为非阻塞 管道的读写特点 使用管道时需要注意以下几种特殊的情况假设都是阻塞I/O操作 1.所有的指向管道写端的文件描述符都关闭了管道写端引用计数为0有进程从管道的读端 读数据那么管道中剩余的数据被读取以后再次read会返回0就像读到文件末尾一样。 2.如果有指向管道写端的文件描述符没有关闭管道的写端引用计数大于0而持有管道写端的进程 也没有往管道中写数据这个时候有进程从管道中读取数据那么管道中剩余的数据被读取后 再次read会阻塞直到管道中有数据可以读了才读取数据并返回。 3.如果所有指向管道读端的文件描述符都关闭了管道的读端引用计数为0这个时候有进程 向管道中写数据那么该进程会收到一个信号SIGPIPE, 通常会导致进程异常终止。 4.如果有指向管道读端的文件描述符没有关闭管道的读端引用计数大于0而持有管道读端的进程 也没有从管道中读数据这时有进程向管道中写数据那么在管道被写满的时候再次write会阻塞 直到管道中有空位置才能再次写入数据并返回。总结 读管道 管道中有数据read返回实际读到的字节数。 管道中无数据 写端被全部关闭read返回0相当于读到文件的末尾 写端没有完全关闭read阻塞等待 写管道 管道读端全部被关闭进程异常终止进程收到SIGPIPE信号 管道读端没有全部关闭 管道已满write阻塞 管道没有满write将数据写入并返回实际写入的字节数 案例
#include unistd.h
#include sys/types.h
#include stdio.h
#include stdlib.h
#include string.h
#include fcntl.h
/*设置管道非阻塞int flags fcntl(fd[0], F_GETFL); // 获取原来的flagflags | O_NONBLOCK; // 修改flag的值fcntl(fd[0], F_SETFL, flags); // 设置新的flag
*/
int main() {// 在fork之前创建管道int pipefd[2];int ret pipe(pipefd);if(ret -1) {perror(pipe);exit(0);}// 创建子进程pid_t pid fork();if(pid 0) {// 父进程printf(i am parent process, pid : %d\n, getpid());// 关闭写端close(pipefd[1]);// 从管道的读取端读取数据char buf[1024] {0};int flags fcntl(pipefd[0], F_GETFL); // 获取原来的flagflags | O_NONBLOCK; // 修改flag的值fcntl(pipefd[0], F_SETFL, flags); // 设置新的flagwhile(1) {int len read(pipefd[0], buf, sizeof(buf));printf(len : %d\n, len);printf(parent recv : %s, pid : %d\n, buf, getpid());memset(buf, 0, 1024);sleep(1);}} else if(pid 0){// 子进程printf(i am child process, pid : %d\n, getpid());// 关闭读端close(pipefd[0]);char buf[1024] {0};while(1) {// 向管道中写入数据char * str hello,i am child;write(pipefd[1], str, strlen(str));sleep(5);}}return 0;
}
有名管道
创建管道的方式
通过命令创建有名管道
mkfifo name
通过函数创建有名管道
#include sys/types.h
#include sys/stat.hint mknod( const char * pathname , mode_t mode , dev_t dev);int mkfifo( const char * pathname , mode_t mode);
一旦使用mkfifo 创建了一个FIFO就可以使用open函数打开了常见的文件IO函数都可以使用。
使用命令创建管道
mkfifo tt 使用函数创建管道
int main()
{int ret mkfifo(test1,0664);if(ret -1){perror(mkfifo);exit(0);}return 0;
}进程管道读写测试
write.cpp
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include string.h// 向管道中写数据
/*有名管道的注意事项1.一个为只读而打开一个管道的进程会阻塞直到另外一个进程为只写打开管道2.一个为只写而打开一个管道的进程会阻塞直到另外一个进程为只读打开管道读管道管道中有数据read返回实际读到的字节数管道中无数据管道写端被全部关闭read返回0相当于读到文件末尾写端没有全部被关闭read阻塞等待写管道管道读端被全部关闭进行异常终止收到一个SIGPIPE信号管道读端没有全部关闭管道已经满了write会阻塞管道没有满write将数据写入并返回实际写入的字节数。
/
int main()
{// 1.判断文件是否存在int ret access(test, F_OK);if (ret -1){printf(管道不存在创建管道\n);// 2.创建管道文件ret mkfifo(test, 0664);if (ret -1){perror(mkfifo);exit(0);}}// 3.以只写的方式打开管道int fd open(test, O_WRONLY);if (fd -1){perror(open);exit(0);}// 写数据for (int i 0; i 100; i){char buf[1024];sprintf(buf, hello, %d\n, i);printf(write data : %s\n, buf);write(fd, buf, strlen(buf));sleep(1);}close(fd);return 0;
}
read.cpp
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include stdlib.h
#include unistd.h
#include fcntl.h// 从管道中读取数据
int main() {// 1.打开管道文件int fd open(test, O_RDONLY);if(fd -1) {perror(open);exit(0);}// 读数据while(1) {char buf[1024] {0};int len read(fd, buf, sizeof(buf));if(len 0) {printf(写端断开连接了…\n);break;}printf(recv buf : %s\n, buf);}close(fd);return 0;
} 内存映射
内存映射是将磁盘中的数据映射到内存当中用户通过修改内存就能修改磁盘文件 函数原型
#includesys/mman.h
#include sys/types.h//这里提供类型pid_t和size_t的定义
#include sys/stat.h
#include fcntl.h
void mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);
1、pathname:
在open函数中第一个参数pathname是指向想要打开的文件路径名或者文件名。我们需要注意的是这个路径名是绝对路径名。文件名则是在当前路径下的。
2、flags:
flags参数表示打开文件所采用的操作我们需要注意的是必须指定以下三个常量的一种且只允许指定一个 O_RDONLY只读模式 O_WRONLY只写模式 O_RDWR可读可写
以下的常量是选用的这些选项是用来和上面的必选项进行按位或起来作为flags参数。 O_APPEND 表示追加如果原来文件里面有内容则这次写入会写在文件的最末尾。 O_CREAT 表示如果指定文件不存在则创建这个文件 O_EXCL 表示如果要创建的文件已存在则出错同时返回 -1并且修改 errno 的值。 O_TRUNC 表示截断如果文件存在并且以只写、读写方式打开则将其长度截断为0。 O_NOCTTY 如果路径名指向终端设备不要把这个设备用作控制终端。 O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件则把文件的打开和后继 I/O设置为非阻塞模式nonblocking mode
以下三个常量同样是选用的它们用于同步输入输出 O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下不等待文件属性更新。 O_RSYNC read 等待所有写入同一区域的写操作完成后再进行 O_SYNC 等待物理 I/O 结束后再 write包括更新文件属性的 I/O
3、mode:
mode参数表示设置文件访问权限的初始值和用户掩码umask有关比如0644表示-rw-r–r–也可以用S_IRUSR、S_IWUSR等宏定义按位或起来表示详见open(2)的Man Page。要注意的是有以下几点 文件权限由open的mode参数和当前进程的umask掩码共同决定。 第三个参数是在第二个参数中有O_CREAT时才作用如果没有则第三个参数可以忽略
使用内存映射实现进程间通信
#include iostream
#include cstring
#include unistd.h
#include cstdio
#includesys/mman.h
#includefcntl.h
#include sys/types.h
#include sys/stat.husing namespace std;
/*
使用内存映射实现进程间通信*/
int main()
{int fd open(test.txt, O_RDWR);int size lseek(fd, 0, SEEK_END);// 创建内存映射区void ptr mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (ptr MAP_FAILED) {perror(mmap);exit(0);}pid_t pid fork();if (pid 0) {strcpy((char)ptr, hello world);// 父进程}else {// 子进程char buf[64]{ 0 };strcpy(buf, (char*)ptr);printf(read data %s, buf);}// 关闭内存映射区munmap(ptr, size);}
输出
read data hello world 内存映射的注意事项
1.如果对mmap的返回值做操作,munmap是否能成功 cvoid * ptr mmap(…)ptr // 可以munmap(ptr,len) // error 要保存首地址 如果open时O_RDONLY,mmap时prot参数指定PROT_READ|PROT_WRITE会怎么样 错误要返回MAP_FAILED
open函数中的权限建议和prot中的权限保持一致
3.如果文件偏移量为1000会怎么样 偏移量必须是1024的整数倍返回MAP_FAILED 4.mmap什么情况下会调用失败
第二个参数 - lengh 0第三个参数 port- 只指定了写权限- PROT_READ | PORT|WRITE第五个参数 fd 通过open打开文件时指定的参数使用 O_RDONLY / O_WRONLY
5.可以open的时候O_CREAT一个新文件来创建映射区吗? 可以但是创建文件的大小不能为0 可以对新文件进行扩展 -lessk() - truncate() 6.mmap后关闭文件描述符对mmap映射有没有影响 映射区还存在创建映射区的fd被关闭没有任何影响 7.对映射区越界操作 会怎样 越界操作访问的是非法的内存会导致段错误 (Segment fault) 使用内存映射实现文件拷贝
#include iostream
#include cstring
#include unistd.h
#include cstdio
#include sys/mman.h
#include fcntl.h
#include sys/types.h
#include sys/stat.h
#include cstdlib
using namespace std;
int main()
{int fd open(test.txt, O_RDWR);if (fd -1){perror(open);exit(0);}// 拓展文件大小int len lseek(fd, 0, SEEK_END);int fd1 open(cpy.txt, O_RDWR | O_CREAT, 0664);if (fd1 -1){perror(open);exit(0);}// 拓展新文件truncate(cpy.txt, len);write(fd1, , 1);// 分别做内存映射void *x mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);void *x1 mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);if (x MAP_FAILED){perror(mmap);exit(0);}// 内存拷贝memcpy(x, x1, len);// 释放资源munmap(x, len);munmap(x1, len);close(fd1);close(fd);
} 匿名映射 匿名映射不需要文件实体进行内存映射 代码实现
#include iostream
#include cstring
#include unistd.h
#include cstdio
#include sys/mman.h
#include fcntl.h
#include sys/types.h
#include sys/stat.h
#include cstdlib
#include wait.h
using namespace std;
int main()
{int len 4096;void *ptr mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);if (ptr MAP_FAILED){perror(mmap);exit(-1);}// 父子进程间通信pid_t pid fork();if (pid 0){strcpy((char *)ptr, hello world\n);wait(NULL);}else if (pid 0){sleep(1);printf(%s \n, (char *)ptr);}int ret munmap(ptr, len);if (ret -1){perror(munmap);exit(-1);}
} 信号概述 信号是Linux进程间通信最古老的方式之一是事件发生时对进程的通知机制有时也称为软件中断它是在软件层次上对中断机制的一种模拟是一种异步通信的方式。 使用信号的两个目的 让进程知道已经发生了一个特定的事件 强迫进程执行它自己代码中的信号处理程序
信号的特点 简单 不能携带大量信息 满足某个特定条件才发送 优先级比较高
查看系统定义的信号列表
kill -l
前三十一个为常规信号其余为实时信号。
信号的5种默认处理动作 kill raise abort 函数
函数原型
#include sys/types.h
#include signal.h
int kill(pid_t pid, int sig);函数参数 pid—进程号或者某个进程组的编号。 如果该值是正数代表信号发给某个进程 如果该值是0代表信号发给调用该函数的进程所在组的其他所有进程。 如果该值是-1信号发送给该进程允许发送的所有进程除了进程1init。 如果该值小于-1该信号发送给进程组ID为-pid内的所有进程。sig—信号的编号或者宏值。 如果信号的值为0 不会发送任何信号可以用来检查该进程ID或进程组ID是否存在。
返回值 如果至少一个信号被发送成功返回0如果错误返回-1并且全局变量errno被设置成相应的值。 #include iostream
#include cstring
#include unistd.h
#include cstdio
#include sys/mman.h
#include fcntl.h
#include sys/types.h
#include sys/stat.h
#include cstdlib
#include wait.h
using namespace std;
int main()
{pid_t pid fork();if (pid 0){for (int i 0; i 5; i)cout chird process\n, sleep(1);}else if (pid 0){cout parent process\n;sleep(2);cout kill child process now \n;kill(pid, SIGINT);}
} int raise(int sig) // 参数 要发送的信号
功能向进程发送信号(给调用者发送一个信号等价于kill(getpid(), sig)
返回值默认返回0发送失败返回 -1 void abort(void )//
函数说明发送SIGABRT信号值为6给当前进程杀死当前进程等价于kill(getpid(), SIGABRT)。 alarm 函数
1.引用头文件
#include unistd.h;
2.函数标准式
unsigned int alarm(unsigned int seconds); // 参数 : 倒计时的时长单位秒 如果倒计时为0不进行倒计时
作用 : 设置定时器函数调用开始倒计时当倒计时为0的时候会给进程发送一个SIGALARM信号 SIGALARM: 返回值 倒计时剩余的时间 案例 1
#include iostream
#include cstring
#include unistd.h
#include cstdio
#include sys/mman.h
#include fcntl.h
#include sys/types.h
#include sys/stat.h
#include cstdlib
#include wait.h
using namespace std;
int main()
{int seconds alarm(5);printf(seconds %d \n,seconds); // 0sleep(2);seconds alarm(2); // 不阻塞printf(seconds %d \n,seconds);while(1){}}
输出 案例 2 计算机一秒钟可以数多少个数
#include iostream
#include cstring
#include unistd.h
#include cstdio
#include sys/mman.h
#include fcntl.h
#include sys/types.h
#include sys/stat.h
#include cstdlib
#include wait.h
using namespace std;
int main()
{alarm(1);int i 0;while(1){printf(%i\n,i);}} ./alarm a.txt
输出
663565
总结
实际的时间 内核时间 用户时间消耗的时间
进行文件IO操作的时候比较消耗时间
定时器与进程的状态无关 自然定时器 setitimer 定时器函数
头文件
#includesys/time.h
函数原型
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)
which有以下可选参数类型
ITIMER_REAL以系统真实的时间来计算它送出SIGALRM信号。
ITIMER_VIRTUAL以该进程在用户态下花费的时间来计算它送出SIGVTALRM信号。
ITIMER_PROF以该进程在用户态下和内核态下所费的时间来计算。它送出SIGPROF信号。
功能 实现周期性的定时
返回值
成功返回0失败返回 -1
过3秒以后每隔2秒钟定时一次
#include sys/time.h
#include stdio.h
#include stdlib.h// 过3秒以后每隔2秒钟定时一次
int main()
{struct itimerval new_value;// 设置间隔的时间new_value.it_interval.tv_sec 2;new_value.it_interval.tv_usec 0;// 设置延迟的时间,3秒之后开始第一次定时new_value.it_value.tv_sec 3;new_value.it_value.tv_usec 0;int ret setitimer(ITIMER_REAL, new_value, NULL); // 非阻塞的printf(定时器开始了…\n);if (ret -1){perror(setitimer);exit(0);}getchar();return 0;
} signal 信号捕捉 函数
函数原型
void (*signal(int sig, void (*func)(int)))(int)
功能
设置某个信号的捕捉行为
返回值
该函数返回信号处理程序之前的值当发生错误时返回 SIG_ERR
实例
#include sys/time.h
#include stdio.h
#include stdlib.h
#include signal.h
#include unistd.h
using namespace std;
void sighandler(int);int main()
{// 注册信号捕捉signal(SIGINT, sighandler);while (1){printf(开始休眠一秒钟…\n);sleep(1);}return 0;
}void sighandler(int signum)
{printf(捕获信号 %d跳出…\n, signum);exit(1);
} 信号集函数 sigsett类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统 实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset t变量,而不应该对它的内部数据做 任何解释,比如用printf直接打印sigset_t变量是没有意义的 /*int sigemptyset(sigset_t *set);- 功能清空信号集中的数据,将信号集中的所有的标志位置为0- 参数set,传出参数需要操作的信号集- 返回值成功返回0 失败返回-1int sigfillset(sigset_t *set);- 功能将信号集中的所有的标志位置为1- 参数set,传出参数需要操作的信号集- 返回值成功返回0 失败返回-1int sigaddset(sigset_t *set, int signum);- 功能设置信号集中的某一个信号对应的标志位为1表示阻塞这个信号- 参数- set传出参数需要操作的信号集- signum需要设置阻塞的那个信号- 返回值成功返回0 失败返回-1int sigdelset(sigset_t *set, int signum);- 功能设置信号集中的某一个信号对应的标志位为0表示不阻塞这个信号- 参数- set传出参数需要操作的信号集- signum需要设置不阻塞的那个信号- 返回值成功返回0 失败返回-1int sigismember(const sigset_t set, int signum);- 功能判断某个信号是否阻塞- 参数- set需要操作的信号集- signum需要判断的那个信号- 返回值1 signum被阻塞0 signum不阻塞-1 失败/阻塞信号集和未决信号集
1.用户通过键盘 Ctrl C, 产生2号信号SIGINT (信号被创建)
2.信号产生但是没有被处理 未决 - 在内核中将所有的没有被处理的信号存储在一个集合中 未决信号集 - SIGINT信号状态被存储在第二个标志位上 - 这个标志位的值为0 说明信号不是未决状态 - 这个标志位的值为1 说明信号处于未决状态
3.这个未决状态的信号需要被处理处理之前需要和另一个信号集阻塞信号集进行比较 - 阻塞信号集默认不阻塞任何的信号 - 如果想要阻塞某些信号需要用户调用系统的API
4.在处理的时候和阻塞信号集中的标志位进行查询看是不是对该信号设置阻塞了 - 如果没有阻塞这个信号就被处理 - 如果阻塞了这个信号就继续处于未决状态直到阻塞解除这个信号就被处理 #### 案例
#include sys/time.h
#include stdio.h
#include stdlib.h
#include signal.h
#include unistd.h
using namespace std;int main()
{// 创建信号集sigset_t set;// 清空信号集的内容sigemptyset(set);// 判断信号 SIGINT 是否在信号集里面int ret sigismember(set, SIGINT);if (ret 0){printf(SIGINT 不阻塞\n);}else{printf(SIGINT 阻塞\n);}// 添加几个信号sigaddset(set, SIGINT);sigaddset(set, SIGQUIT);// 判断SIGINT 是否在信号集当中ret sigismember(set, SIGINT);if (ret 0){printf(SIGINT 不阻塞\n);}else{printf(SIGINT 阻塞\n);}ret sigismember(set, SIGQUIT);if (ret 0){printf(SIGQUIT 不阻塞\n);}else{printf(SIGQUIT 阻塞\n);}// 删除一个信号,判断SIGQUIT 是否在当前信号集当中sigdelset(set, SIGQUIT);ret sigismember(set, SIGQUIT);if (ret 0){printf(SIGQUIT 不阻塞\n);}else{printf(SIGQUIT 阻塞\n);}
}sigprocmask函数
#include signal.h
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
功能将自定义信号集中的数据设置到内核中阻塞非阻塞
// 编写一个程序把所有的常规信号1-31的未决状态打印到屏幕
// 设置某些信号是阻塞的通过键盘产生这些信号#include iostream
#include cstring
#include unistd.h
#include cstdio
#include sys/mman.h
#include fcntl.h
#include sys/types.h
#include sys/stat.h
#include cstdlib
#includesignal.h
using namespace std;
int main() {// 设置2、3号信号阻塞sigset_t set;sigemptyset(set);// 将2号和3号信号添加到信号集中sigaddset(set, SIGINT);sigaddset(set, SIGQUIT);// 修改内核中的阻塞信号集sigprocmask(SIG_BLOCK, set, NULL);int num 0;while(1) {num;// 获取当前的未决信号集的数据sigset_t pendingset;sigemptyset(pendingset);sigpending(pendingset);// 遍历前32位for(int i 1; i 31; i) {if(sigismember(pendingset, i) 1) {printf(1);}else if(sigismember(pendingset, i) 0) {printf(0);}else {perror(sigismember);exit(0);}}printf(\n);sleep(1);if(num 10) {// 解除阻塞sigprocmask(SIG_UNBLOCK, set, NULL);}}return 0;
}sigaction 信号捕捉函数
何为信号信号就是由用户、系统或进程发送给目标进程的信息以通知目标进程中某个状态的改变或是异常。
信号产生总体来说其产生的条件有两种分别是硬件和软件原因又称为硬中断和软中断。可细分为如下几种原因
①系统终端Terminal中输入特殊的字符来产生一个信号比如按下ctrl\会产生SIGQUIT信号。
②系统异常。比如访问非法内存和浮点数异常。
③系统状态变化。如设置了alarm定时器当该定时器到期时候会引起SIGVTALRM信号。
④调用了kill命令或是kill函数。
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数 参数1要捕获的信号 参数2接收到信号之后对信号进行处理的结构体 参数3接收到信号之后保存原来对此信号处理的各种方式与信号可用来做备份。如果不需要备份此处可以填NULL
返回值成功返回0失败返回-1
内核实现信号捕捉的流程 测试代码
#include stdio.h
#include stdlib.h
#include unistd.h
#include signal.h/自定义的信号捕捉函数/
void sig_int(int signo)
{printf(catch signal SIGINT\n);//单次打印sleep(10);printf(—-slept 10 s\n);
}int main(void)
{struct sigaction act; act.sa_handler sig_int;act.sa_flags 0;sigemptyset(act.sa_mask); //不屏蔽任何信号sigaddset(act.sa_mask, SIGQUIT);sigaction(SIGINT, act, NULL);printf(————main slept 10\n);sleep(10);while(1);//该循环只是为了保证有足够的时间来测试函数特性return 0;
} SIGCHLD 信号
SIGCHLD 信号产生的条件 子进程终止时 子进程接收到SIGSTOP信号停止时 子进程处在停止态接受到SIGCONT后唤醒时
以上三种条件都会给父进程发送SIGCHLD信号父进程默认忽略 SIGCHLD解决僵尸进程
#include cstdio
#include stdlib.h
#include unistd.h
#include signal.h
#include iostream
#include sys/types.h
#include sys/wait.h
using namespace std;
void myFun(int num)
{printf(捕捉到的信号 %d\n, num);// 回收子进程PCB的资源// while(1) {// wait(NULL);// }while (1){int ret waitpid(-1, NULL, WNOHANG);if (ret 0){printf(child die , pid %d\n, ret);}else if (ret 0){// 说明还有子进程或者break;}else if (ret -1){// 没有子进程break;}}
}int main()
{// 提前设置好阻塞信号集阻塞SIGCHLD因为有可能子进程很快结束父进程还没有注册完信号捕捉sigset_t set;sigemptyset(set);sigaddset(set, SIGCHLD);sigprocmask(SIG_BLOCK, set, NULL);// 创建一些子进程pid_t pid;for (int i 0; i 20; i){pid fork();if (pid 0){break;}}if (pid 0){// 父进程// 捕捉子进程死亡时发送的SIGCHLD信号struct sigaction act;act.sa_flags 0;act.sa_handler myFun;sigemptyset(act.sa_mask);sigaction(SIGCHLD, act, NULL);// 注册完信号捕捉以后解除阻塞sigprocmask(SIG_UNBLOCK, set, NULL);while (1){printf(parent process pid : %d\n, getpid());sleep(2);}}else if (pid 0){// 子进程printf(child process pid : %d\n, getpid());}return 0;
} 共享内存
什么是共享内存 内存映射是共享内存的一种方式。共享内存是一种允许不同进程或线程访问相同物理内存区域的技术而内存映射是实现这种共享的具体方法之一。 内存映射通常是通过将一个文件或其他资源映射到一个进程的地址空间实现的。这样当多个进程映射同一个文件或资源时它们实际上在访问相同的内存区域从而实现了内存共享。内存映射可以用于进程间通信以及实现文件的高效访问。 总的来说共享内存是一种概念而内存映射是实现共享内存的一种具体技术。 共享内存相关操作
(1)创建/打开共享内存:创建共享内存需要用到shmget()函数原型如下
#include sys/types,h
#include sys/ipc.h
#include sys/shm.h
int shmget(key_t key, int size, int flag);
创建成功返回共享内存的ID出错返回-1。
参数key为共享内存的键值参数size为创建共享内存的大小参数flag为调用函数的操作类型。参数key和参数flag共同决定的shmget()的作用 key为IPC_PRIVATE时创建一个新的共享内存flag取值无效。 key不为IPC_PRIVATE且flag设置了IPC_CREAT位而没有设置IPC_EXCL位时如果key为内核中的已存在的共享内存键值则打开否则创建一个新的共享内存。 key不为IPC_PRIVATE且flag设置了IPC_CREAT和IPC_EXCL位时则只执行创建共享内存操作。如果key为内核中的已存在的共享内存键值返回EEXIST错误。
(2)共享内存的附加(映射)
创建一个共享内存后某个进程若想使用需要将此内存区域附加(attach)到自己的进程空间(或称地址映射)需要用到shmat()函数
#include sys/types,h
#include sys/ipc.h
#include sys/shm.h
int *shmat(int shmid, const void *addr, int flag);
运行成功返回指向共享内存段的地址指针出错返回-1。
参数shmid为共享内存的ID参数addr和参数flag共同说明要引入的地址值通常只有2种用法 addr为0表明让内核来决定第1个可引用的位置 addr非0且flag中指定SHM_RND则此段引入到addr所指向的位置。
shmat()函数执行成功后会将shmid的共享内存段的shmid_ds结构的shm_nattch计数器值加1
(3)共享内存的分离 当进程使用完共享内存后需要将共享内存从其进程空间中去除(detach)使用shmdt()函数
#include sys/types,h
#include sys/ipc.h
#include sys/shm.h
int shmdt(void *addr);
运行成功返回0出错返回-1。
参数addr是调用shmat()函数的返回值即共享内存段的地址指针。shmdt()函数执行成功后shm_nattch计数器值减1。
(4)共享内存的控制 使用shmctl()可以对共享内存段进行多种控制操作函数原型
#include sys/types,h
#include sys/ipc.h
#include sys/shm.h
int shmctl(int shmid, int cmd, struct shmid_s *buf);
运行成功返回0出错返回-1。
参数shmid为共享内存的ID参数cmd指明了所要进行的操作与参数*buf配合使用
取shmid指向的共享内存的shmid_ds结构对参数buf指向的结构赋值
案例 生成keyftok() 使用key创建/获得一个共享内存shmget() 映射共享内存得到虚拟地址shmat() 使用共享内存通过地址指针 移除映射shmdt() 销毁共享内存shmctl()
#include cstdio
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/ipc.h
#include sys/shm.h
#include iostream
#include sys/wait.h
using namespace std;
int main()
{// generate keykey_t key ftok(./, 200);printf(key%#x\n, key);// create a share memoryint shmid shmget(key, 8, IPC_CREAT | 0666 | IPC_EXCL);if (shmid -1){perror(shmget failed\n);exit(1);}printf(shmid%#x\n, shmid);// map share memory to get the virtual addressvoid *p shmat(shmid, 0, 0);if ((void *)-1 p){perror(shmat failed);exit(2);}// write data to share memoryint *pi (int *)p;pi 0xaaaaaaaa;(pi 1) 0x55555555;// remove the mapif (shmdt(p) -1){perror(shmdt failed);exit(3);}// delete the share memoryprintf(use Enter to destroy the share memory\n);getchar();if (shmctl(shmid, IPC_RMID, NULL) -1){perror(shmctl);exit(4);}return 0;
}
共享内存操作命令 守护进程 进程组
进程组和会话之间形成了一种两级层次关系进程组是一组进程相关的集合。
进行组是由一个或多个共享同一进程组标识符(PGID)的进程组成
进程组拥有一个生命周期其开始时间为首进程创建组的时间结束时间为最后一个进程退出的时间。一个进程可能会因为进程结束而退出进程组也有可能会因为别的进程组加入而退出当前进程组。 会话
会话是一组进程组的集合。会话首进程是创建新会话的进程其进程id会成为会话id。
一个会话中的所有进程共享单个控制终端。
在任意时刻会话中的其中一个进程组会成为终端的前台进程组其他进程组会成为后台进程组。
当控制终端的连接建立起来之后会话首进程会成为终端的控制进程。 守护进程
守护进程Daemon Process也就是通常说的 Daemon 进程精灵进程是 Linux 中的后台服务进程。它是一个生存期较长的进程通常独立于控制终端并且周 期性地执行某种任务或等待处理某些发生的事件。一般采用以 d 结尾的名字。 守护进程具备下列特征 生命周期很长守护进程会在系统启动的时候被创建并一直运行直至系统被关闭。 它在后台运行并且不拥有控制终端。没有控制终端确保了内核永远不会为守护进 程自动生成任何控制信号以及终端相关的信号如 SIGINT、SIGQUIT。 Linux 的大多数服务器就是用守护进程实现的。比如Internet 服务器 inetd Web 服务器 httpd 等
守护进程的创建步骤 执行一个 fork()之后父进程退出子进程继续执行。 子进程调用 setsid() 开启一个新会话。 清除进程的 umask 以确保当守护进程创建文件和目录时拥有所需的权限。 修改进程的当前工作目录通常会改为根目录/。 关闭守护进程从其父进程继承而来的所有打开着的文件描述符。 ◼ 在关闭了文件描述符0、1、2之后守护进程通常会打开/dev/null 并使用dup2() 使所有这些描述符指向这个设备。 核心业务逻辑 案例
/*写一个守护进程每隔2s获取一下系统时间将这个时间写入到磁盘文件中。
*/#include stdio.h
#include sys/stat.h
#include sys/types.h
#include unistd.h
#include fcntl.h
#include sys/time.h
#include signal.h
#include time.h
#include stdlib.h
#include string.hvoid work(int num)
{// 捕捉到信号之后获取系统时间写入磁盘文件time_t tm time(NULL);struct tm *loc localtime™;// char buf[1024];// sprintf(buf, %d-%d-%d %d:%d:%d\n,loc-tm_year,loc-tm_mon// ,loc-tm_mday, loc-tm_hour, loc-tm_min, loc-tm_sec);// printf(%s\n, buf);char *str asctime(loc);int fd open(time.txt, O_RDWR | O_CREAT | O_APPEND, 0664);write(fd, str, strlen(str));close(fd);
}int main()
{// 1.创建子进程退出父进程pid_t pid fork();if (pid 0){exit(0);}// 2.将子进程重新创建一个会话setsid();// 3.设置掩码umask(022);// 4.更改工作目录chdir(/home/nowcoder/);// 5. 关闭、重定向文件描述符int fd open(/dev/null, O_RDWR);dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);// 6.业务逻辑// 捕捉定时信号struct sigaction act;act.sa_flags 0;act.sa_handler work;sigemptyset(act.sa_mask);sigaction(SIGALRM, act, NULL);struct itimerval val;val.it_value.tv_sec 2;val.it_value.tv_usec 0;val.it_interval.tv_sec 2;val.it_interval.tv_usec 0;// 创建定时器setitimer(ITIMER_REAL, val, NULL);// 不让进程结束while (1){sleep(10);}return 0;
}
相关文章
-
wordpress仿站函数手机电脑版浏览器网页入口
wordpress仿站函数手机电脑版浏览器网页入口
- 站长
- 2026年02月18日
-
wordpress仿互站口碑好的句容网站建设
wordpress仿互站口碑好的句容网站建设
- 站长
- 2026年02月18日
-
WordPress防伪插件青岛谷歌优化公司
WordPress防伪插件青岛谷歌优化公司
- 站长
- 2026年02月18日
-
wordpress仿站开发wordpress网站载入慢
wordpress仿站开发wordpress网站载入慢
- 站长
- 2026年02月18日
-
wordpress仿站开发梧州市建设局网站
wordpress仿站开发梧州市建设局网站
- 站长
- 2026年02月18日
-
wordpress访问子网站网站建设推广选stso88效果好
wordpress访问子网站网站建设推广选stso88效果好
- 站长
- 2026年02月18日
