进程环境
进程的终止
-
正常终止
从main函数返回
调用exit
调用_exit或者_Exit
最后一个线程从其启动例程返回
最后一个线程调用pthread_exit -
异常终止
调用abort
接到一个信号并终止
最后一个线程对其取消请求作出响应
钩子函数 atexit
- int atexit(void (*function)(void));
注册函数、函数将在程序正常终止后被调用
All functions registered with atexit(3) and on_exit(3) are called,in the reverse order of their registration.
命令行参数的分析
- int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt; //optind(当前找到的下标)
如果option被成功找到,返回option,否则返回-1
- 实现-y year -m math -d day -H hour -M minute -S second
./mydate -M -S
./mydate -MS
int c;
char fmtstr[SIZE];
fmtstr[0] = '\0';
c = getopt(argc, argv, "HMSymd");
if (c < 0) {
return;
}
swtich(c) {
case 'H':
strncat(fmtstr, "%H", SIZE);
break;
case 'M':
strncat(fmtstr, "%M", SIZE);
break;
....
}
strftime(timestr, TIMESIZE, fmtstr, tm);
- 实现带参选项
./mydate -y 4 -H 12
c = getopt(argc, argv, "H:MSy:md");
if (c < 0) {
return;
}
swtich(c) {
case 'H':
if (strcmp(optarg, "12") == 0) {
strncat(fmtstr, "%I(%p)", SIZE);
} else if (strcmp(optarg, "24") == 0) {
strncat(fmtstr, "%H", SIZE);
} else {
fprintf(strerr, "invalid argument");
}
break;
....
}
- 解析命令行
while(1) {
c = getopt(argc,argv,"lt-a"); // - 识别非选项的传参
if (c < 0){
break;
}
switch (c){
case 'l':
f.filesize = flen(argv[1]);
strncat(fmtstr,"filesize:%ld ",FMTSTRSIZE-1);
break;
case 't':
f.filetype = ftype(argv[1]);
strncat(fmtstr,"filetype:%c ",FMTSTRSIZE-1);
break;
case 'a':
PathParse(argv[optind]);
break;
default:
break;
}
}
C程序的存储空间布局
32位系统 虚拟空间4G (64位 128T)
4G---------------------
kernel
3G------ 0xc000000
argv、env
栈
库
堆
未初始化数据段
已初始化数据段
text代码段
0------------------------
函数跳转
适用场景:
在树结构中查找元素,找到后直接回到第一次调用处(跨函数),不用一层一层返回
- setjmp() 长返回、长跳转,可以跨函数跳转
- longjmp()
进程
PID
进程标识符 类型 pid_t,顺次向下使用,非优先使用当前范围可用最小的
- ps axf 查看进程树
- ps axm
- ps ax -L 以linux特有方式查看
- ps -ef
pid_t getpid(void); 返回当前进程pid
pid_t getppid(void);返回当前进程父进程pid
进程的产生
-
fork()
通过复制当前进程来创建进程
返回值:
fork成功将在父进程返回子进程pid,在子进程返回0;fork失败将在父进程返回-1,无子进程创建。
fork 后父子进程除以下几点其余都一样 :
1)fork 的返回值不一样
2)pid、ppid不同
3)未决信号与文件锁不继承
4)资源利用量清0 -
init进程 是所以进程的祖先进程 pid == 1
-
调度器的调度策略来决定哪个进程先运行
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("%d start !\n",getpid());
fflush(NULL);//记得刷新 否则begin放到缓冲区 父子进程的缓冲区里各有一句begin
pid_t pid = fork();
if (pid == 0){
printf("child %d\n",getpid());
}else{
printf("parent %d\n",getpid());
}
getchar();
printf("pid %d end\n",getpid());
return 0;
}
注意点:
- fork前要刷新该刷新的流:标准输出设备默认是行缓冲,’\n’刷新。文件默认是全缓冲,fllush刷新。
- 子进程exit(0)结束
- fork写时拷贝:fork时父子进程共用同一数据模块,若父子进程对数据库都只读不写,不会拷贝数据。如果有进程要改数据块,要改的数据的进程应该拷贝数据库,并修改拷贝后的数据块
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define LEFT 2
#define RIGHT 200
int main()
{
pid_t pid = 0;
int i,j,mark;
for (i = LEFT;i <= RIGHT;i++){
pid = fork();
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 primer\n",i);
}
exit(0);
}
}
getchar();
exit(0);
}
进程的消亡以及释放资源
-
pid_t wait(int *wstatus);
等待进程状态发生变化,wait成功返回终止的进程pid,失败返回-1 -
pid_t waitpid(pid_t pid, int *wstatus, int options);
-
wstatus:进程退出状态
WIFEXITED(wstatus) 返回子进程是否正常结束,that is, by calling exit(3) or _exit(2), or by returning from main().
WEXITSTATUS(wstatus) 返回子进程结束状态码。WIFEXITED为真时才可以用。
WIFSIGNALED(wstatus) 返回子进程是否由信号终止。
WTERMSIG(wstatus) 返回signal number。WIFSIGNALED为真时才可以用。
WCOREDUMP(wstatus) 返回子进程是否产生了coredump。WIFSIGNALED为真时才可以用。 -
option:选项
WNOHANG 如果没有子进程exit,立即返回
WUNTRACED
WCONTINUED (since Linux 2.6.10) -
pid:返回值
< -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid.
-1 meaning wait for any child process.任何子进程
0 meaning wait for any child process whose process group ID is equal to that of the calling process.任何同进程组的子进程
> 0 meaning wait for the child whose process ID is equal to the value of pid. 指定pid
waitpid(-1, &status, 0)相当于wait(&status)
进程分配
分块、交叉分配、池
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#define N 3
#define LEFT 100000002
#define RIGHT 100000200
//交叉算法计算 池类算法涉及到竞争
int main()
{
printf("[%d] start !\n",getpid());
fflush(NULL);//记得刷新 否则begin放到缓冲区 父子进程的缓冲区里各有一句begin
pid_t pid = 0;
int i,j,mark;
for (int n = 0;n < N;n++){
pid = fork();
if (pid < 0){
perror("fork");
for (int k = 0;k < n;k++){
wait(NULL);
}
exit(1);
}
if (pid == 0){
for (i = LEFT+n;i <= RIGHT;i+=N){
mark = 1;
for (j = 2;j <= i/2;j++){
if (i%j == 0){
mark = 0;
break;
}
}
if (mark) {
printf("%d is a primer\n",i);
}
}
printf("[%d] exit\n",n);
exit(0);
}
}
int st,n;
for (n =0 ;n < N;n++){
wait(&st);
printf("%d end\n",st);
}
exit(0);
}
exec函数族
exec
执行一个文件,替换当前进程映像, pid不变
extern char **environ;
int execl(const char *path, const char arg, … / (char *) NULL */);
int execlp(const char *file, const char arg, … / (char *) NULL */);
int execle(const char *path, const char arg, … /, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
执行data +%s
puts("Begin");
fflush(NULL);
pid = fork();
if (pid < 0) {
exit(1);
}
if (pid == 0) {
execl("/bin/date", "date", "+%s", NULL);
perror("execl()");
exit(1);
}
wait(NULL);
puts("End");
如果execl正确执行的话,不应该返回
注意:execl前要刷新该刷新的流
-
system()
int system(const char *command);
执行一个命令,相当于fork+exec+wait -
glob, globfree
int glob(const char *pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob);
void globfree(glob_t *pglob);
glob函数搜索匹配pattern中的参数,如/*是匹配根文件下的所有文件(不包括隐藏文件,要找的隐藏文件需要从新匹配)
pglob 存放匹配出的结果存放到 pglob
flags 选择匹配模式,如是否排序 GLOB_NOSORT,或者在函数第二次调用时,是否将匹配的内容追加到pglob中 GLOB_APPEND
eerrno 查看错误信息用,一般置为NULLtypedef struct { size_t gl_pathc; /* Count of paths matched so far */ char **gl_pathv; /* List of matched pathnames. */ size_t gl_offs; /* Slots to reserve in gl_pathv. */ } glob_t;
-
strsep
char *strsep(char **stringp, const char *delim);
strsep返回值为分割后的开始字符串,并将函数的第一个参数指针指向分割后的剩余字符串。
因为函数内部会修改原字符串变量,所以传入的参数不能是不可变字符串(即文字常量区),否则编译可以通过但是运行时会报段错误
shell命令实现
shell —fork() -----> shell(ls…) execl()
wait()
char *strtok(char *str, const char *delim);
char *strsep(char **stringp, const char *delim); //delim分隔符
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <wait.h>
#include <glob.h>
#include <string.h>
#define BUFSIZE 1024
#define DELIMS " \t\n"
extern char **environ;
static int parse(char *linebuf,glob_t *globres){
char *tok;
int flag = 0;
while (1){
tok = strsep(&linebuf,DELIMS);
if (tok == NULL){
break;
return -1;
} else if(strcmp(tok,"cd") == 0){
char *path = strsep(&linebuf,DELIMS);
return cd(path);
}else if(tok[0] == '\0'){
//判断返回值是否是空串
continue;
}
glob(tok,GLOB_NOCHECK|GLOB_APPEND*flag,NULL,globres);
//第一次不能append glob_argv中是随机值 GLOB_NOCHECK | (GLOB_APPEND*flag)==0
flag = 1;
}
return 1;
}
//之后记得 将 ctrl+c 转为 stdout:\n 将ctrl+d 转为 退出+再见标语
int main()
{
pid_t pid;
char *linebuf = NULL;
size_t lienbuf_size = 0;
glob_t globres;//解析命令行
//读取配置文件
//char *path = "/home/...";//填一个绝对路径
//readrc(path);
while(1){
printf("mysh:");
//获取命令
getline(&linebuf,&lienbuf_size,stdin);
//解析命令
int ret = parse(linebuf,&globres);
if (ret == -1){
}else if (ret == 0){//内部命令
}else if (ret == 1){//外部命令
fflush(NULL);
pid = fork();
if (pid < 0){
perror("fork()");
exit(1);
}else if(pid == 0){
execvp(globres.gl_pathv[0],globres.gl_pathv);
perror("execl()");
exit(1);
}
}
waitpid(pid,NULL,0);
}
exit(0);
}
用户权限以及组权限
-
u+s 当其他用户调用该可执行文件时,会切换成当前可执行文件的user的身份来执行
-
g+s
chown root ./mysudo
chmod u+s ./mysudo -
getgid
-
getegid
-
setuid 设置effective
-
setgid 设置effective
-
setreuid 交换 r e //是原子的交换
-
setregid 交换 r e
守护进程
-
会话 session 标识是
sid
pid_t setsid(void);
创建一个会话,并设置进程组id,只能子进程调用
调用的子进程会成为新会话的进程组leader PID = SID = PGID
脱离控制终端 tty
不需要父进程收尸(PPID = 1) -
getpgrp() 返回当前进程所在的进程组id
-
getpgid() 返回指定进程PGID
-
setpgid()
-
tail -f /tmp/out 动态查看文件 kill 杀守护进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/syslog.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#define FNAME "/tmp/out"
static int deamonize(void)
{
int fd;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork()");
return -1;
}
if (pid > 1) {
exit(0);
}
fd = open("/dev/null", O_RDWR);//输出都忽略
if (fd < 0) {
perror("open()");
return -1;
}
//对0,1,2重定向
dup2(fp, 0);
dup2(fp, 1);
dup2(fp, 2);
if (fd > 2) {
close(fd);//脱离终端
}
setsid();
chdir("/");
return 0;
}
int main()
{
FILE* fp;
//开启日志服务
openlog("print i",LOG_PID,LOG_DAEMON);
if (deamonize()){
syslog(LOG_ERR,"init failed!");
}else{
syslog(LOG_INFO,"successded!");
}
fp = fopen(FNAME,"w+");
if (fp == NULL){
syslog(LOG_ERR,"write file failed!");
exit(1);
}
syslog(LOG_INFO,"%s opened",FNAME);
for(int i = 0; ;i++){
fprintf(fp,"%d\n",i);
fflush(NULL);
syslog(LOG_DEBUG,"%d 写入",i);
sleep(1);
}
closelog();
fclose(fp);
exit(0);
}
系统日志
- syslogd 服务
/var/log 配置文件 、etc/sysconfig syslog.cfg - void openlog(const char *ident, int option, int facility); 关联syslogd,facility消息来源
- void syslog(int priority, const char *format, …);提交
- void closelog(void);