- 进程标识符PID
- 父子进程的产生
fork()、vfork() - 进程的消亡及资源释放
- exec函数族
- 用户权限及组权限
- 观摩:什么是解释器文件
- system()函数
- 进程会计
- 进程时间
- 守护进程
- 系统日志
类型pid_t,有符号16位整型数
进程号顺序向下使用
命令pd
getpid()
getppid()
man fork
NAME
fork - create a child process //复制当前进程,一模一样,执行到的位置都一样
SYNOPSIS
#include
#include
pid_t fork(void); //
返回值:
成功:在父进程中返回的是子进程的PID,在子进程中返回的是0
失败:-1,返回给父进程,并设置errno
duplicating:代码一式两份,一摸一样,除了以下几点:
父子进程的区别:
- PID不同,PPID不同
- fork()的返回值不同
- 未决信号(还没来得及响应的信号)和文件锁不继承
- 资源利用量清零
init进程:所有进程的父进程,PID为1
调度器调度策略,来决定哪个进程先运行。
#include#include #include #include int main(int argc, char const *argv[]) { pid_t pid; printf("[%d]Begin!n",getpid()); //fflush(NULL); //fork()之前一定要刷新缓冲区,如果不刷新,子进程缓冲区同样会有"[父进程号]:Begin!",从而会导致进程输出重定向至文件时,出现两次“[父进程号]:Begin!” //标准输出行缓冲机制,文件全缓冲机制 pid=fork(); if (pid<0) { perror("fork()"); exit(1); } if (pid==0) { //子进程 printf("[%d]:Child is workingn",getpid()); }else{ //父进程 printf("[%d]:Parent is working!n",getpid()); } printf("[%d]:End!n",getpid()); } ===================未刷新流============= lry@ubuntu:~/codes/procrdd$ cat /tmp/out [2506]Begin! -------------注意哦 [2507]:Child is working [2507]:End! [2506]Begin! ------------注意哦,父进程Begin [2506]:Parent is working! [2506]:End!
查看进程树
ps -afx
顶格表示父进程是init进程,此时子进程是孤儿进程
fork()之前一定要刷新缓冲区!!!!fflush()
fork-demofork-demo1
#include#include #include #include #define LEFT 30000000 #define RIGHT 30000200 int main(int argc, char const *argv[]){ int i=0,j,mark; for (i= LEFT; i <= RIGHT; i++) { mark=1; for (j = 2; j < i/2; j++) { if (i%j==0) { mark=0; break; } } if (mark) { printf("%d is a primern",i); } } exit(0); }
fork-demo2
#include#include #include #include #define LEFT 30000000 #define RIGHT 30000200 int main(int argc, char const *argv[]){ int i=0,j,mark; pid_t pid; for (i= LEFT; i <= RIGHT; i++) {//201次 pid=fork();//子进程与父进程执行的位置(状态)一样 if (pid<0) { perror("fork()"); exit(1); }else if(pid==0){ mark=1; for (j = 2; j < i/2; j++) { if (i%j==0) { mark=0; break; } } if (mark) { printf("%d is a primern",i); } exit(0); } } exit(0); }
时间统计
lry@ubuntu:~/codes/procrdd$ time ./getprime > /dev/null real 0m0.789s user 0m0.784s sys 0m0.004s lry@ubuntu:~/codes/procrdd$ time ./getprime2 > /dev/null real 0m0.070s user 0m0.000s sys 0m0.012s
谁打开谁关闭,谁申请谁释放
父进程给子进程收尸,否组子进程会成为僵尸进程
僵尸态:应该是一闪而过,若操作系统或父进程比较忙,僵尸状态可能会持续一会,
僵尸进程会浪费PID
#include#include #include #include #define LEFT 30000000 #define RIGHT 30000200 int main(int argc, char const *argv[]){ int i=0,j,mark; pid_t pid; for (i= LEFT; i <= RIGHT; i++) {//201次 pid=fork();//子进程与父进程执行的位置(状态)一样 if (pid<0) { perror("fork()"); exit(1); }else if(pid==0){ //printf("我正在找素数,我的PID:%d",getpid()); mark=1; for (j = 2; j < i/2; j++) { if (i%j==0) { mark=0; break; } } if (mark) { printf("%d is a primern",i); } // sleep(1000); //子进程执行完直接退出,子进程没有人收尸,于是便成为了僵尸进程 exit(0); } }//谁打开谁关闭,谁申请谁释放 sleep(1000); //父进程睡眠态 exit(0); }
父进程创建完子进程后sleep(1000),子进程执行完之后因无人收尸变成僵尸进程,父进程醒后直接退出,子进程变成孤儿进程,挂靠init进程
vfork fork写时拷贝
父子进程,谁写谁拷贝,写的时候才拷贝
5个正常终止,3个异常终止
wait() waitpid()
waitid()
wait3()
wait4()
man 2 wait
NAME
wait, waitpid, waitid - wait for process to change state //等待进程状态发生变化
SYNOPSIS
#include
#include
pid_t wait(int *wstatus); //把当前子进程收尸收回来的状态放到wstatus中
参数:wstatus 进程状态存储的地址
返回值:pid_t
成功:已终止的子进程的pid
失败:-1
pid_t waitpid(pid_t pid, int *wstatus, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
WIFEXITED(status) 返回true,如果正常退出:exit() / _exit() / return /
WEXITSTASTUS(status) 只有WIFEXITED返回true,才能调用这个函数。返回子进程的退出码
WIFSIGNALED(status) 如果子进程被信号终止,那么返回true
WTREMSIG(status) 返回导致子进程结束的信号的编号,只有WIFSIGNALED返回true才能调用
WCOREDUPM(status) 如果子进程产生了core文件,返回true,只有WIFSIGNALED返回true才能调用
pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
pid,
< -1,给任一子进程收尸,进程组ID为pid绝对值的进程组
= 0,给进程自己的任一子进程收尸,进程组ID,等于当前进程的进程组ID,同组ID子进程
> 0,给指定pid进程收尸
wstatus,
option
WNOHANG 立即返回,如果子进程没有退出(阻塞-->非阻塞)
wait()的封装:waitpid(-1, &wstatus, 0);
待计算 进程数 201 --> 201 201 --> N 找N个子进程进行计算 方法①:分块法 进程1:[1-67] 进程2:[68-135] 进程3:[136-201] 方法②:交叉分配法 有一个进程永远拿不到素数,N的倍数那个 方法③:池内算法
FEW:fork、exec、wait
同样需要注意刷新流fflush()
NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file //运行一个二进制可执行文件
The exec() family of functions replaces the current process image with a new process image.
替换进程映像
SYNOPSIS
#include
extern char **environ;
int execl(const char *pathname, const char *arg, ... );
int execlp(const char *file, const char *arg, ...);
int execle(const char *pathname, const char *arg, ... );
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
返回值:如果有返回值,则表示出错了
返回值是-1,设置errno
示例:
#include#include #include #include #include int main(int argc, char const *argv[]) { pid_t pid; puts("Begin!"); fflush(NULL); pid=fork(); if (pid<0) { perror("fork()"); exit(1); } if (pid==0) { execl("/bin/date","date","+%s",NULL); //木马可伪装称date perror("execl()"); exit(1); } wait(NULL); puts("End!"); exit(0); }
文件描述符继承:
外部命令:能够在磁盘上找到二进制可执行文件的命令
内部命令:
外部命令实现:
myshell 命令实现 #include#include #include #include #include #include #include #define DELIMS " tn" struct cmd_st { glob_t globres; }; static void prompt(void){ printf("mysh-0.0.1$ "); } static void parse(char *line,struct cmd_st *res){ char *tok; int i=0; while (1) { tok=strsep(&line,DELIMS); if(tok==NULL) break; if(tok[0]==' ') continue; glob(tok,GLOB_NOCHECK|GLOB_APPEND*i,NULL,&res->globres); i=1; } } int main(int argc, char const *argv[]) { char *linebuf=NULL; size_t linebuf_size=0; struct cmd_st cmd; pid_t pid; while (1) { prompt(); if(getline(&linebuf,&linebuf_size,stdin)<0) break; parse(linebuf,&cmd); if (0) { //暂不处理内部命令 }else{ pid=fork(); if (pid<0) { perror("fork()"); exit(1); } if (pid==0) { execvp(cmd.globres.gl_pathv[0],cmd.globres.gl_pathv); perror("execvp()"); exit(1); }else{ wait(NULL);//父进程 } } } exit(0); }
UNIX–三个函数few组成
用户权限和组权限执行命令的时候带着执行者的身份。
u+s,如果一个可执行文件有u+s权限,那么当其他用户执行这个可执行文件的时候,该用户会切换成这个可执行文件的user身份,然后再执行
g+s,同理u+s
u+s和g+s用来将root权限下放。
UID:
存储了三份:
real 真实的
effective 有效的,鉴定权限看的是effective
save(可以没有save)
GID
由exec来鉴定权限
lry@ubuntu:~/codes$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 68208 Jul 15 06:08 /usr/bin/passwd 's': 保存当前文件是否有u+s、 g+s的权限常用函数
getuid()
geteuid()
getgid()
getegid()
setuid()
setgid() 设置effective gid
setreuid() 交换ruid和euid
setregid() 交换rgid和egid
seteuid()
setegid()
#!+解释器
#!/bin/bash看到脚本文件标记,直接把/bin/bash装载进来
解释器:
可以修改登录的shell为/usr/bin/top
system()函数fork、exec*、wait的简单封装
man system
NAME
system - execute a shell command
SYNOPSIS
#include
int system(const char *command);
进程会计
略。
man 5 acct
不可移植
man 2 times
AME
times - get process times
SYNOPSIS
#include
clock_t times(struct tms *buf);
DEscriptION
times() stores the current process times in the struct tms that buf points to. The struct tms is as
defined in :
struct tms {
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_cstime;
};
守护进程
- 脱离控制终端
- 一般是会话leader,进程组leader
会话 session,标识sid
终端(虚拟终端)
最多只有一个前台进程组,可以没有前台进程组,前台进程组能够接收标准输入或者进行标准输出。
后台进程组则不可以,如果试图将标准输入的内容发送给后台进程,那么将会导致后台进程退出。后台进程需要重定向IO
NAME
setsid - creates a session and sets the process group ID
SYNOPSIS
#include
#include
pid_t setsid(void);
ps -axj
PPID PID PGID SID TTY
PPID为1,PID=PGID=SID,TTY=?的进程,就是守护进程
lry@ubuntu:~/codes/procrdd$ ps -axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 32558 32558 32558 ? -1 Ss 0 0:00 /sbin/iscsid
1 32559 32559 32559 ? -1 S
函数getpgrp()
man getpgrp
NAME
setpgid, getpgid, setpgrp, getpgrp - set/get process group
SYNOPSIS
#include
#include
int setpgid(pid_t pid, pid_t pgid); 把pid进程放到进程组pgid
pid_t getpgid(pid_t pid); //获取进程的进程组ID
pid_t getpgrp(void);
pid_t getpgrp(pid_t pid);
int setpgrp(void);
int setpgrp(pid_t pid, pid_t pgid);
守护进程实例
系统日志
/var/log
系统主日志文件/etc/log/messages
syslogd服务
man openlog
NAME
closelog, openlog, syslog, vsyslog - send messages to the system logger
SYNOPSIS
#include
void openlog(const char *ident, int option, int facility);
参数:
ident, 人物
option,选项
facility, 消息来源
void syslog(int priority, const char *format, ...);
参数:
priority,日志级别
void closelog(void);
配置文件:/etc/sys
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FNAME "/tmp/out"
static int daemonize(void) {
int fd;
pid_t pid;
pid = fork();
if (pid < 0) {
//perror("fork()");
return -1;
}
if (pid > 0) {
exit(0);
}
fd = open("/dev/null", O_RDWR);
if (fd < 0) {
//perror("open");
return -1;
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2) {
close(fd);
}
setsid();
chdir("/");
umask(0);
return 0;
}
#define FNAME "/tmp/out"
int main(int argc, char const *argv[]) {
FILE *fp;
int i = 0;
openlog("mydaemon",LOG_PID,LOG_DAEMON);
if (daemonize()) {
syslog(LOG_ERR,"daemonize() has failed!");
exit(1);
}else {
syslog(LOG_INFO,"daemonize() has succeeded");
};
fp = fopen(FNAME, "w");
if (fp == NULL) {
// perror("fopen()");
syslog(LOG_ERR,"fopen():%s",strerror(errno));
exit(1);
}
syslog(LOG_INFO,"%s has been opened.",FNAME);
for (i = 0;; i++) {
fprintf(fp, "%dn", i);
fflush(fp);
syslog(LOG_DEBUG,"%d printed.",i);
sleep(1);
}
fclose(fp); //执行不到
closelog(); //执行不到
exit(0);
}
单实例的守护进程
使用锁文件来实现/var/run/name.pid
开机启动脚本文件/etc/rc*
lry@ubuntu:/var/run$ cat /etc/rc.d/rc.local
#!/bin/bash
# secu-tcs-agent bootstart, install at Sun 03 Oct 2021 12:57:11 PM CST
/usr/local/sa/agent/secu-tcs-agent-mon-safe.sh > /dev/null 2>&1



