1. 进程操作
1.1 环境变量
1.1.1 关于环境变量 修改方式
1.1.2 环境变量加载的顺序
1.1.3 Linux下操作环境变量的函数
include
//这个全局变量是直接导出的,能够直接访问到所有的环境变量
extern char **environ;
//这个全局变量是直接导出的,能够直接访问到所有的环境变量
extern char **environ;
void showallenviron()
{
for (char *ptr = environ; ptr != NULL; ++ptr)
puts(ptr);
}
//设置环境变量
void getenvirton()
{
// 设置环境变量 key:value
setenv(“TEST”,”12345678”,1);
// 获取环境变量内容
char test = getenv(“TEST”);
puts(test);
}
int main()
{
//showallenviron();
getenvirton();
return 0;
}
1.2 进程相关
1.2.1 基本信息
1.2.2 命令
- ps命令 ps -aux
如果想要查看某个进程的信息
grep 进行一个过滤
上面这个命令,不是动态
- Linux版本的任务管理器-top命令
- 另外一个比较好用的进程查看工具
htop 默认是不安装的
apt-get install htop
如果想要结束一个进程
按 F9 选择9号 结束进程
也可以
- 结束进程
kill -9 进程的ID 也可以
killall 进程名
- 查看进程树
1.2.3 proc文件系统
我们去1645中查看
这些文件就是进程相关的信息
比如:
cat maps 查看进程的内存布局
cat cmdline查看命令行参数
cat status 查看进程的一些状态信息
还有一个进程信息的查看方式
cat stat 对于我们来说 可读性比较差 对于软件来说,可以按照一定的格式去读取他们
1.2.4 遍历进程的思路
我们可以遍历 proc目录下的 所有数字文件夹,然后就能够得到所有的进程ID,之后,进入ID文件夹,通过读取文件,就能够获取到进程的信息。
我们可以参考PS命令的实现方式:
下载源码:
PS的源码:
int main(int argc, char *argv[]){
arg_parse(argc, argv);
#if 0
choose_dimensions();
#endif
if(!old_h_option){
const char *head;
switch(ps_format){
default: /* can't happen */
case 0: head = " PID TTY TIME CMD"; break;
case 'l': head = "F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break;
case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break;
case 'j': head = " PID PGID SID TTY TIME CMD"; break;
case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break;
case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break;
case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break;
case 'l'|0x80: head = "F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break;
}
printf("%s\n",head);
}
if(want_one_pid){
if(stat2proc(want_one_pid)) print_proc();
else exit(1);
}else{
struct dirent *ent; /* dirent handle */
DIR *dir;
int ouruid;
int found_a_proc;
found_a_proc = 0;
ouruid = getuid();
dir = opendir("/proc");
while(( ent = readdir(dir) )){
if(*ent->d_name<'0' || *ent->d_name>'9') continue;
if(!stat2proc(atoi(ent->d_name))) continue;
if(want_one_command){
if(strcmp(want_one_command,P_cmd)) continue;
}else{
if(!select_notty && P_tty_num==NO_TTY_VALUE) continue;
if(!select_all && P_euid!=ouruid) continue;
}
found_a_proc++;
print_proc();
}
}
}
另外一段
static int stat2proc(int pid) {
char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
int num;
int fd;
char* tmp;
struct stat sb; /* stat() used to get EUID */
snprintf(buf, 32, "/proc/%d/stat", pid);
if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
num = read(fd, buf, sizeof buf - 1);
fstat(fd, &sb);
P_euid = sb.st_uid;
close(fd);
if(num<80) return 0;
buf[num] = '\0';
tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
*tmp = '\0'; /* replace trailing ')' with NUL */
/* parse these two strings separately, skipping the leading "(". */
memset(P_cmd, 0, sizeof P_cmd); /* clear */
sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */
num = sscanf(tmp + 2, /* skip space after ')' too */
"%c "
"%d %d %d %d %d "
"%lu %lu %lu %lu %lu %lu %lu "
"%ld %ld %ld %ld %ld %ld "
"%lu %lu "
"%ld "
"%lu %lu %lu %lu %lu %lu "
"%u %u %u %u " /* no use for RT signals */
"%lu %lu %lu",
&P_state,
&P_ppid, &P_pgrp, &P_session, &P_tty_num, &P_tpgid,
&P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
&P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_alarm,
&P_start_time, &P_vsize,
&P_rss,
&P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
&P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
&P_wchan, &P_nswap, &P_cnswap
);
/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
P_vsize /= 1024;
P_rss *= (PAGE_SIZE/1024);
memcpy(P_tty_text, " ? ", 8);
if (P_tty_num != NO_TTY_VALUE) {
int tty_maj = (P_tty_num>>8)&0xfff;
int tty_min = (P_tty_num&0xff) | ((P_tty_num>>12)&0xfff00);
snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
}
if(num < 30) return 0;
if(P_pid != pid) return 0;
return 1;
}
遍历进程的实现
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
// 显示进程名称
void proc_show_name(int pid)
{
// 打开文件
char path[266];
sprintf(path, "/proc/%d/status", pid);
FILE *fd = fopen(path, "r");
if (fd == NULL)
{
printf("无法打开文件 :%s\n", path);
return;
}
fscanf(fd, "Name: %s\n", path);
printf(" %s ", path);
// 关闭文件.
fclose(fd);
}
// 显示进程模块名称
void proc_show_module(int pid)
{
char path[266];
sprintf(path, "/proc/%d/maps", pid);
FILE *fd = fopen(path, "r");
if (fd == NULL)
{
printf("无法打开文件 :%s\n", path);
}
// %llx-%llx %4s %x %5s %d %s
unsigned long long base = 0, end = 0;
char pageatt[5];
int offset;
char time[6];
unsigned int inode;
while (
7 ==
fscanf(fd, "%llx-%llx %4s %x %5s %d %s",
&base, &end, pageatt, &offset, time, &inode, path))
{
if (offset == 0)
{
printf("\t%llx-%llx %4s %x %5s %d %s\n",
base, end, pageatt, offset, time, inode, path);
}
}
// 关闭文件.
fclose(fd);
return;
}
int main()
{
//1.遍历/proc目录
struct DIR *pdir = opendir("/proc");
if (pdir == NULL)
{
printf("无法打开/proc目录\n");
return;
};
// 2.遍历目录下所有数字的目录
int pid;
char path[266] = {0};
char proc_name[100];
struct dirent *ent = NULL;
while (ent = readdir(pdir))
{
// 得到进程id,如果无法将字符串转换成数字,说明目录名不是进程id
if (1 != sscanf(ent->d_name, "%d", &pid))
continue;
// 进程信息(包括进程名)保存在status文件中中
proc_show_name(pid);
// 进程pid
printf("%6d \n", pid);
proc_show_module(pid);
}
// 3.关闭目录流cl
closedir(pdir);
}
1.2.5 创建进程
void CreateChild() { pid_t pid; const char *message = NULL; int n = 0; int i =0; pid = fork(); // 创建进程,子进程代码也是从这个地方开始执行 if(pid<0) { // pid大于0时表示当前处于父进程 perror(“fork failed\n”); return; } if (pid == 0){ //pid为0时时子进程 message = “this is the child\n”; n = 6; } else{ // pid是-1时错误 message = “this is the parent\n”; n=3; } for(i =0;i<n;i++) { printf(message); sleep(1); } return; } int main() { CreateChild(); return 0; }
![image.png](https://cdn.nlark.com/yuque/0/2021/png/22743586/1638952286287-d90cb5ee-bb3b-4b1c-8e37-9570790bd5e0.png#clientId=ue2b19156-ddc7-4&from=paste&id=ub752cb82&margin=%5Bobject%20Object%5D&name=image.png&originHeight=330&originWidth=392&originalType=url&ratio=1&size=35876&status=done&style=none&taskId=u7b0fcc2a-5ac1-454a-adc7-f933b8fd3bf)
<a name="D8mhq"></a>
### **1.2.6 创建进程并且能够 执行一个其他程序**
```cpp
#include <unistd.h>
#include <stdio.h>
pid_t CreateProcess(char *name)
{
pid_t pid;
pid = fork(); // 创建子进程
if(pid<0)
{
perror("fork failed\n");
return -1;
}
if (pid == 0)
{
execl(name,NULL); // 让子进程执行新程序
}else{
return pid;
}
return pid;
}
int main()
{
CreateProcess("/home/pb/app");
return 0;
}
1.2.7 关于子进程的等待
子进程是需要被父进程等待,才能够清理完全,
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
pid = fork(); // 创建子进程
if (pid < 0)
{ // -1时错误
perror("fork failed");
exit(1);
}
if (pid == 0) { // 子进程
int i;
for (i = 3; i > 0; i--) {
printf("This is the child\n");
sleep(1);
}
exit(3);
}
else
{ //父进程
int stat_val;
waitpid(pid, &stat_val, 0); // 等待指定的子进程结束,否则子进程会变成僵尸进程
if (WIFEXITED(stat_val))// 如果子进程正常结束则为非0值。
printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
else if (WIFSIGNALED(stat_val)) // 进程是因为信号而结束则此宏值为真
printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
}
return 0;
}
2. 线程
2.1 创建线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// 线程回调函数
void *thread_run(void *arg){
int n = (int)arg;
for(int i=0;i<10000;i++){
printf("child: %d out number%d \n",i,n);
}
// 释放线程资源
pthread_detach(pthread_self());
return NULL;
}
int main(){
//1.创建线程
pthread_t pthread;
pthread_create(&pthread,NULL,thread_run,100);
for(int i=0;i<10000;i++){
printf("main: %d \n",i);
}
//2. 等待线程结束
pthread_join(pthread,NULL);
return 0;
}
// 编译 gcc 01创建线程.c -g -lpthread -o app
总结:
子线程结束的时候,一定要调用pthread_detach 这个函数。
创建线程的地方,应该调用pthread_join去等待线程结束。
2.2 Linux上的线程同步问题的解决
问题:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 全局变量(共享资源)
int g_source = 0;
// 线程1
void *workthread1(void * param){
// 循环增加10000次
for (size_t i = 0; i < 10000; i++){
g_source += 1;
}
// 销毁线程
pthread_detach(pthread_self());
return NULL;
}
// 线程2
void *workthread2(void * param){
// 循环增加10000次
for (size_t i = 0; i < 10000; i++){
g_source += 1;
}
// 销毁线程
pthread_detach(pthread_self());
return NULL;
}
int main(){
pthread_t threads[2]={};
// 创建两个线程
pthread_create(&threads[0],NULL,workthread1,0);
pthread_create(&threads[1],NULL,workthread2,0);
// 等待线程销毁
for (size_t i = 0; i < 2; i++)
{
pthread_join(threads[i],NULL);
}
// 显示全局变量的内容
printf("g_source = %d \n",g_source);
return 0;
}
如何解决这个问题呢???
可以使用互斥锁
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 全局变量(共享资源)
int g_source = 0;
pthread_mutex_t counter_mutex=PTHREAD_MUTEX_INITIALIZER;
// 线程1
void *workthread1(void * param){
// 循环增加10000次
for (size_t i = 0; i < 100000; i++){
pthread_mutex_lock(&counter_mutex); //获取锁
g_source += 1;
pthread_mutex_unlock(&counter_mutex); //释放锁,其它线程才可以取得锁
}
// 销毁线程
pthread_detach(pthread_self());
return NULL;
}
// 线程2
void *workthread2(void * param){
// 循环增加10000次
for (size_t i = 0; i < 100000; i++){
pthread_mutex_lock(&counter_mutex); //获取锁
g_source += 1;
pthread_mutex_unlock(&counter_mutex); //释放锁,其它线程才可以取得锁
}
// 销毁线程
pthread_detach(pthread_self());
return NULL;
}
int main(){
pthread_t threads[2]={};
// 创建两个线程
pthread_create(&threads[0],NULL,workthread1,0);
pthread_create(&threads[1],NULL,workthread2,0);
// 等待线程销毁
for (size_t i = 0; i < 2; i++)
{
pthread_join(threads[i],NULL);
}
// 显示全局变量的内容
printf("g_source = %d \n",g_source);
return 0;
}
信号量解决问题
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
// 全局变量(共享资源)
int g_source = 0;
// 信号量
sem_t g_semaphore;
// 线程1
void *workthread1(void * param){
// 循环增加10000次
for (size_t i = 0; i < 10000; i++){
sem_wait(&g_semaphore); //阻塞,直到获取信号,信号数-1
g_source += 1; //访问全局变量,不会被其它线程打断
sem_post(&g_semaphore); //释放一个信号数+1,让其它线程可以获取信号
}
// 销毁线程
pthread_detach(pthread_self());
return NULL;
}
// 线程2
void *workthread2(void * param){
// 循环增加10000次
for (size_t i = 0; i < 10000; i++){
sem_wait(&g_semaphore); //阻塞,直到获取信号,信号数-1
g_source += 1; //访问全局变量,不会被其它线程打断
sem_post(&g_semaphore); //释放一个信号数+1,让其它线程可以获取信号
}
// 销毁线程
pthread_detach(pthread_self());
return NULL;
}
int main(){
pthread_t threads[2]={};
// 创建信号量
sem_init(
&g_semaphore, //信号量
0, //0表示同一进程间线程同步
1); //信号最大值
// 创建两个线程
pthread_create(&threads[0],NULL,workthread1,0);
pthread_create(&threads[1],NULL,workthread2,0);
// 等待线程销毁
for (size_t i = 0; i < 2; i++)
{
pthread_join(threads[i],NULL);
}
// 显示全局变量的内容
printf("g_source = %d \n",g_source);
// 销毁信号量
sem_destroy(&g_semaphore);
return 0;
}
信号量,比较适合解决生产消费的问题。