linux线程操作

一.进程

1.进程相关指令

ps -ef:查看当前系统进程

ps -aux:查看当前系统进程,并显示单签进程状态

top:每隔3秒,显示进程的实时信息。动态显示,cpu利用率,内存占用率高的放前面

2.进程的优先级

进程的优先级(NI)取值范围:-20~19        优先级默认值为0,值越小,优先级越高,-20的优先级最高。

普通用户能设置的优先级最高为0.        

修改进程的优先级:

nice -n 优先等级 ./可执行文件        在准备运行时,将可执行文件的优先级设为自选的优先等级

        eg:        nice -n 2 ./a.out        在准备运行时,将a.out的优先级设为2

        

renice -n 优先等级 进程号                在进程运行阶段,将进程的优先级修改

        eg:        renice -n 2 5549        将进程号为5549的进程的优先级改为2

3.前后台进程的切换

./a.out         将./a.out在前台执行

./a.out &      将./a.out在后台执行

ctrl c                结束进程

ctrl z                挂起前台进程(此时终端会显示挂起的任务号)

bg 任务号        将后台挂起的进程唤醒,唤醒之后进程会变为后台进程

fg 任务号         将后台的进程换至前台运行

jobs                查看后台任务

 二、进程相关接口函数

1.创建子进程--fork

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

返回值:
成功创建一个子进程,父进程返回子进程的PID号,子进程返回0.通过返回值来规划父子进程执行额代码
失败父进程返回-1,没有子进程创建

getpid()    返回当前进程的PID号

 父子进程:

一个进程通过fork函数创建一个新的进程,原本进程称为新进程的父进程,新的进程称为原进程的子进程。

子进程在fork语句的下一条指令开始执行

子进程会继承父进程中几乎所有数据(局部变量,文件描述符)

PCB(系统数据段)不一样

1.如果父进程优先于子进程结束

子进程称为孤儿进程,由前台进程变为后台进程,统一由init(编号为1)进程收养

2.如果子进程优于父进程结束,且父进程没有回收子进程资源

子进程会变成僵尸进城(僵尸态)

一般来说,如果子进程先于父进程结束,子进程应该统一由父进程回收

2.结束进程 --exit() / _exit()

#include <stdlib.h>

void exit(int status);

参数:
    status:表示进程退出的状态。

       
#include <unistd.h>
void _exit(int status);

参数:
    status:表示进程退出的状态。

注意:exit函数调用后会刷新所有缓冲区,_exit函数不会刷新缓冲区

3.进程回收 --wait()/waitpid()

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *wstatus);

参数:
    wstatus:进程结束时状态信息的首地址,从这个变量返回
        
返回值:成功返回结束子进程的PID号,失败返回-1
        
一个wait结束一个子进程,wait(NULL)不接受子进程的消息💖💖💖

wait的位置不同效果不同,wait会阻塞程序

 如果想要得到子进程结束信息,可以用以下宏函数来得到:🤷‍♂️🤷‍♂️🤷‍♂️🤷‍♂️
        
WIFEXITED(wstatus)---判断一个子进程是否是正常退出,正常退出返回1,非正常退出返回0
        
WEXITSTATUS(wstatus)---查看一个子进程的返回值
        
WIFSIGNALED(wstatus)---查看是否是信号导致的结束,此时子进程的返回值为0
        
WTERMSIG(wstatus)---  查看信号是多少 

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
    pid:进程号,-1表示接收任意子进程
    watatus:进程结束时状态信息的首地址,从这个变量返回
    options:
        0---表示以阻塞方式等待结束
        WNOHANG---以非阻塞方式等待结束。如果没有孩子存在,结束

4.exec函数族

#include <unistd.h>
extern char **environ;

l:list列表
v:argv
p:PATH

这个函数是没有返回值的,因为从调用该函数开始,用户区就被调用的二进制程序给替换掉了,已经不再受我们控制
可以看到在程序运行时,下面的代码并没有执行.🐱‍🚀🐱‍🚀🐱‍🚀

execl,参数:    路径+可执行文件名,列表
execlp,参数为:    可执行文件名,列表        (带p的要先配置环境变量)
execv,参数:    路径+可执行文件名,指针数组首地址    (指针数组要提前定义)
execvp,参数为:    可执行文件名,指针数组首地址


int execl(const char *pathname, const char *arg, .../* (char  *) NULL */);
参数:
    pathname:要执行程序的文件名,包含路径
    arg:执行程序的命令行参数
    命令行参数列表以NULL结尾    🤳🤳🤳
    
    eg:ls -l,        execl("/bin/ls","ls","-l",NULL);
    execl("test","./test",NULL);
    shell命令多在/bin
返回值:失败返回 -1

int execlp(const char *file, const char *arg, .../* (char  *) NULL */);

添加环境变量:
在家目录下的.bashrc内加入export PATH=$PATH:文件路径,再用source .bashrc刷新一下,就可以执行。👀👀
    
    
参数:    
    file:要执行的程序名,不需要加路径。
             
int execv(const char *pathname, char *const argv[]);

char *buf[]={"ls","-l",NULL};
execv("/bin/ls",buf);

int execvp(const char *file, char *const argv[]);

5.守护进程

守护进程与终端无关(终端关了也在执行),负责在后台周期性的处理某些事件,或者等待某些事件响应

(1)进程组:当用户运行一个进程时,就相当于创建了一个进程组,与该进程具有亲缘关系(父子关系)的所有进程都属于该进程组

(2)会话:当用户打开一个终端时,就创建了一个会话,一个会话由一个或者多个进程组组成。一旦会话结束/终端关闭,该会话中所有进程组全部结束。

守护进程的创建流程:

1.创建子进程,父进程退出
    fork();    exit(0);
2.让子进程脱离原本会话
    setsid();
3.修改当前工作路径,防止意外(路径丢失)---非必要
    改为/    或者/tmp
    chdir()
4.重设文件权限掩码---非必要
      umask(0);
5.删除进程中所有的文件描述符
    守护进程无输入输出,默认3个自动打开的。
    n=getdtablesize();获得当前文件描述符的最大个数
    for(i=;i<n;i++)
    {
        close(i);
    }


while(1)
{
    周期性需要执行的事件
}
    
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
    
    
#include <unistd.h>
int chdir(const char *path);        

#include <unistd.h>
int getdtablesize(void);

作业:

创建一个守护进程,在time.log日志文件中,每隔一秒记录当前时间

1.创建一个守护进程

2.守护进程中执行功能

/*===============================================
*   文件名称:daemon.c
*   创 建 者:     
*   创建日期:2022年08月08日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>

int main(int argc, char *argv[])
{ 
    pid_t pid=fork();
    if(pid<0)//出错
    {
        perror("fork");
    }

    time_t my_t;
    time(&my_t);
    struct tm *t=localtime(&my_t);

    if(pid==0)//子进程
    {
        setsid();//脱离原本会话,成为新的回话
        chdir("/");//修改文件路径
        umask(0);//重设文件掩码
        for(int i=0;i<getdtablesize();i++)
        {
            close(i);   //关闭所有的文件描述符
        }

        while(1)//周期性的实现功能
        {
            //execl("/home/hqyj/test/IO/day1/a.out","./a.out",NULL);
            FILE *fp = fopen("/home/hqyj/test/IO/day4/homework/time.log","a+");
            time(&my_t);	//更新时间
            t=localtime(&my_t);

            fprintf(fp,"%04d年%02d月%02d日 %d:%02d:%02d\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);//输出到文档中
            //fflush(fp);	
            fclose(fp);
            sleep(1);
        }
    }
    else    //父进程
    {
        exit(0);    //删除父节点,使子节点变成孤儿节点
    }
    return 0;
} 

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值