目录
1.守护进程(daemon)
1.1什么是守护进程
守护进程是后台运行的进程,脱离某个终端,随着系统的启动而运行,
随着系统的终止而终止。例如:windows上的各种服务。
1.2守护进程创建的流程
-
创建一个孤儿进程
(孤儿进程和终端脱离了,终端关闭孤儿进程不会结束)
-
设置孤儿进程的会话id和组id
pid_t setsid(void); 功能:设置一个进程的会话id和组id和pid相同 参数: @无 返回值:成功返回会话id,失败返回-1置位错误码
-
切换进程对应的路径为根路径
int chdir(const char *path); 功能:切换路径 参数: @path:想切换到的路径 返回值:成功返回0,失败返回-1置位错误
-
对标准输入、输出、出错重定向
以后在进程内对标准输入、输出、出错重定向到日志文件中,如何对文件描述符进行重定向dup/dup2
int dup(int oldfd); 功能:拷贝oldfd生成newfd,新旧fd都能操作文件,它们共用光标 参数: @oldfd:旧的文件描述符 返回值:成功返回新的文件描述符(最小未使用原则),失败返回-1置位错误码 int dup2(int oldfd, int newfd); 功能:dup2()系统调用执行与dup()相同的任务,但它不是使 用编号最低的未使用文件描述符,而是使用在newfd中指 定的文件描述符编号。如果文件描述符newfd以前是打开 的,那么在重用它之前会以静默方式关闭它。 参数: @oldfd:旧的文件描述符 @newfd:新的文件描述符 返回值:成功返回新的文件描述符,失败返回-1置位错误码
dup函数使用
#include <head.h> int main(int argc,const char * argv[]) { int ofd,nfd; char ch; if((ofd = open("./hello.txt",O_RDWR))==-1) PRINT_ERR("open file error"); nfd = dup(ofd); printf("ofd = %d,nfd = %d\n",ofd,nfd); //通过改变oldfd的光标,能够影响newfd的光标 lseek(ofd,5,SEEK_SET); read(nfd,&ch,1); printf("ch = %c\n",ch); close(ofd); close(nfd); return 0; }
dup2实例
#include <head.h> int main(int argc, const char* argv[]) { int fd; char buf[] = "i am ok ................\n"; if ((fd = open("./hello.txt", O_RDWR)) == -1) PRINT_ERR("open file error"); //dup2会先将0 1 2三个文件描述符关闭掉 //然后通过dup2进行文件描述符重定向 //以后在对0 1 2操作的时候都是对fd对应的文件操作 dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); //将printf标准IO打印的数据写入到文件中,终端上不会显示 printf("hello DC22071 everyone!!!!!\n"); printf("hello DC22071 everyone!!!!!\n"); printf("hello DC22071 everyone!!!!!\n"); printf("hello DC22071 everyone!!!!!\n"); fflush(stdout); fprintf(stderr,"12345678!!!!!!!!!!!!\n"); write(1,buf,strlen(buf)); close(fd); return 0; }
-
开启自己的服务即可
1.3守护进程的实例
#include <head.h>
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid > 0) {
printf("父进程退出\n");
exit(EXIT_SUCCESS);
} else {
// 1创建孤儿进程
// 2.设置组id和会话id
if (setsid() == -1)
PRINT_ERR("set sid error");
// 3.切换路径
if (chdir("/"))
PRINT_ERR("change dir error");
// 4.创建日志文件
int fd;
if ((fd = open("daemon.log", O_RDWR | O_CREAT | O_APPEND, 0666)) == -1)
PRINT_ERR("open daemon.log error");
// 5.重定向
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
// 6.开启自己的服务
while (1) {
printf("i am test daemon code....\n");
fflush(stdout);
sleep(1);
}
}
return 0;
}
2.进程中程序替换函数
#include <stdlib.h>
int system(const char *command);
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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
2.1system函数
2.1.1system函数接口介绍
#include <stdlib.h>
int system(const char *command);
功能:在c语言中执行可执行程序(shell命令),system=fork+execl实现的
参数:
@command:要执行的命令字符串
返回值:
*如果command为NULL,那么如果shell可用,则为非零值,如果没有shell可用,则为0。
*如果无法创建子进程,或者无法检索其状态,则返回值为-1。
*如果shell不能在子进程中执行,那么返回值就好像子进程通过调用_exit(2)来终止127.
*如果所有系统调用都成功,那么返回值是用于执行命令的子shell的终止状态。
(shell的终止状态是它执行的最后一个命令的终止状态。)
2.1.2system函数使用实例
#include <head.h>
int main(int argc,const char * argv[])
{
//system的执行的过程就是首先先fork出来一个进程
//然后将b.out替换到这个进程中,这个进程在执行的时候
//就是执行的b.out的代码
// if(system("ls -lh")){
if(system("./b.out")){
printf("system exec error");
return -1;
}
return 0;
}
2.2exec函数族
2.2.1execl/execv函数API介绍
int execl(const char *path, const char *arg, .../* (char *) NULL */);
功能:替换当前进程的可执行程序
参数:
@path:可执行程序的路径及名字 "/bin/ls"
@arg:可变参数 "ls","-lh",NULL
返回值:失败返回-1,置位错误码
int execv(const char *path, char *const argv[]);
功能:替换当前进程的可执行程序
参数:
@path:可执行程序的路径及名字 "/bin/ls"
@arg:指针数组 argv[] = { "ls","-lh",NULL};
返回值:失败返回-1,置位错误码
2.2.2execl/execv函数实例
execl实例
#include <head.h>
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid == 0) {
// if(execl("/bin/ls","ls","-lh",NULL)==-1)
if(execl("./b.out","b.out",NULL)==-1)
PRINT_ERR("execl error");
} else {
//父进程等待回收子进程的资源
wait(NULL);
}
return 0;
}
execv实例
#include <head.h>
char *const arg[] = {"ls","-lh",NULL};
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid == 0) {
if(execv("/bin/ls",arg)==-1)
PRINT_ERR("execv error");
} else {
//父进程等待回收子进程的资源
wait(NULL);
}
return 0;
}
2.2.3execlp/execvp函数API介绍
execlp/execvp可以执行PATH环境变量中指定的任意可执行程序,而参数不用写路径了
sudo vi /etc/environment 如果想添加自己的路径就修改environment文件即可
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
功能:替换当前进程的可执行程序(指定可执行程序路径的PATH环境变量)
参数:
@path:可执行程序的路径及名字 "ls"
@arg:可变参数 "ls","-lh",NULL
返回值:失败返回-1,置位错误码
int execvp(const char *file, char *const argv[]);
功能:替换当前进程的可执行程序(指定可执行程序路径的PATH环境变量)
参数:
@path:可执行程序的路径及名字 "ls"
@arg:指针数组 argv[] = { "ls","-lh",NULL};
返回值:失败返回-1,置位错误码
2.2.4execlp/execvp函数实例
execlp实例
#include <head.h>
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid == 0) {
if(execlp("ls","ls","-lh",NULL)==-1)
PRINT_ERR("execlp error");
} else {
//父进程等待回收子进程的资源
wait(NULL);
}
return 0;
}
execvp实例
#include <head.h>
char *const arg[] = {"ls","-lh",NULL};
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid == 0) {
if(execvp("ls",arg)==-1)
PRINT_ERR("execvp error");
} else {
//父进程等待回收子进程的资源
wait(NULL);
}
return 0;
}
2.2.5execle/execvpe函数API介绍
execle/execvpe可以向程序传递任意的环境变量
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
功能:替换当前进程的可执行程序(传递任意的环境变量)
参数:
@path:可执行程序的路径及名字 "/bin/ls"
@arg:可变参数 "ls","-lh",NULL,
@envp:自己传递的环境变量
char * const envp[] = {"AA=123","BB=456",NULL};
返回值:失败返回-1,置位错误码
int execvpe(const char *file, char *const argv[],char *const envp[]);
功能:替换当前进程的可执行程序(传递任意的环境变量)
参数:
@path:可执行程序的路径及名字 "/bin/ls"
@arg:指针数组 argv[] = { "ls","-lh",NULL};
@envp:环境变量数组 char * const envp[] = {"AA=123","BB=456",NULL};
返回值:失败返回-1,置位错误码
2.2.6execle/execvpe函数实例
#!/bin/bash
echo $0
echo $1
echo $2
echo $3
echo $@
echo $#
echo $AA
echo $BB
execle实例
#include <head.h>
char * const envp[] = {
"AA=123",
"BB=456",
NULL
};
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid == 0) {
if(execle("./myshell.sh","myshell.sh","11","22","33",NULL,envp)==-1)
PRINT_ERR("execle error");
} else {
//父进程等待回收子进程的资源
wait(NULL);
}
return 0;
}
execvpe的实例
#include <head.h>
int execvpe(const char *file, char *const argv[], char *const envp[]);
char * const envp[] = {
"AA=123",
"BB=456",
NULL
};
char * const arg[] = {"myshell.sh","11","22","33",NULL};
int main(int argc, const char* argv[])
{
pid_t pid;
pid = fork();
if (pid == -1) {
PRINT_ERR("fork error");
} else if (pid == 0) {
if(execvpe("./myshell.sh",arg,envp)==-1)
PRINT_ERR("execvpe error");
} else {
//父进程等待回收子进程的资源
wait(NULL);
}
return 0;
}
3.多线程
3.1线程的概念
线程(LWP):线程又叫做轻量级的进程,进程是分配资源的最小单位,线程是调度的最小单位
线程本省并不占用资源(8K),同一个进程内所有的线程共享进程的资源。多线程没有多进程安全。
多线程的是创建时间要比多进程创建的时间更少。多线程的切换,要比多进程的切换时间更短。
多线程的效率要比多进程的效率更高。线程的函数是第三方库提供的,所以以后在编译多线程的
程序的时候要加上-lpthread的库
//多线程的man手册安装
sudo apt-get install manpages-posix manpages-posix-dev
3.2线程创建API接口的介绍
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能:创建线程
参数:
@thread:线程号
@attr:线程的属性,一般填写为NULL
@start_routine:线程体(线程处理函数)
@arg:向线程体传递参数
返回值:成功返回0,失败返回错误码
编译的时候一定要加上-lpthread
注:查看多线程的命令
ps -eLf
3.3多线程创建实例(不传递参数)
#include <head.h>
void *Task(void *arg)
{
while(1){
printf("i am thread....\n");
sleep(1);
}
}
int main(int argc,const char * argv[])
{
pthread_t tid; //线程号
//创建线程
if(errno = pthread_create(&tid,NULL,Task,NULL))
PRINT_ERR("pthread create error");
//如果向看到线程的执行的过程,一定不能让进程退出
while(1);
return 0;
}
3.4多线程创建实例(传递参数)
#include <head.h>
typedef struct{
char name[20];
int age;
}stu_t;
void *Task(void *arg)
{
stu_t *t = (stu_t *)arg;
printf("name = %s,age = %d\n",t->name,t->age);
}
int main(int argc,const char * argv[])
{
stu_t st = {
"zhangsan",23
};
pthread_t tid; //线程号
//创建线程
if(errno = pthread_create(&tid,NULL,Task,(void *)&st))
PRINT_ERR("pthread create error");
//如果向看到线程的执行的过程,一定不能让进程退出
while(1);
return 0;
}
3.5多线程执行的顺序
多线程执行没有先后顺序,时间片轮询,上下文切换
3.6多线程内存空间问题
多线程共用同一个进程的内存空间,所以全局变量每个线程都能获取并使用
#include <head.h>
int a=10;
void* Task1(void* arg)
{
//线程1在修改a的值
while(1){
a++;
sleep(1);
}
}
void* Task2(void* arg)
{
//线程2能拿到线程1修改后的值
while(1){
printf("a = %d\r",a);
fflush(stdout);
}
}
int main(int argc, const char* argv[])
{
pthread_t tid1, tid2; //线程号
//创建线程1
if (errno = pthread_create(&tid1, NULL, Task1, NULL))
PRINT_ERR("pthread create error");
//创建线程2
if (errno = pthread_create(&tid2, NULL, Task2, NULL))
PRINT_ERR("pthread create error");
//如果向看到线程的执行的过程,一定不能让进程退出
while (1);
return 0;
}
3.7多线程pthread_self函数
pthread_t pthread_self(void);
功能:获取当前线程的线程号
参数:
@无
返回值:成功返回线程号
#include <head.h>
void* Task1(void* arg)
{
printf("child tid = %ld\n", pthread_self());
}
int main(int argc, const char* argv[])
{
pthread_t tid; //线程号
//创建线程
if (errno = pthread_create(&tid, NULL, Task1, NULL))
PRINT_ERR("pthread create error");
printf("parent tid=%ld,child tid = %ld\n", pthread_self(),tid);
//如果向看到线程的执行的过程,一定不能让进程退出
while (1)
;
return 0;
}
3.8多线程pthread_exit函数
在线程中不能使用exit(_exit)来退出线程,因为exit(_exit)是用来结束进程的。
void pthread_exit(void *retval);
功能:退出一个线程
参数:
@retval:返回结束的状态值
返回值:无
3.9多线程pthread_join函数
int pthread_join(pthread_t thread, void **retval);
功能:阻塞等待回收thread线程的资源
参数:
@thread:线程号
@retval:就是pthread_exit给出的错误状态
返回值:成功返回0,失败返回错误码
实例如下:
#include <head.h>
void* Task1(void* arg)
{
//这里的status如果不加static,在main函数获取的status值和预想的不一样
//应为栈空间定义的变量,当函数调用结束的时候,内存就被释放了,就不能
//通过指针获取它内部的成员。而加static之后变量就被放到.data,所以就
//不会随函数的结束而释放空间了。所以在main函数就能够获取到这个结果了
static int status=40;
printf("child tid = %ld\n", pthread_self());
while(1){
sleep(1);
// pthread_exit(&status);
pthread_exit(NULL);
}
}
int main(int argc, const char* argv[])
{
pthread_t tid; //线程号
//创建线程
if (errno = pthread_create(&tid, NULL, Task1, NULL))
PRINT_ERR("pthread create error");
printf("parent tid=%ld,child tid = %ld\n", pthread_self(),tid);
int *status;
//pthread_join会阻塞等待子线程的退出
// pthread_join(tid,(void **)&status);
// printf("*status = %d\n",*status);
pthread_join(tid,NULL);
return 0;
}
3.10多线程pthread_cancel函数
int pthread_cancel(pthread_t thread);
功能:一个线程给另外一个线程发送取消的请求
参数:
@thread:接收取消请求的tid
返回值:成功返回0,失败返回错误码
#include <head.h>
pthread_t tid1, tid2; //线程号
void* Task1(void* arg)
{
sleep(5);
//给线程2 发送一个取消的信号,线程2将会结束掉
pthread_cancel(tid2);
}
void* Task2(void* arg)
{
// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
while (1) {
printf("i am thread 2....\n");
sleep(1);
}
}
int main(int argc, const char* argv[])
{
//创建线程1
if (errno = pthread_create(&tid1, NULL, Task1, NULL))
PRINT_ERR("pthread create error");
//创建线程2
if (errno = pthread_create(&tid2, NULL, Task2, NULL))
PRINT_ERR("pthread create error");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
3.11多线程pthread_detach函数
线程是有两种状态的:结合态、分离态。
对于默认创建的线程就是结合态,结合态的线程的资源回收的时候需要通过pthread_join完成。
但是如果将一个线程标记为分离态,这个线程结束后资源会被自动回收。
int pthread_detach(pthread_t thread);
功能:标记线程为分离态
参数:
@thread:线程号
返回值:成功返回0,失败返回错误码
#include <head.h>
pthread_t tid1, tid2; //线程号
void* Task1(void* arg)
{
sleep(5);
}
void* Task2(void* arg)
{
while (1) {
printf("i am thread 2....\n");
sleep(1);
}
}
int main(int argc, const char* argv[])
{
//创建线程1
if (errno = pthread_create(&tid1, NULL, Task1, NULL))
PRINT_ERR("pthread create error");
//创建线程2
if (errno = pthread_create(&tid2, NULL, Task2, NULL))
PRINT_ERR("pthread create error");
//将线程1和线程2标记为分离态,这个函数不会阻塞
pthread_detach(tid1);
pthread_detach(tid2);
while(1);
return 0;
}